끝나지 않는 프로그래밍 일기


Abex Crack-Me 1번문제는 Immunity Debugger를 사용하여 풀었습니다.


abexcrackme1.exe


우선 위의 apexcrackme를 다운받고 실행해봅시다.


'나를 CD-ROM으로 생각하게 만들어라'


그리고 확인을 누르게되면,


'아냐... 이것은 CD-ROM 드라이브가 아니다!'


아마도 CD-ROM인지 아닌지에 따라 각각 성공했을 때와 실패했을때의 메세지 박스가 뜰것이며, 이것을 우회하여 성공 메세지 박스를 띄우게 만들어야 하는 문제 같습니다. 그럼 이제 디버거를 실행하여 파일을 열어봅시다.


디버거를 실행하면, 주소 창, 옵코드 창, 디스어셈블리 창, 레지스터 창, 스택 창, 메모리 덤프창 등이 보이죠.

우선 F8(Step over)키를 이용해서 한줄 한줄 실행해보도록 하겠습니다. 

(F7(Step info)키와 차이점은 함수 내부로 진입하느냐 마느냐에 있습니다.)


실행하자마자, 바로 우리가 방금 보았던 내용들이 보입니다.


00401007  |. 68 12204000    PUSH abexcrac.00402012       ; |Text = "Make me think your HD is a CD-Rom."

0040102F  |. 68 3B204000    PUSH abexcrac.0040203B      ; |Text = "Nah... This is not a CD-ROM Drive!"


쭉쭉 실행시키시다 보면, MessageBoxA 부분을 넘어간 뒤에 메세지 박스가 뜸을 확인할 수 있습니다. 그러곤 다음 부분으로 넘어가게 되는데,

00401013  |. 68 94204000    PUSH abexcrac.00402094                   ; /RootPathName = "c:\"
00401018  |. E8 38000000    CALL <JMP.&KERNEL32.GetDriveTypeA>       ; \GetDriveTypeA

401018에서 GetDriveTypeA API를 이용하여 드라이브의 타입을 반환합니다. 찾아보니 이렇습니다.

DRIVE_UNKNOWN 0 The drive type cannot be determined.
DRIVE_NO_ROOT_DIR 1 The root path is invalid; for example, there is no volume mounted at the specified path.
DRIVE_REMOVABLE 2 The drive has removable media; for example, a floppy drive, thumb drive, or flash card reader.
DRIVE_FIXED 3 The drive has fixed media; for example, a hard disk drive or flash drive.
DRIVE_REMOTE 4 The drive is a remote (network) drive.
DRIVE_CDROM 5 The drive is a CD-ROM drive.
DRIVE_RAMDISK 6 The drive is a RAM disk.

그럼 리턴값이 담기는 EAX 레지스터의 값을 살펴보도록 합시다.

EAX 00000003
ECX 77486500 ntdll.77486500
EDX 002AA1B0
EBX 7FFDF000
ESP 0012FF8C
EBP 0012FF94
ESI 00000000
EDI 00000000

EAX 레지스터에 담겨있는 값이 3이네요.


0040101D  |. 46             INC ESI

0040101E  |. 48             DEC EAX

0040101F  |. EB 00          JMP SHORT abexcrac.00401021

00401021  |> 46             INC ESI

00401022  |. 46             INC ESI

00401023  |. 48             DEC EAX

00401024  |. 3BC6           CMP EAX,ESI


여기서 INC(인크리먼트, increment)는 1을 증가시키는 역할을 합니다. 그리고 DEC(디크리먼트, decrement)는 1을 감소시키는 역할을 하구요. 즉 ESI를 증가시키고 EAX를 감소 시킨뒤에, ESI를 두번 증가시키고 EAX를 한번 감소시킵니다. 그리고 CMP는 비교 연산으로 두 개의 오퍼랜드를 가지며 비교 결과는 프로세스의 플래그에 기록합니다.


CMP 명령은 - 연산으로 비교를 하며, 같을 경우엔 CF(캐리 플래그, Carry Flag)는 0이 되며 제로 플래그(ZF, Zero Flag) 값이 1으로 설정됩니다. 다를 경우에는 제로 플래그가 0으로 설정되죠. 


0040101D  |. 46             INC ESI     ; ESI = 1, EAX = 3

0040101E  |. 48             DEC EAX    ; ESI = 1, EAX = 2

0040101F  |. EB 00          JMP SHORT abexcrac.00401021

00401021  |> 46             INC ESI    ; ESI = 2, EAX = 2

00401022  |. 46             INC ESI    ; ESI = 3, EAX = 2

00401023  |. 48             DEC EAX    ; ESI = 3, EAX = 1

00401024  |. 3BC6           CMP EAX,ESI    ; ESI > EAX, ZF = 0


그 아래부분도 봅시다.


00401026  |. 74 15           JE SHORT abexcrac.0040103D

00401028  |. 6A 00           PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL

0040102A  |. 68 35204000     PUSH abexcrac.00402035                   ; |Title = "Error"

0040102F  |. 68 3B204000     PUSH abexcrac.0040203B                   ; |Text = "Nah... This is not a CD-ROM Drive!"

00401034  |. 6A 00           PUSH 0                                   ; |hOwner = NULL

00401036  |. E8 26000000     CALL <JMP.&USER32.MessageBoxA>           ; \MessageBoxA

0040103B  |. EB 13           JMP SHORT abexcrac.00401050

0040103D  |> 6A 00           PUSH 0                                   ; |/Style = MB_OK|MB_APPLMODAL

0040103F  |. 68 5E204000     PUSH abexcrac.0040205E                   ; ||Title = "YEAH!"

00401044  |. 68 64204000     PUSH abexcrac.00402064                   ; ||Text = "Ok, I really think that your HD is a CD-ROM! :p"

00401049  |. 6A 00           PUSH 0                                   ; ||hOwner = NULL

0040104B  |. E8 11000000     CALL <JMP.&USER32.MessageBoxA>           ; |\MessageBoxA

00401050  \> E8 06000000     CALL <JMP.&KERNEL32.ExitProcess>         ; \ExitProcess

00401055   $-FF25 50304000   JMP DWORD PTR DS:[<&KERNEL32.GetDriveTyp>;  kernel32.GetDriveTypeA

0040105B   .-FF25 54304000   JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>;  kernel32.ExitProcess


401026 부분의 JE 라는 명령어는 제로 플래그가 1일때 점프합니다. 즉 같을 경우에는 40103D로 넘어가겠다는 말입니다. 만약에 같지 않을 경우에는 어떻게 될까요? PUSH 명령으로 스택에 데이터를 넣고, 메세지 박스가 나온 뒤에 401050으로 점프하여 ExitProcess API를 만나 프로그램이 종료됩니다. 만약 40103D로 점프를 시켜준다면 강제로 메세지 박스를 띄울수 있겠죠. 즉, JE 부분에 JMP 명령을 넣어 40103D로 점프하면 끝납니다. 


그렇지만 문제에서 의도한 것은 아니므로, 다시 위로 돌아갑니다. 40101D에 브레이크 포인트를 걸어줍니다. 그리고 리턴값이 담긴 EAX 레지스터의 값을 바꿉니다. 5로 바꿔주면 ESI 레지스터의 값과 일치하겠죠.



OK를 누른 후 F8을 눌러 계속 진행하게 되면, 값이 서로 일치해서 맞아 떨어지므로 40103D로 점프됩니다.



그리고, CD-ROM 인식에 성공했다는 메세지와 함께 문제는 끝이납니다.