Cracking Devoney's Crackme 2.0 - by Sunshine

Target : Devoney's Crackme 2.0 - Find the secret text
Downloaded from : Crackmes.de
Author of target: Devoney's
Requirements:

* Ollydbg (I used Shadow Ollydbg)
* a bit knowledge about Asm and Windows API
* a tool converting char value <-> ascii value

Additional: * something to drink; I prefer always coffee or beer :-)
* good music; I listened to Flyleaf :-)

Download whole package here! (includes crackme and this tut)

First check what we gonna have to do: The exercise is to find a valid password which gives us somehow a secret text. To check if we got the correct secret text, we have to go to "http://members.lycos.nl/wietsite/fp.php?x=X" where 'X' is replaced with the secret text. I just chose any word, went to the site, and it just told me that I'm wrong :-( So we know what it's all about, let's start...

Ok, the crackme is coded in Assembler and just 2kb in size... nice! Starting it, we see just one input box and button, that's it. So I just left the input box empty, pressed the 'Go' button and got a really nice exception; so I restarted it, typed in anything, pressed the button and again I got an exception (altough this time it was another one ;-)

Let's crack this one...

So fire up Ollydbg and load our target: at the entrypoint we see after calls to GetModuleHandle and GetCurrentProcess (note that the handle to the current process is saved @ 4030C8). Then a buffer is allocated, we see some calls to MultiByteToWideChar and some structures are filled The window is created with DialogBoxIndirectParamA. The window procedure starts @ 4011A2. Ok, nothing strange till now... Examining it, we easily see that the WM_COMMAND message is handled from 4011CF on (remember, it's the constant 0x111). Then the text from the input box is taken, saved @ 403000 and a loop follows...
Scroll to the end of the loop, we see that there comes a call to WriteProcessMemory.

00401139 |. 837D 0C 01     CMP [ARG.2],1 ;                         -- WM_CREATE?
0040113D 74 20             JE SHORT nagoya.0040115F

0040132F FF35 21304000     PUSH DWORD PTR DS:[403021]               -- pBytesWritten
00401335 FF35 1D304000     PUSH DWORD PTR DS:[40301D]               -- BytesToWrite = 13 (19.)
0040133B 68 09304000       PUSH CrackMe2.00403009                   -- Buffer = CrackMe2.00403009
00401340 68 51134000       PUSH CrackMe2.00401351                   -- Address = 401351
00401345 FF35 C8304000     PUSH DWORD PTR DS:[4030C8]               -- hProcess (our process!)
0040134B E8 76000000       CALL <JMP.&kernel32.WriteProcessMemory>  -- WriteProcessMemory

Hm... let's have a detail look at it: 0x13 bytes from address 403009 are written to 401351 to this process. What, 401351? That's the address after the WriteProcessMemory call! So it's gonna be executed! That's means that the buffer at 403009 MUST contain code!
Ok, 0x13 bytes are written, so the memory from 403009 till 40301C must be executable. If we have some look at the loop, we see that our input string modifies this memory range. Now it's clear why the crackme crashes: our serial produces garbage in the memory buffer which are not valid opcodes.
Also note in the dump window in Ollydbg that right after our memory buffer, at address 403025 there is the string "The Secret Text is: 00000". If we look again at the loop, we see that this string is also modified by our entered password.

At first, I thought I had to choose the password so that the memory buffer 403009 contains 0x90 (just nops). So the crackme would run fine and I could dump the secret text at 403025. Well, I tried it, but it didn't work :-( I visited the discussion board of this crackme at crackmes.de and luckily the user profdracula gave us the hint that in order to crack it, you have to know how to call a messagebox! Ah, ok... we should call a messagebox where we put in the string from address 403025.

Nice, so I decided that my code should look like the following. Note that the code consists of 19 bytes, exactly the number of bytes written by WriteProcessMemory! I think that's a hint, cause we could for example leave the caption blank, but then we would need less bytes.

00401351 6A 00           PUSH 0
00401353 68 25304000     PUSH CrackMe2.00403025 ; ASCII "The Secret Text is: 00000"
00401358 68 25304000     PUSH CrackMe2.00403025 ; ASCII "The Secret Text is: 00000"
0040135D 6A 00           PUSH 0
00401360 E8 32000000     CALL <JMP.&user32.MessageBoxA>

So our buffer must look like this after the loop has processed our password:

00403009 = { 0x6A 0x00 0x68 0x25 0x30 0x40 0x00 0x68 0x25 0x30 0x40 0x00 0x6A 0x00 0xE8 0x32 0x00 0x00 0x00 }

Here is the complete loop:

004011FC |. BB FFFFFFFF       MOV EBX,-1
00401201 |. B9 FFFFFFFF       MOV ECX,-1
00401206 |> 43               /INC EBX                       ; ebx = loop counter
00401207 |. 36:8A0C1A        |MOV CL,BYTE PTR SS:[EDX+EBX]  ; cl = password[i]
0040120B |. 84C9             |TEST CL,CL                    ; cl == 0 ?
0040120D |. 0F84 1C010000    |JE CrackMe2.0040132F          ; -> bye
00401213 |. 83FB 00          |CMP EBX,0                     ; i == 0?
00401216 |. 77 40            |JA SHORT CrackMe2.00401258    ; no? -> jump
00401218 |. 80E9 5B          |SUB CL,5B                     ; cl -= 0x5B
0040121B |. 36:880D 0A304>   |MOV BYTE PTR SS:[40300A],CL   ; write cl to mem locations
00401222 |. 36:880D 0F304>   |MOV BYTE PTR SS:[40300F],CL
00401229 |. 36:880D 14304>   |MOV BYTE PTR SS:[403014],CL
00401230 |. 36:880D 16304>   |MOV BYTE PTR SS:[403016],CL
00401237 |. 36:880D 19304>   |MOV BYTE PTR SS:[403019],CL
0040123E |. 36:880D 1A304>   |MOV BYTE PTR SS:[40301A],CL
00401245 |. 36:880D 1B304>   |MOV BYTE PTR SS:[40301B],CL
0040124C |. 80C1 72          |ADD CL,72                     ; cl += 0x72
0040124F |. 36:880D 3A304>   |MOV BYTE PTR SS:[40303A],CL
00401256 |.^ EB AE           |JMP SHORT CrackMe2.00401206   ; jump back to loop begin
00401258 |> 83FB 01          |CMP EBX,1                     ; i == 1?
0040125B |. 77 1D            |JA SHORT CrackMe2.0040127A    ; no? -> jump
0040125D |. 80C1 0B          |ADD CL,0B                     ; cl += 0x0B
00401260 |. 36:880D 09304>   |MOV BYTE PTR SS:[403009],CL   ; write cl to mem locations
00401267 |. 36:880D 15304>   |MOV BYTE PTR SS:[403015],CL
0040126E |. 80E9 03          |SUB CL,3                      ; cl -= cl
00401271 |. 36:880D 39304>   |MOV BYTE PTR SS:[403039],CL
00401278 |.^ EB 8C           |JMP SHORT CrackMe2.00401206   ; jump back to loop begin
0040127A |> 83FB 02          |CMP EBX,2                     ; i == 2?
0040127D |. 77 20            |JA SHORT CrackMe2.0040129F    ; no? -> jump
0040127F |. 80C1 25          |ADD CL,25                     ; cl += 0x25
00401282 |. 36:880D 0B304>   |MOV BYTE PTR SS:[40300B],CL   ; write cl to mem locations
00401289 |. 36:880D 10304>   |MOV BYTE PTR SS:[403010],CL
00401290 |. 80E9 03          |SUB CL,3                      ; cl -= 0x03
00401293 |. 36:880D 3B304>   |MOV BYTE PTR SS:[40303B],CL
0040129A |.^ E9 67FFFFFF     |JMP CrackMe2.00401206         ; jump back to loop begin
0040129F |> 83FB 03          |CMP EBX,3                     ; i == 3?
004012A2 |. 77 20            |JA SHORT CrackMe2.004012C4    ; no? -> jump
004012A4 |. 80E9 42          |SUB CL,42
004012A7 |. 36:880D 0D304>   |MOV BYTE PTR SS:[40300D],CL   ; ... and so on
004012AE |. 36:880D 12304>   |MOV BYTE PTR SS:[403012],CL
004012B5 |. 80C1 35          |ADD CL,35
004012B8 |. 36:880D 3C304>   |MOV BYTE PTR SS:[40303C],CL
004012BF |.^ E9 42FFFFFF     |JMP CrackMe2.00401206
004012C4 |> 83FB 04          |CMP EBX,4
004012C7 |. 77 16            |JA SHORT CrackMe2.004012DF
004012C9 |. 80E9 21          |SUB CL,21
004012CC |. 36:880D 0E304>   |MOV BYTE PTR SS:[40300E],CL
004012D3 |. 36:880D 13304>   |MOV BYTE PTR SS:[403013],CL
004012DA |.^ E9 27FFFFFF     |JMP CrackMe2.00401206
004012DF |> 83FB 05          |CMP EBX,5
004012E2 |. 77 0F            |JA SHORT CrackMe2.004012F3
004012E4 |. 80C1 85          |ADD CL,85
004012E7 |. 36:880D 17304>   |MOV BYTE PTR SS:[403017],CL
004012EE |.^ E9 13FFFFFF     |JMP CrackMe2.00401206
004012F3 |> 83FB 06          |CMP EBX,6
004012F6 |. 77 0F            |JA SHORT CrackMe2.00401307
004012F8 |. 80E9 2C          |SUB CL,2C
004012FB |. 36:880D 0C304>   |MOV BYTE PTR SS:[40300C],CL
00401302 |.^ E9 FFFEFFFF     |JMP CrackMe2.00401206
00401307 |> 83FB 07          |CMP EBX,7
0040130A |. 77 19            |JA SHORT CrackMe2.00401325
0040130C |. 80E9 3A          |SUB CL,3A
0040130F |. 36:880D 11304>   |MOV BYTE PTR SS:[403011],CL
00401316 |. 80C1 3F          |ADD CL,3F
00401319 |. 36:880D 3D304>   |MOV BYTE PTR SS:[40303D],CL
00401320 |.^ E9 E1FEFFFF     \JMP CrackMe2.00401206
00401325 |> 80E9 2B           SUB CL,2B
00401328 |. 36:880D 18304>    MOV BYTE PTR SS:[403018],CL

Note that in the loop the eight seven chars of the password are processed, and the ninth one after the loop. Is the password longer, the other chars are ignored.

Let's examine what is done with the first char:
At 401218 0x5B are subtracted. Then this value is written at 40300A, 40300F and so on... Well our buffer starts at 403009, so at 40300A, 40300F, ... there must be a 0x00. So we get following equation:

char[0] - 0x5B must be 0x00
=> char[0] = 0x5B = '['

For the other eight characters of the password, we do the same: look which value is subtracted/added, at which position in the buffer it's written and which opcode byte is has to be. So we get the following:

char[1] + 0x0B is written @403009 which must be 0x6A.
=> char[1] = 0x5F = '_'

char[2] + 0x25 is written @40300B which must be 0x68.
=> char[2] = 0x43 = 'C'

char[3] - 0x42 is written @40300D which must be 0x30.
=> char[3] = 0x72 = 'r'

char[4] - 0x21 is written @40300E which must be 0x40.
=> char[4] = 0x61 = 'a'

char[5] + 0x85 is written @403017 which must be 0xE8.
=> char[5] = 0x85 = 'c'

char[6] + 0x2C is written @40300C which must be 0x25.
=> char[6] = 0x51 = 'Q'

char[7] + 0x3A is written @403011 which must be 0x25.
=> char[7] = 0x5F = '_'

char[8] + 0x2B is written @403018 which must be 0x32.
=> char[8] = 0x5D = ']'

So run the crackme, type in the input box "[_CracQ_]" and we get a nice messagebox: "The Secret Text is: greed". So take your prefered browser and go to http://members.lycos.nl/wietsite/fp.php?x=greed and we see the 'good boy' message.

Hm, "[_CracQ_]" doesn't make much sense, "[_Crack_]" look much better. If you try it you'll see it also works: the messagebox text remains the same, but the caption becomes "Check at http://members.lycos.nl/wietsite/fp.php?x=secret_text".
00401353 68 25304000     PUSH CrackMe2.00403025 becomes
00401353 68 3F304000     PUSH CrackMe2.0040303F
So I think this is the real solution, but both work! This was luck!

That was a really nice crackme. Hope you understood what I was talking about :-) Till the next time...

Sunshine, February 2k7


This site is part of Sunshine's Homepage