cover image for post 'Crackmes.de – CrackMe v2.0 by Greedy_Fly'

Crackmes.de – CrackMe v2.0 by Greedy_Fly

The crackme CrackMe v2.0 by Greedy_Fly has been published April 22, 2015. It is rated at 2 - Needs a little brain (or luck). The crackme is written in Assembler and runs on Windows. The description reads:

Hi, All…Test Your Skills… ))))

Solution: Valid Serial and solution.txt //Don’t post your solution(Serial) on the board! Have Fun! Greedy_Fly

The crackme greets us with the following user interface:

The chess board looks like a regular “White to move first and checkmate Black”-problem. The serial is probably the solution to the problem. But before trying to solve the chess problem, let’s look at the disassembly first to see if we can find additional hints.

Serial Length

The following lines read the serial, call a subroutine that I named length_function with the length of the serial as the argument, compare the result to 148224 and jump to fail if the values don’t match:

.text:004012E5 push    68h             ; nIDDlgItem
.text:004012E7 push    [ebp+hWnd]      ; hDlg
.text:004012EA call    GetDlgItem
.text:004012EF mov     dword ptr hwnd, eax
.text:004012F4 lea     eax, input      ; "serial"
.text:004012FA push    eax             ; lParam
.text:004012FB push    20h             ; wParam
.text:004012FD push    0Dh             ; Msg
.text:004012FF push    dword ptr hwnd  ; hWnd
.text:00401305 call    SendMessageA
.text:0040130A push    eax
.text:0040130B xchg    eax, ebx
.text:0040130C call    length_function
.text:00401311 cmp     esi, 148224
.text:00401317 jz      short valid_length

The routine length_function looks like that:

.text:0040148E length_function proc near
.text:0040148E inc     ebx
.text:0040148F shl     ebx, 4          ; times 16
.text:00401492 mov     esi, 64
.text:00401497 lea     edi, [ebx+esi]  ; 16*(ebx+1)+64
.text:0040149A imul    ebx, edi, 386   ; *386
.text:004014A0 xor     esi, esi
.text:004014A2 xchg    esi, ebx
.text:004014A4 retn
.text:004014A4 length_function endp

It calculates the following value (where l is the length of the serial):

$$ (16\cdot(l+1) +64)\cdot 386 $$

So we need to have:

$$ \begin{align} (16\cdot(l+1) +64)\cdot 386 &= 148224 \\ (16\cdot(l+1) +64) &= 384 \\ 16l + 80 &= 384 \\ l &= 19 \end{align} $$

Conclusion: The length of the serial needs to be 19 characters.

Hash and Check

If the length of the serial is 19 characters, we get to these instructions:

.text:0040131B valid_length:
.text:0040131B call    md5open
.text:00401320 pop     ecx
.text:00401321 push    ecx
.text:00401322 push    offset input    ; "serial"
.text:00401327 call    make_copy
.text:0040132C call    hash
.text:00401331 push    offset hex_hash ; "0B17E4358A309CC122E8856AD0FCACEC"
.text:00401336 push    10h
.text:00401338 push    eax
.text:00401339 call    to_hex
.text:0040133E lea     esi, hex_hash   ; "0B17E4358A309CC122E8856AD0FCACEC"
.text:00401344 lea     edi, a7e9c7f1a62b7b9 ; "7E9C7F1A62B7B93F34A6A6C16BCAA840"
.text:0040134A mov     ecx, 20h
.text:0040134F cld
.text:00401350 repe cmpsb
.text:00401352 jnz     short fail

The first subroutine — I named it md5open — is:

.text:00402240 md5open proc near
.text:00402240 push    edi
.text:00402241 xor     eax, eax
.text:00402243 mov     offset2, eax
.text:00402248 xor     eax, eax
.text:0040224A mov     offset, eax
.text:0040224F mov     edi, offset input_copy
.text:00402254 mov     ecx, 10h
.text:00402259 rep stosd
.text:0040225B mov     eax, offset md5
.text:00402260 mov     dword ptr [eax], 67452301h
.text:00402266 mov     dword ptr [eax+4], 0EFCDAB89h
.text:0040226D mov     dword ptr [eax+8], 98BADCFEh
.text:00402274 mov     dword ptr [eax+0Ch], 10325476h
.text:0040227B pop     edi
.text:0040227C retn
.text:0040227C md5open endp

The last four constants should look familiar: those are the 32bits used as the starting point of the MD5 calculation. Sure enough, eventually the crackme will call the following routine:

.text:00401830 md5_0 proc near
.text:00401830
.text:00401830 var_30= dword ptr -30h
.text:00401830 var_2C= dword ptr -2Ch
.text:00401830 var_28= dword ptr -28h
.text:00401830 var_24= dword ptr -24h
.text:00401830 anonymous_0= qword ptr -20h
.text:00401830
.text:00401830 pusha
.text:00401831 mov     esi, offset md5
.text:00401836 mov     ebp, offset input_copy
.text:0040183B mov     eax, [esi]
.text:0040183D mov     ebx, [esi+4]
.text:00401840 mov     ecx, [esi+8]
.text:00401843 mov     edx, [esi+0Ch]
.text:00401846 mov     edi, ebx
.text:00401848 xor     edi, ecx
.text:0040184A xor     edi, edx
.text:0040184C add     eax, [ebp+0]
.text:0040184F add     eax, edi
.text:00401851 rol     eax, 0Bh
.text:00401854 mov     edi, eax
.text:00401856 xor     edi, ebx
.text:00401858 xor     edi, ecx
.text:0040185A add     edx, [ebp+4]
.text:0040185D add     edx, edi
.text:0040185F rol     edx, 0Eh
.text:00401862 mov     edi, edx
.text:00401864 xor     edi, eax
.text:00401866 xor     edi, ebx
.text:00401868 add     ecx, [ebp+8]
.text:0040186B add     ecx, edi
.text:0040186D rol     ecx, 0Fh
.text:00401870 mov     edi, ecx
.text:00401872 xor     edi, edx
...

This is the start of the MD5 hashing of the 64 bit value in input_copy. The value of input_copy consists of the entered serial followed by the hardcoded byte 0x80, and the result of 19 times 8, i.e., 0x98, at the eighth last byte:

.data:00404150 input_copy db 61h, 34h, 61h, 34h, 62h, 35h, 61h, 33h 
.data:00404150 db 4Eh, 62h, 34h, 2Bh, 4Bh, 61h, 31h, 4Eh
.data:00404150 db 62h, 33h, 23h, 80h, 0, 0, 0, 0
.data:00404150 db 0, 0, 0, 0, 0, 0, 0, 0
.data:00404150 db 0, 0, 0, 0, 0, 0, 0, 0
.data:00404150 db 0, 0, 0, 0, 0, 0, 0, 0
.data:00404150 db 0, 0, 0, 0, 0, 0, 0, 0
.data:00404150 db 98h, 0, 0, 0, 0, 0, 0, 0

After the MD5 hashing seems to follow another hash, maybe SHA. The resulting hash is converted to a hex string by the call at offset 0x401339, and the result is compared to the hardcoded string “7E9C7F1A62B7B93F34A6A6C16BCAA840” (offset 0x401344).

There is no way we can reverse the hash result or even brute-force the 19 character serial. So we really need to solve the chess problem. Fortunately, it is a very easy one. Here is the unique, shortest sequence of moves to get to a checkmate:

  • a4
  • bxa4
  • b5
  • a3
  • Nb4+
  • Ka1
  • Nb3#

All that’s left is to convert those move into a 19 character string. Since the above moves take 21 characters, you need to lose the check (+) and checkmate (#) indicators:

a4bxa4b5a3Nb4Ka1Nb3

Entering this string give the goodboy messages: