Try Attack/Reverse Engineering[basic]

[crackme1] abex' 1th crackme 풀이 및 복원

D4tai1 2018. 11. 7.

※ crack보다 배우는데 목적이 있기 때문에 여러가지 방법으로 시도해 보았다.


1. 리버싱 과정(순서)

1) 실행파일 실행

 [1] 실행파일을 실행해서 뭐하는 프로그램인지 확인해본다.


2) 개발언어 및 링커 확인

 [1] 어떤언어로 개발된 프로그램인지 확인한다.

  ▶ 언어에 따라 어떤 API를 사용했는지 확인이 가능하다.

 [2] 어떤 컴파일러를 사용하였는지 확인한다.

  ▶ 컴파일러에 따라 기계어로 변환하는 방법이 다르기 때문에 컴파일러의 스타일을 확인할 수 있다.


3) 상상

 [1] 개발된 언어를 알고 있다면 어떠한 로직으로 구성되었는지 유추해본다.

 [2] 만약 C++로 제작되었지만 모른다면 의사코드를 이용해 짐작해본다.


4) 디버거 툴을 사용한 분석

 [1] 전체분석

  ▶ 전체분석의 경우 전체적인 로직을 분석하여 개발 툴의 특성에 따라 구조를 완성할 수 있다.

 [2] 부분분석

  ▶ 부분분석의 경우 문제만 해결하는 방법을 말한다.


5) 문자열 검색

  ▶ 프로그램에서 출력되는 문자열의 위치를 따라가서 영역을 분석한다.


6) API 검색

  ▶ 단순하게 출력되는 문자열이 없는 곳은 내부에서 동작하는 System Call함수를 검색해서 분석한다.


7) 분석완료 후 변조

  ▶ 분석이 완료되었다면 여러가지의 해결방법 중 한 가지방법으로 문제를 해결한다.


2. [abex' 1st crackme] 풀이시연

1) 실행파일 실행

  ▶ HD가 CD-Rom이라고 생각하도록 만들어야 할 것 같다.


  ▶ 이 드라이브는 CD-Rom이 아니라는데..

  ▶ 이 경우 Hard를 CD-Rom으로 변경해주면 해결될 것 같다.


2) 개발언어 및 링커 확인

 [1] Pascal계열의 Delphi로 작성됨을 알 수 있다.

 [2] 32비트 실행파일임을 알 수 있다.

 [3] Terbo 링커를 사용하여 링킹한 것을 알 수 있다.

 [4] 헤더타입이 PE라고 적힌 것이 확인되어 Windows용 실행파일임을 알 수 있다.

 [5] ImageBase(기준주소)가 0x400000임을 알 수 있다 

 [6] EntryPoint(시작주소)가 기준주소로부터 0x1000 만큼 떨어진 곳임을 알 수 있다.

 [7] 기타 시그니쳐 및 옵션 세부사항은 직접 하나씩 확인해보면 된다.

 [8] 필자는 Detect It Easy 2.0 툴을 사용하였다.


3) 상상

 [1] 1)에서 실행해본 결과 두 개의 메세지박스가 보여지는 것을 확인하였다.

 [2] 첫 번째 메세지 창에서 출력된 메세지, 두 번째 메세지 창에서 출력된 메세지를 확인해서 유추한다.

 [3] 의사코드

 MessageBox(핸들번호, "make me think your HD is a CD-Rom.", "abex' 1st crackme", MB_OK);

 if (드라이브가 CD-Rom인지 확인) {

//참인경우? 실행할 내용

} else {

 MessageBox(핸들번호, "Nah... this is not a CD-Rom Drive!", "Error", MB_OK);

//거짓일경우? 실행할 내용

}

종료..


4) 디버거 툴을 사용한 분석

 [1] 디스어셈블한 코드


5) 문자열 검색

 [1] 현재 메모리에 존재하는 문자열을 출력한다.

 [2] [Nah... this is not a CD-Rom Drive!] 이 문자열이 출력되었으므로 이 문자열의 주소로 이동한다.

 [3] 아까 의사코드를 작성하면서 유추했을 때 이 전에 분기문이 있을 것이며 비교 후 위 문자열을 출력했다는 생각이 든다.

 [4] 0x00401026주소로 이동해보면 같으면 점프하라는 jmp문이 보인다.

 [5] 그 위 0x00401024에서 cmp문으로 비교하는 것을 알 수 있다.

 [6] 0x00401026주소의 내용은 eax와 esi가 같으면 0x40103D로 점프하라는 내용이다.

 [7] 0x0040103D주소 이후 내용은 HD가 CD-Rom이라는 메세지를 출력하는 메세지박스를 띄우고 있다.


6) API 검색

 [1] GetDriveTypeA() 함수의 MSDN

  ▶ 파라미터는 드라이브의 루트디렉터리이며, 백슬래시(\\)가 뒤에 들어가야 한다.

  ▶ 매개변수가 NULL일 경우 현재디렉터리의 루트를 사용한다.

  ▶ 드라이버 타입이 CD-Rom인 경우 EAX에 5를 반환한다.


7) 분석완료 후 변조

 [1] GetDriveType( ) 함수의 Return 값을 5(CD)로 변경

  ▶ 먼저 처음 메세지박스(0x40100E)를 확인할 때까지 F8로 StepOver를 진행한다.

  ▶ 이 후 파라미터에는 "C://"를 넣고 GetDriveTypeA() 함수를 호출해서 돌아온다.


  ▶ 이 경우 EAX에는 위에 MSDN에서 나온 것과 같이 DRIVE_FIXED로 하드나 플래시드라이버를 의미하는 3이 저장된다.


  ▶ 이 값을 CD-Rom을 의미하는 5의 값으로 변경한다.


  ▶ 계속 다음으로 넘기면 조건문에서 비교 후 CD-Rom이라고 출력해준다.


-------------------------------------------------------------------


 [2] GetDriveType( ) 함수의 파라미터 변경

  ▶ 0x401013에서 0x402094에 있는 값을 GetDriveTypeA( )함수의 파라미터로 사용하기 위해 스택에 저장한다.


  ▶ 0x402094에 이동해보니 "C:\" 가 저장되어 있다.


  ▶ 내컴퓨터는 D드라이브가 CD-Rom이므로 "D:\"를 의미하는 아스키코드 16진수 값으로 변경하였다.


  ▶ 메모리 내에서 변경된 것을 확인할 수 있다.


  ▶ GetDriveTypeA( ) 함수에 갔다오면 CD-Rom을 의미하는 5가 EAX에 반환된다.


  ▶ 계속 다음으로 넘기면 조건문에서 비교 후 CD-Rom이라고 출력해준다.


-------------------------------------------------------------------


 [3] if(eax==esi) 문에서 비교하는 eax와 esi의 값을 강제로 일치시키도록 변경(단순)

  ▶ 이 로직대로 실행해보면 eax = [3, 2, 1]이 되고, esi = [0, 1, 2, 3]이 된다.

  ▶ 만약 eax에 반환 값이 5라면 eax = [5, 4, 3]이 되어 eax와 esi가 같게 된다.


  ▶ 증가와 감소가 끝난 후 cmp문 실행 전에 eax와 esi를 각각 0x100으로 동일하게 변경한다.


  ▶ 계속 다음으로 넘기면 조건문에서 비교 후 CD-Rom이라고 출력해준다.


-------------------------------------------------------------------


 [4] if(eax==esi) 문을 if(eax != esi) 와 같이 반대로 변경하여 반대로 점프하도록 설정(단순)

  ▶ 0x401026의 명령어 je는 같은경우 CD-Rom이 맞다는 텍스트를 메세지박스에 출력한다.


  ▶ 그러나 이미 위에서 값이 같지 않기 때문에 je를 jne로 변경하여 같지않은 경우 맞다는 메세지박스를 출력하도록 변경한다.

  ▶ 여기서 중요한 것은 기계어코드를 살펴보면 74에서 75로 변경된 것을 알 수 있다.

  ▶ 즉 원래 기계어가 2바이트라면 명령어 변경시에도 2바이트짜리로 변경하여야 문제가 안생긴다.

  ▶ 그러지 않을 경우 OPCODE의 주소가 밀려서 문제가 발생한다.

  ▶ 문제가 발생하였다면 NOP를 넣어서 상대적으로 수정이 가능하다.


-------------------------------------------------------------------


 [5] 출력부분의 문자열이나 주소를 교환(무식)

  ▶ 위와 같이 내부 문자열을 그대로 교환하거나 문자열의 주소를 교환한다.

  ▶ 결과만 필요한 경우 무식하지만 출력되는 내용을 바꿔서 출력하도록 할 수 있다.


8) 원본소스 가복원

<<Event_start>>
USER32.MessageBoxA(NULL,"abex's 1st crackme","Make me think ...",MB_OK);


<<Event_OK>>
eax = KERNEL32.GetDriveType("C:\")

esi = 0;
esi++;
eas--;
esi++;
esi++;
eax--;


if (eax == esi)
{
   USER32.MessageBoxA(NULL,"YEAH!","Ok ...",MB_OK);
}
else
{
  USER32.MessageBoxA(NULL,"Error","Nah....",MB_OK);
}

KERNEL32.ExitProcess();


9) C언어 코드 복구

#include<stdio.h>
#include<Windows.h>

int main() {
	int eax = 0;
	int esi = 0;
	
	MessageBoxA(NULL, "Make me think your HD is a CD-Rom", "abex' 1st crackme", MB_OK);
	eax = GetDriveTypeA("C:\\");

	esi++;
	eax--;
	esi++;
	esi++;
	eax--;

	if (eax == esi) {
		MessageBoxA(NULL, "Ok, I really think that your HD is a CD-ROM! :p", "YEAH!", MB_OK);
	}
	else {
		MessageBoxA(NULL, "Nah... This is not a CD-ROM Drive!", "Error", MB_OK);
	}

	return 0;
}


10) 복구한 C언어 코드로 실행



  ▶ 소스가 어느정도 복원 된 것을 확인할 수 있다.



 11) 기타

  ▶ 그 외 여러가지 방법이 있겠지만 보통 문자열만 가지고 분석했을 때 변조 방법은 한정적이다.

  ▶ [1]과 [2] 방법은 API함수를 알고 있을 경우에만 사용가능한 방법이다.

  ▶ 같은 결과지만 변조방법을 5가지정도 예를 들었다.

  ▶ 현재 리버싱의 목적이 crack하는 것이 목적이라면 이렇게 할 이유는 없다.

  ▶ 그러나 배우는데 목적이 있다면 여러가지 방법을 시도하는 것을 추천한다.



'Try Attack > Reverse Engineering[basic]' 카테고리의 다른 글

[crackme3] abex' 3rd crackme 풀이 및 복원  (0) 2018.11.08
[crackme2] abex' 2nd crackme 풀이 및 복원  (0) 2018.11.07
악성코드와 리버싱  (0) 2018.10.09
전략  (0) 2018.09.16
올리디버거  (0) 2018.09.16

댓글