외로운 Nova의 작업실

creackme 시리즈 - 2(crackme10.exe) 본문

Computer App Penetesting/Reversing

creackme 시리즈 - 2(crackme10.exe)

Nova_ 2023. 1. 26. 16:34

이번 장에서는 크랙미 10을 크랙해보겠습니다.

Crackme 10.exe
0.01MB

 

 

- 문제 파악

한번 실행해보겠습니다.

시리얼을 입력하고 check 버튼을 눌러서 확인하는 것같다는 생각이듭니다. 1234를 넣고 check버튼을 눌러보겠습니다.

wrong으로 틀리다고 나옵니다. 이번 문제도 시리얼 넘버를 맞추는 문제인것 같다는 생각이듭니다.

 

- 리버싱

먼저 올리디버거로 리버싱해보겠습니다.

F9를 눌러서 프로그램의 시리얼값을 받을때 pause 되어야되지만 실제 F9를 누르면 프로그램이 종료가됩니다. 이는 첫번째 실행되는 코드부분에서 디버깅되었는지 확인하는 코드때문입니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
00401000  /$  E8 37020000   CALL <JMP.&kernel32.IsDebuggerPresent>   ; [KERNEL32.IsDebuggerPresent
00401005  |.  83F8 00       CMP EAX,0                                ; CONST 0 => FALSE
00401008  |.  75 25         JNE SHORT 0040102F
0040100A  |.  6A 00         PUSH 0                                   ; /ModuleName = NULL

IsDebuggerPresent를 call한후 만약 디버깅상태라면 eax에 0이 아닌 값이 들어가고 JNE 구문이 실행되어 프로그램이 종료됩니다. 따라서 JNE 구문이 실행되지않게 cmp 명령어 이후 ZF를 1로 변경해줘야합니다.

이후 F9를 누르면 프로그램이 실행되는 것을 확인할 수 있습니다.

이제 check 부분을 누르면 실행되는 코드부분을 찾아보겠습니다. PUSH EBP부분을 검색하고 f2로 브레이크를 걸겠습니다.

해당 코드 이후에는 PUSH EBP가 없습니다. 이제 동적분석을 위해 프로그램에 1234를 넣고 실행시켜보겠습니다. 그러면 첫번쨰 PUSH EBP부분에서 브레이크가 걸리고 계속 F9를 눌러도 실행이 잘안됩니다. 이 부분은 브레이크를 풀어주겠습니다. 이제 1234를 넣고 check버튼을 눌러보겠습니다.

그러면 위와같이 브레이크가 걸립니다. 401148 부분이 check 버튼의 콜백함수임을 알 수 있습니다. 이제 천천히 step over로 하나씩 분석해보겠습니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
0040114A  /$  55            PUSH EBP
0040114B  |.  8BEC          MOV EBP,ESP
0040114D  |.  83C4 F8       ADD ESP,-8
00401150  |.  68 24314000   PUSH OFFSET 00403124                     ; /String = "1234"
00401155  |.  E8 F4000000   CALL <JMP.&kernel32.lstrlenA>            ; \KERNEL32.lstrlen
0040115A  |.  8945 FC       MOV DWORD PTR SS:[LOCAL.1],EAX
0040115D      837D FC 0E    CMP DWORD PTR SS:[EBP-4],0E
00401161  |.  0F85 8A000000 JNE 004011F1

위는 call 명령어를 통해 1234의 길이를 eax에 받아오고 그걸 EBP-4 스택부분에 옮기고 0E와 비교하는 코드입니다. 만약 같지않다면 exit 부분으로 jump하는 것을 알 수 있습니다. 즉, 실제 시리얼키의 길이는 0E로 15임을 알 수 있습니다. 여기서 계속 분석해야하기때문에 CMP 명령어 다음에 ZF를 1로 설정하고 넘어가겠습니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
00401167  |.  68 48304000   PUSH OFFSET 00403048                     ; /Src = "VaZoNeZ"
0040116C  |.  68 A4314000   PUSH OFFSET 004031A4                     ; |Dest = "VaZoNeZVaZoNeZ"
00401171  |.  E8 D2000000   CALL <JMP.&kernel32.lstrcpyA>            ; \KERNEL32.lstrcpy
00401176  |.  68 48304000   PUSH OFFSET 00403048                     ; /Src = "VaZoNeZ"
0040117B  |.  68 A4314000   PUSH OFFSET 004031A4                     ; |Dest = "VaZoNeZVaZoNeZ"
00401180  |.  E8 BD000000   CALL <JMP.&kernel32.lstrcatA>            ; \KERNEL32.lstrcat
00401185  |.  8D45 F8       LEA EAX,[LOCAL.2]

위코드는 시리얼의 재료를 만드는 코드입니다. 즉 lstrcpy로 VaZoNeZ 문자열을 복사하고 VaZoNeZ문자열 뒤에 lstrcatA 함수로 붙여넣습니다. 따라서 VaZoNeZVaZoNeZ 문자열이 완성되며 이문자열은 004031A4에 저장됩니다. 다음 코드로 넘어가겠습니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
00401188  |.  50            PUSH EAX                                 ; /Bufsize = 1
00401189  |.  68 24324000   PUSH OFFSET 00403224                     ; |Buffer = "Nova"
0040118E  |.  E8 C1000000   CALL <JMP.&advapi32.GetUserNameA>        ; \ADVAPI32.GetUserNameA
00401193  |.  837D F8 00    CMP DWORD PTR SS:[LOCAL.2],0
00401197  |.  74 58         JE SHORT 004011F1

위 코드는  GetUserNameA 함수를 통해 컴퓨터의 사용자이름을 버퍼부분 00403224에 넣어주고 그 길이를 stack에 넣어줍니다. 따라서 DWORD PTR SS:[LOCAL.2]에는 사용자의 이름의 길이인 4가 들어가게되고 만약 그 길이가 0과 같다면 exit코드로 점프하게됩니다. 즉, 사용자이름의 길이가 0이라면 바로 exit 되는 구문입니다. 우리는 길이가 0이 아니기에 step over 해줍니다. 다음으로 넘어가겠습니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
00401199  |.  BF 24324000   MOV EDI,OFFSET 00403224                  ; ASCII "Nova"
0040119E  |.  B9 A4314000   MOV ECX,OFFSET 004031A4                  ; ASCII "VaZoNeZVaZoNeZ"
004011A3  |.  0FB607        MOVZX EAX,BYTE PTR DS:[EDI]
004011A6  |>  0FB611        /MOVZX EDX,BYTE PTR DS:[ECX]
004011A9  |.  85D2          |TEST EDX,EDX
004011AB  |.  74 0F         |JZ SHORT 004011BC
004011AD  |.  02D0          |ADD DL,AL
004011AF  |.  80F2 05       |XOR DL,05
004011B2  |.  02D1          |ADD DL,CL
004011B4  |.  80EA 1E       |SUB DL,1E
004011B7  |.  8811          |MOV BYTE PTR DS:[ECX],DL
004011B9  |.  41            |INC ECX
004011BA  |.^ EB EA         \JMP SHORT 004011A6

위 코드는 반복문인 것을 알 수 있습니다. 즉, for문입니다. TEST EDX,EDX가 0이 될때까지 반복하는 구문입니다. 코드를 분석해보면 Nova와 VaZoNeZVaZoNeZ 문자열에서 각각 1바이트 즉, 한개의 문자를 가지고와서 ADD 하고 XOR 하고 ADD하고 SUB한 값을 VaZoNeZVaZoNeZ문자열의 n번째에 넣어주게됩니다. 언제까지하냐면 TEST EDX, EDX가 0이되어야하니 밑의 코드에서 ECX가 VaZoNeZVaZoNeZ의 길이인 14를 넘어야 끝나게됩니다. 즉 VaZoNeZVaZoNeZ 문자열을 암호화하는 코드입니다.

004011A6  |>  0FB611        /MOVZX EDX,BYTE PTR DS:[ECX]

CPU Disasm
Address   Hex dump          Command                                  Comments
004011BC  |> \B9 A4314000   MOV ECX,OFFSET 004031A4                  ; ASCII "Nd\lJd`UkcsQkg"
004011C1  |.  BA 24314000   MOV EDX,OFFSET 00403124                  ; ASCII "1234"
004011C6  |.  33FF          XOR EDI,EDI
004011C8  |>  0FB601        MOVZX EAX,BYTE PTR DS:[ECX]
004011CB  |.  0FB61A        MOVZX EBX,BYTE PTR DS:[EDX]
004011CE      3BC3          CMP EAX,EBX
004011D0  |.  75 1F         JNE SHORT 004011F1
004011D2      83FF 0E       CMP EDI,0E
004011D5  |.  74 05         JE SHORT 004011DC
004011D7  |.  41            INC ECX
004011D8  |.  42            INC EDX
004011D9  |.  47            INC EDI
004011DA  |.^ EB EC         JMP SHORT 004011C8

암호화 이후 문자열은 Nd\lJd`UkcsQkg가 됩니다. 위 코드는 반복문으로 Nd\lJd`UkcsQkg 문자열과 1234 문자열을 비교합니다. 몇번하냐면 EDI 값이 0E가 될때까지, 즉 처음에 XOR EDI, EDI로 0으로 초기화했으니 15번 진행합니다. 이는 Nd\lJd`UkcsQkg 문자열의 길이입니다. 사용자가 입력한 값에서 한바이트, Nd\lJd`UkcsQkg문자열에서 한바이트씩 가져와서 비교합니다. 이후 15번이 모두 같으면 004011D5의 코드가 실행되어 밑에 코드로 넘어갑니다.

CPU Disasm
Address   Hex dump          Command                                  Comments
004011DC  |> \6A 00         PUSH 0                                   ; /Type = MB_OK|MB_DEFBUTTON1|MB_APPLMODAL
004011DE  |.  68 42304000   PUSH OFFSET 00403042                     ; |Caption = "Lucky"
004011E3  |.  68 31304000   PUSH OFFSET 00403031                     ; |Text = "Rigth number !!!"
004011E8  |.  6A 00         PUSH 0                                   ; |hOwner = NULL
004011EA  |.  E8 35000000   CALL <JMP.&user32.MessageBoxA>           ; \USER32.MessageBoxA

그러면 Right number 가 뜹니다. 즉, Nd\lJd`UkcsQkg문자열이 알맞은 키값이였다는 말입니다. 하지만 이 키값은 사용자의 이름에 따라 달라집니다. 저의 컴퓨터에선 Nd\lJd`UkcsQkg이지만 구독자님들의 컴퓨터에선 다를 것입니다. 그럼한번 Nd\lJd`UkcsQkg을 넣어보겠습니다.

알맞은 키라고 나옵니다.

 

- CRACK

크랙은 길이를 비교하는 부분과 마지막 시리얼값 비교구문을 크랙해주면됩니다.

<길이비교 크랙>

<시리얼 비교구문 크랙>

이렇게 만든후에 저장해주면됩니다.

Crackme10(cracked).exe
0.01MB

Comments