외로운 Nova의 작업실

리버싱 입문 - 1(crackme1.exe) 본문

Computer App Penetesting/Reversing

리버싱 입문 - 1(crackme1.exe)

Nova_ 2022. 11. 30. 18:19

 안녕하세요. 저는 시스템해킹을 배우기위해 리버싱입문(조성문)책을 샀고 그에 따라 실습한 내용들을 기록해볼까합니다. 먼저, 준비물은 ollydbg와 crackme1.exe 파일입니다. ollydbg는 v1.10을 사용하였습니다. crackme1.exe 파일은 아래에 올려놓겠습니다. 세계에서 가장 많이 활용되는 예제가 abex crackme 시리즈라고 합니다. 

 

Crackme1.exe
0.01MB

 

 

자 이제 실습을 진행해보겠습니다.

 

- 문제 인식

먼저 Crackme1.exe를 실행시켜보도록 하겠습니다.

그러면 하드디스크를 cd-rom으로 생각하게 자신을 만들어달라고합니다. 그렇다면 하드디스크를 cd-rom으로 인식하게  바꿔주면되겠죠? 일단 내부적으로 어떤 window API를 쓰는지 ollydbg로 열어보겠습니다.

 

- 코드 분석

00401000부터 Crackme1.exe가 실행되는 것을 알 수 있습니다. 이는 메모리의 절대주소로 현재 프로그램이 실행된 상태이며 메모리에 올라와있고 그 메모리의 내용을 보여줍니다. 

00401000 >/$ 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
00401002  |. 68 00204000    PUSH Crackme1.00402000                   ; |Title = "abex' 1st crackme"
00401007  |. 68 12204000    PUSH Crackme1.00402012                   ; |Text = "Make me think your HD is a CD-Rom."
0040100C  |. 6A 00          PUSH 0                                   ; |hOwner = NULL
0040100E  |. E8 4E000000    CALL <JMP.&USER32.MessageBoxA>           ; \MessageBoxA
00401013  |. 68 94204000    PUSH Crackme1.00402094                   ; /RootPathName = "c:\"
00401018  |. E8 38000000    CALL <JMP.&KERNEL32.GetDriveTypeA>       ; \GetDriveTypeA
0040101D  |. 46             INC ESI
0040101E  |. 48             DEC EAX
0040101F  |. EB 00          JMP SHORT Crackme1.00401021
00401021  |> 46             INC ESI
00401022  |. 46             INC ESI
00401023  |. 48             DEC EAX
00401024  |. 3BC6           CMP EAX,ESI
00401026  |. 74 15          JE SHORT Crackme1.0040103D
00401028  |. 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040102A  |. 68 35204000    PUSH Crackme1.00402035                   ; |Title = "Error"
0040102F  |. 68 3B204000    PUSH Crackme1.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 Crackme1.00401050
0040103D  |> 6A 00          PUSH 0                                   ; |/Style = MB_OK|MB_APPLMODAL
0040103F  |. 68 5E204000    PUSH Crackme1.0040205E                   ; ||Title = "YEAH!"
00401044  |. 68 64204000    PUSH Crackme1.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
00401061   $-FF25 5C304000  JMP DWORD PTR DS:[<&USER32.MessageBoxA>] ;  USER32.MessageBoxA

00401000에서 계속 push 하다가0040100E에서 CALL <JMP.&USER32.MessageBoxA>를 하는 것을 볼 수 있습니다. MessageBoxA는 Win API 함수로 MSDN에서 검색하여 어떤 함수인지 알아보겠습니다.
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messageboxa

 

MessageBoxA function (winuser.h) - Win32 apps

Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message, such as status or error information. The message box returns an integer value that indicates which button the user clicked. (MessageBoxA)

learn.microsoft.com

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

MessageBoxA 함수는 메시지 박스를 만드는 함수로 인자값은 hWnd, lpText, lpCation, utype임을 알 수 있습니다. 즉 아까 했던 push는 이 인자값을 전달하기위함이였음을 알 수 있습니다. hWnd는 윈도우의 핸들러이고 lpText는 메시지의 내용, lpCation은 메시지박스의 타이틀, uType은 버튼 생성을 어떤것으로 할건지 정하는 매개변수 입니다. 실제 코드에서 저장된 내용을 보면

00401000 >/$ 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
00401002  |. 68 00204000    PUSH Crackme1.00402000                   ; |Title = "abex' 1st crackme"
00401007  |. 68 12204000    PUSH Crackme1.00402012                   ; |Text = "Make me think your HD is a CD-Rom."
0040100C  |. 6A 00          PUSH 0                                   ; |hOwner = NULL

utype에는 0이 들어갔으니 Ok 버튼만 있는 메시지박스가 출력될 것이며 title은 "abex 1st crack me"가 될것이며 메시지 내용은 Make me think your HD is a CD-Rom 이고 hWnd가 0이니 기본 윈도우에 메시지 박스가 뜨게 만들어놨습니다. 이는 아까 문제 확인때 보았던 메시지박스를 만드는 코드임을 알 수 있습니다. 함수 호출 이후 코드르 보겠습니다.

00401013  |. 68 94204000    PUSH Crackme1.00402094                   ; /RootPathName = "c:\"
00401018  |. E8 38000000    CALL <JMP.&KERNEL32.GetDriveTypeA>       ; \GetDriveTypeA
0040101D  |. 46             INC ESI
0040101E  |. 48             DEC EAX
0040101F  |. EB 00          JMP SHORT Crackme1.00401021
00401021  |> 46             INC ESI
00401022  |. 46             INC ESI
00401023  |. 48             DEC EAX
00401024  |. 3BC6           CMP EAX,ESI
00401026  |. 74 15          JE SHORT Crackme1.0040103D

이후 어떤 값을 push 하고GetDriveTypeA 함수를 호출하고 있습니다. 이함수도 MSDN에서 확인해보겠습니다.

https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getdrivetypea

 

GetDriveTypeA function (fileapi.h) - Win32 apps

Determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive. (ANSI)

learn.microsoft.com

경로를 인자로받아 그 경로가 어떤 드라이브 타입인지 EAX로 반환해주는 것을 알 수 있습니다. 

인자로 C:/ 경로를 주었으니 아마 하드디스크이기때문에 3이 eax에 담겼을겁니다. 이후 코드를 보면 eax는 2번 dec되고 esi는 3번 inc됩니다. 이후 eax와 esi를 비교한후 JE(jump to equal) 문장을 실행합니다. 즉, 만약 c:/ 경로가 CD-Rom이여서 eax에 5가 반환됫다고 eax는 5-2해서 3이될 것이고 esi는 원래 0이였기때문에 0+3해서 3이될것이고 eax도3, esi도3이기때문에 JE명령어가 실행될 것 입니다. 

00401028  |. 6A 00          PUSH 0                                   ; /Style = MB_OK|MB_APPLMODAL
0040102A  |. 68 35204000    PUSH Crackme1.00402035                   ; |Title = "Error"
0040102F  |. 68 3B204000    PUSH Crackme1.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 Crackme1.00401050
0040103D  |> 6A 00          PUSH 0                                   ; |/Style = MB_OK|MB_APPLMODAL
0040103F  |. 68 5E204000    PUSH Crackme1.0040205E                   ; ||Title = "YEAH!"
00401044  |. 68 64204000    PUSH Crackme1.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
00401061   $-FF25 5C304000  JMP DWORD PTR DS:[<&USER32.MessageBoxA>] ;  USER32.MessageBoxA

 만약 eax와 esi가 같지않으면 00401028로 넘어가서 Error라는 메시지박스를 생성할테고 만약 eax와 esi가 같다면 아래 문장이 실행되어 0040103D로 점프하게되어 ok~ 라는 메시지박스가 생성될 것 입니다.

00401026  |. 74 15          JE SHORT Crackme1.0040103D

 

- Crack

이로써 프로그램의 구조를 알게되었으니 크랙을 한번 해보겠습니다. 제가 택한 방법은 GetDriveTypeA 함수 호출 이후에 JMP 명령어가 하나 있습니다. 이를 이용해 성공한 부분(0040103D)으로 점프시켜보도록 하겠습니다. 코드를 변경하는 방법은 코드줄을 선택하고 오른쪽 마우스클릭후 assemble을 클릭하면됩니다.

코드를 변경했다면 code 부분에서 오른쪽마우스클릭 - copy to executable - all modification - copy all을 선택하고 이후 새롭게 뜨는 창에서 오른쪽마우스클릭 - save file을 눌러주면 수정한 코드가 적용된 exe 파일이 생성됩니다. 제가 크랙한 파일을 올려놓겠습니다.

Crackme1(cracked).exe
0.01MB

 

 

이걸 실행해보면

성공적으로 크랙한 것을 확인 할 수 있습니다.

Comments