해보자고

[리버싱 핵심 원리] 6장 정리 본문

리버싱/리버싱 핵심 원리

[리버싱 핵심 원리] 6장 정리

초코맛동산 2023. 12. 27. 19:32

# 학습 목표

- 6장 :  abex' crackme #1 분석


 

6. abex' crackme #1

 

- EP 코드가 매우 짧음 

=> 어셈블리 언어로 만들어진 실행 파일. (개발툴을 사용하면 내가 작성한 소스코드 외의 Stub Code가 추가되기에 디스어셈블시 복잡해보임) 

 

 

6.1 코드 및 사용 API/함수 이해

1) MessageBoxA() :

 

- 형식

int MessageBoxA(
  [in, optional] HWND   hWnd,
  [in, optional] LPCSTR lpText,
  [in, optional] LPCSTR lpCaption,
  [in]           UINT   uType
);

 

- 4개의 인자로 메시지 박스 생성.

- Style, Title, Text, h0wner 의 순서대로 uType, lpCaption, lpText, hWnd.

- Style 의 옵션으로 디폴트 값인 MB_OK 를 사용하므로 '확인' 버튼

- Title, Text는 실행 파일 구동시 뜨는 알람창과 비교하며 이해.

- h0wner는 만들 메시지 상자의 소유자 창에 대한 핸들로, NULL 값이므로 소유자 창 없음. 

(파라미터가 역순인 것이 이해가 안 가면 맨 아래 6.3을 먼저 읽어봐주세요.)

 

- MessageBoxA만을 실행시켰을 때 교재와 같이 ESI 가 FFFFFFFF로 세팅되지는 않고, entry point 주소로 세팅되어 있어서 이 부분이 이해가 안되었지만, 일단 넘어갑니다. (혹시 아시는 분..?)

 

 

2) GetDriveTypeA():

 

- 형식

UINT GetDriveTypeA(
  [in, optional] LPCSTR lpRootPathName
);

 

- 1가지 인자로 디스크 드라이브가 이동식, 고정식, CD-ROM, RAM 디스크 또는 네트워크 드라이브인지 여부를 확인. 

- 인자인 lpRootPathName은 드라이브의 루트 디렉터리로 C:\를 사용.

- 인자가 NULL 값이면 현재 디렉토리 루트를 사용. 

 

- GetDriveTypeA 함수가 실행되면,,, EAX와 ESP가 바뀌는데 ESP는 항상 변동하다시피 하니까 넘어가고, EAX가 3으로 바뀐 것으로 보여 RootPathName의 리턴값으로 보인다. 

- MSDN을 통해 리턴 값을 해석해보면, 하드 디스크 드라이버로 현재 인식되고 있다. 

 

=> 즉 사용 함수와 메시지를 통해서 추측할 수 있는 바는, 현재 HDD의 타입을 CD-Rom 타입으로 변조하여 crack할 수 있을 것이다. 

 

- 계속해서 코드 진행상황을 살펴보면 간단하게 INC와 DEC로 ESI와 EAX의 값이 변동되고, 마지막에는 CMP로 EAX와 ESI의 값을 비교해서 JE 명령어를 통해 성공 분기로 넘어가는 것을 확인할 수 있었다. 

 

- 그러나 현재 00401026까지 진행을 해봤을 때 당연히 EAX와 ESI의 값은 달랐기에 JE 명령의 수행대신 실패 분기로 넘어가게 될 것인데, 다행이도 교재와 다른 부분은 ESI의 값이라 ESI 값을 crack 해주는 김에 바꿔버리면 될 것 같다.

 

6.2 Crack

1) ESI 혹은 EAX 값의 변조

- 레지스터 윈도우 창의 ESI를 두 번 클릭하면 간단하게 변조가 가능하며 EAX와 값을 맞춰주면 crack!

- EAX도 마찬가지로 변조하여 ESI와 값을 맞춰주면 crack!

 

2) JE 명령어를 다른 명령어로 패치 

- JE 명령어는 앞선 CMP 명령어의 값이 같으면 원하는 주소로 Jump하는 명령어이다. 그리고 패치하고자 하는 JMP 명령어는 비교 없이 원하는 주소로 무조건 점프하는 명령어이다. 굳이 EAX와 ESI 레지스터의 값 변조 없이 원하는 위치로 이동할 수 있게 된다. 

 

 

+) JE 명령어가 어떻게 CMP 명령어의 값을 참조하는지 궁금할 수 있다. 이는 Z 플래그를 통해 가능한 것인데, CMP의 값이 같으면 Z 플래그는 1로 설정되고 이 값을 JE 명령어가 참조하여 CMP 명령어의 결과를 알 수 있게된다. 

 

 

6.3 스택에 파라미터를 전달하는 방법

- 6.1의 MeassageBoxA()를 설명할 때 당연하게 형식과는 반대로 파라미터의 값이 들어가는 것을 이야기 했다. 이는 단순한데 스택 메모리 구조가 LIFO(Last In First Out)이기 때문이다. 

- 그냥 마지막으로 들어가는 파라미터가 가장 먼저 읽히기 때문에 예를 들어 1-A 2-B 3-C 4-D라는 짝이 있다면(숫자는 단지 순서), 스택에서는 아래 그림과 같이 된다.   

- 스택에 들어가는 순서는 D - C - B - A 이지만 결과적으로 읽혀지는 순서는 A - B - C - D가 된다는 것. 

- MessageBoxA() 의 스택 윈도우를 확인해보면 앞선 설명의 결과를 확인할 수 있다.

(순서대로 이쁘게 잘 스택에 들어간 것을 확인할 수 있다.)

- 또한 주소를 보면 스택이 높은 주소 -> 낮은 주소로 자라나는 것도 확인할 수 있다.