Security/Reversing

[abex' crackme] #1 분석

아이렌. 2021. 11. 2. 18:03

  문제

abexcm1.exe
0.01MB

  실행

  분석

 0040100E | MessageBoxA("Make me think your HD is a CD-ROM.")
 00401018 | GetDriveTypeA("C:\\")
 ...
 00401036 | MessageBoxA("Nah... This is not a CD-ROM Drive!")
 0040104B | MessageBoxA("OK, I really think that your HD is a CD-ROM! :p")

호출되는 Win32 API함수만 보자면 다음과 같은데 실행 버튼을 눌렀을때 00401036 | MessageBoxA("Nah... This is not a CD-ROM Drive!")이 호출되었다.

 

그러므로, 0040104B | MessageBoxA("OK, I really think that your HD is a CD-ROM! :p")이 호출되는게 제작자가 원하는 정답일 것이다.

 

 00401024 | CMP EAX, ESI
 00401026 | JE SHORT 0040103D

디버깅을 진행하면 00401024 | CMP EAX, ESI에서 EAX와 ESI를 비교하고, 00401026 | JE SHORT 0040103D에서는 JE 연산 후에 조건에 맞으면 0040103D로 분기하라 되어있다.

 

 00401028 | PUSH 0
 0040102A | PUSH OFFSET 0040205E
 0040102F | PUSH OFFSET 0040203B
 00401034 | PUSH 0
 00401036 | CALL <JMP.&USER32.MessageBoxA>>

그대로 진행하다보면 0040103D로 분기되지 않고 00401028으로 넘어가 실행 버튼을 눌렀을 때 틀렸다는 화면이 뜬다.

 

 00401024 | CMP EAX, ESI
 00401026 | JE SHORT 0040103D

따라서 0040104B | MessageBoxA("OK, I really think that your HD is a CD-ROM! :p")이 호출되도록 하려면 분기 되기 이전 위의 명령어를 조작하여 0040104D로 분기시키고 0040104B가 호출되도록 해야 한다.

  풀이

  • 1. JE → JMP를 사용한 단순 패치

 

단순히 0040104D으로 분기시키게 하려면 00401026의 명령어를 'Assemble' [Space]을 사용하여 JE를 JMP로 바꾸면 된다.

 

 

그러면 0040104D으로 분기되고 0040104B | MessageBoxA("OK, I really think that your HD is a CD-ROM! :p")을 호출할 수 있다.

 

 

  • 2. CMP EAX, ESI 수정

CMP 명령어는 두 피연산자를 비교하여 서로 같으면 결과는 0이 되고 ZF가 1로 셋되는 명령어이고,

JE(Jump if equal)는 ZF = 1일 때 점프하는 명령어이다.

 

 

위 그림은 00401024 | CMP EAX, ESI가 실행되기 이전인데 EAX = 1, ESI = 00401003을 나타내고 있다.

 

EAX나 ESI 둘 중에 하나를 수정해 서로 같은 값을 가지게 하면 ZF가 1로 셋 된다.

 

이후 디버깅을 진행시키면 JE(Jump if equal)는 ZF가 1이기 때문에 0040104D으로 점프하고 이후 0040104B가 호출될 것이다.

이 방법도 JE → JMP로 바꾼 것처럼 단순 패치 방법이다.

 

 

3. GetDriveTypeA 수정

 

EAX는 함수 리턴값을 저장하는 레지스터로 00401018 | GetDriveTypeA가 호출되면 리턴값으로 3이 저장되는데

EAX에 3이 어떻게 저장되는지 알아보자.

 

 

00401018 | GetDriveTypeA가 호출되기 이전에 00401013 | PUSH OFFSET 00402094 명령어가 수행되는데

Dump Window의 402094를 가면 63 3A 5C로 C:\가 저장되어 있고 PUSH 명령으로 스택에 402094가 저장된다.

이후 00401018 | GetDriveTypeA 명령어가 실행된다.

 

00401018 | GetDriveTypeA를 Step into 하면 MOV ESI,DWORD PTR SS:[EBP+8] 명령어가 나오고 ESI에 EBP+8의 값인 c:\를 저장한다.

 

위 그림은 MSDN에서 나와있는 GetDriveTypeA 리턴값 표인데 GetDriveTypeA 함수가 실행되면 c:\는 하드디스크이므로 DRIVE_FIXED 값인 3이 EAX에 반환된다.

 

그리고 00401018 | GetDriveTypeA가 호출된 이후에 위 명령어들이 실행된다.

현재 EAX는 3, ESI는 0이므로 위 명령어들이 실행되면 결과값은 아래와 같다.

 00401010 | INC ESI                                 // ESI 1 증가 ESI = 1
 0040101E | DEC EAX                              // EAX 1 감소 EAX = 2
 0040101F | JMP SHORT 00401021       
 00401021 | INC ESI                                 // ESI 1 증가 ESI = 2
 00401022 | INC ESI                                 // ESI 1 증가 ESI = 3
 00401023 | DEC EAX                              // EAX 1 감소 EAX = 1

위 명령어들이 실행돼 EAX는 2번 감소해 3에서 1이 되었고 , ESI는 3번 증가해 0에서 3이 되었다.

 

이후 00401024 | CMP EAX, ESI 명령어가 수행되는데 EAX와 ESI값이 서로 다르기 때문에 ZF는 0이 되고

00401026 | JE SHORT 0040103D 명령어에서는 점프 되지 않고 00401028으로 넘어가 실행 버튼을 눌렀을 때 틀렸다는 화면이 뜬다.

 

그러므로 EAX와 ESI가 서로 같게 하기 위해서는 EAX에 00401018 | GetDriveTypeA의 리턴값으로 5가 저장되게 해야하는데

GetDriveTypeA함수에서 메모리 주소 00402094의 값인 c:\을 불러오므로 이 값을 다른 드라이브로 바꿔줘야 한다.

 

가상이미지를 D:\에 마운트 시키고 메모리 주소 00402094의 값 63을 64로 변경한다.

 

GetDriveTypeA 함수에서 D:\가 cd-rom으로 인식하여 EAX에 5가 반환되었다.

 

이후에 INC, DEC 연산을 거치고 난 뒤 EAX와 ESI가 서로 같기 때문에 CMP 연산 후 ZF = 1이라 JE 명령어에서는 0040103D로 점프되고 맞았다는 정답이 출력된다.