OK, for this tutorial, You should have:
* SoftIce (Version is personal preference) * W32DASM 8.9/8.93 * Hopefully an API ref, but you can still tag along with my little lookup until you get a real one ;) * Of course, the crackme. Reading the Keyfile - What, Where and How Much?OK, seeing as this is a keyfile crackme, let's look up an API that will check if the file exists. CreateFile looks good:" The CreateFile function creates or opens the following objects and returns a handle that can be used to access the object. "So, off we go into SoftICE and BPX CreateFileA. When we press "Check", we break in SoftICE:
The "CMP EAX, -01" is checking the return value to see if the file was opened or not. If -01 is returned, then the file doesn't exist. .You should see from the API ref that the last parameter is the filename to load, so do a "d edx". You see:
At the end of the ".bit", there is a 00, or NULL, making this a null-terminated string. This matches up with our API: "Points to a null-terminated string that specifies the name of the object". This is the name of our keyfile! So, now let's make a "fake" keyfile. Time for a small tip from +Aesculapius:
Damn, I love the Cracker's Notes 8). Anyway, I decided to use 20h bytes worth of the "Vindaloo" song :D, like so: 00000000: 56 69 6E 64-61 6C 6F 6F-21 20 56 69-6E 64 79 6C Vindaloo! Vindyl 00000010: 6F 6F 2C 20-6C 61 20 6C-61 21 20 4E-61 20 6E 61 oo, la la! Na na OK, so let's fire up SoftICE and we see this:
Have look at your API reference yet again, and you should see that this is reading 01 byte from the file handle stored at DWORD PTR [00403444] into 004034FA. Doesn't mean much yet? Let's see what it's just read into 4034FA. Do a "D 4034FA", and you'll see:
So, this reads the first byte of our file into 4034FA, and then MOVZX's it into EAX. If TEST EAX, EAX comes out as zero, then EAX is zero. So it also tests if the first byte of the file is zero. Let's trace some more:
OK, so it's now using the first byte of the file to "know" how many more bytes to read. Let's replace the vindaloo song with something a little closer to the keyfile: 00000000: 07 42 6F 6F-6D 42 6F 78- - BoomBox So it should now only read "BoomBox" in. Let's see what happens next:
OK, it's now reading in 12h bytes. 12h = 18 decimal, so lets update our keyfile with some padding: 00000000: 07 42 6F 6F-6D 42 6F 78-31 32 33 34-35 36 37 38 BoomBox12345678 00000010: 39 30 31 32-33 34 35 36-37 38 - 9012345678 OK, we now see:
So it's now finished reading the file, closing it, and jumping somewhere. Where?
This ends the call into the check. The Check Part 1 - A Wierd Grid:0040173D E8D0000000 Call 00401812 ; KERNEL32.ReadFile, Ord:01FDh :00401742 E882F9FFFF call 004010C9 ; ??? :00401747 FF3544344000 push dword ptr [00403444] ; The file handle :0040174D E8A2000000 Call 004017F4 ; KERNEL32.CloseHandle, Ord:0019hLet's have a look at the call: :004010C9 55 push ebp :004010CA 8BEC mov ebp, esp :004010CC 83C4FC add esp, FFFFFFFC :004010CF 6865334000 push 00403365 ; Wierd string :004010D4 68BC314000 push 004031BC ; The same string, I think :004010D9 E83A070000 Call 00401818 ; KERNEL32.lstrcpyA, Ord:02DChHum. Let's have a look at this wierd string from SoftICE:
OK, let's remember this and come back to it. The Check Part 2 - XORingIn the call: :0040101D 8A15FB344000 mov dl, byte ptr [004034FB] :00401023 B912000000 mov ecx, 00000012 ; Repeat 12h times :00401028 B8E8344000 mov eax, 004034E8 ; mov eax, your 12h digits :0040102D 3010 xor byte ptr [eax], dl ; XOR your 12h digits with something? :0040102F 40 inc eax ; Next digit :00401030 E2FB loop 0040102D ; A loop! :) :00401032 C3 retOk, at the end of this, eax is our 12 bytes XOR B6. So, I'm gonna update me keyfile: 00000000: 07 42 6F 6F-6D 42 6F 78-87 84 85 82-83 80 81 8E BoomBoxçäàéâÇüÄ 00000010: 8F 86 87 84-85 82 83 80-81 8E 8F - ÅåçäàéâÇüÄÅBack to the code: This is moving 4034FB, which is just past where the file was loaded into memory. This is a "hash" of my name. When I change the 42("B") to a 43("C"), it changes to B7. It might be a total of some sort. We now have some pseudo-code: * Read the first byte of the file * Check if it's zero * Get a string of the same length as the value of the first byte of the file * Make a hash of this & store * Read the next 12h bytes * XOR these 12h bytes with the hash * Check the 12h bytes somehow...I'm gonna try & find where the hash is calculated. Let's have a look at the next call: :00401724 E8D7F8FFFF call 00401000 :00401000 33C0 xor eax, eax :00401002 33D2 xor edx, edx :00401004 33C9 xor ecx, ecx :00401006 8A0DFA344000 mov cl, byte ptr [004034FA] ; The first byte of the file (See above) :0040100C BE88324000 mov esi, 00403288 ; "BoomBox" :00401011 AC lodsb :00401012 03D0 add edx, eax ; Add to running total :00401014 E2FB loop 00401011 :00401016 8815FB344000 mov byte ptr [004034FB], dl ; The address we saw earlier :0040101C C3 retFirst this code empties EAX, and puts the first byte of the file in CL. It then loops round CL times, adding the currant byte of your name into EDX. After this, it takes DL (The low byte of EDX), and moves it into the addry we saw earlier, which is the "hash" of our name. The Check Part 3 - A Loop Inside A LoopNow, all this was called from here::004010E8 E830FFFFFF call 0040101DSo let's go looking for something interesting after this... :004010ED C645FE00 mov [ebp-02], 00 ; [EBP-02] -=> 00 :004010F1 33C0 xor eax, eax :004010F3 33C9 xor ecx, ecx :004010F5 C645FF08 mov [ebp-01], 08 ; [EBP-01] -=> 08 :004010F9 806DFF02 sub byte ptr [ebp-01], 02 ; [EBP-01] -=> 06 :004010FD 0FB64DFE movzx ecx, byte ptr [ebp-02] ; ECX -=> 00 :00401101 81C1E8344000 add ecx, 004034E8 ; ECX -=> 4034E8 -=> The grid :00401107 8A01 mov al, byte ptr [ecx] ; AL -=> The grid :00401109 8A4DFF mov cl, byte ptr [ebp-01] ; Cl -=> 06 :0040110C D2E8 shr al, cl :0040110E 2403 and al, 03 :00401110 E81EFFFFFF call 00401033 ; ??? :00401115 85C0 test eax, eax ; eax=0 -=> Bad :00401117 7411 je 0040112A :00401119 0FB655FF movzx edx, byte ptr [ebp-01] :0040111D 85D2 test edx, edx :0040111F 75D8 jne 004010F9 ; Small loop :00401121 FE45FE inc [ebp-02] ; Big loop counter :00401124 807DFE12 cmp byte ptr [ebp-02], 12 ; Bigger loop :00401128 75CB jne 004010F5 :0040112A C9 leave :0040112B C3 retWe have 2 sets of CMP/JNE here, so there's probably 2 loops, a loop inside a loop. The first one uses [ebp-01] as a counter, the second [ebp-02]. I've commented the code to give you a better idea of what's going on. I had to come back and look at this to understand it, read on to find out why... The Check Part 4 - Moving PacManLet's trace into the call...:00401033 55 push ebp :00401034 8BEC mov ebp, esp :00401036 83C4F8 add esp, FFFFFFF8 :00401039 8B1584314000 mov edx, dword ptr [00403184] :0040103F 8955FC mov dword ptr [ebp-04], edx :00401042 0AC0 or al, al ; 0 :00401044 7509 jne 0040104F :00401046 832D8431400010 sub dword ptr [00403184], 00000010 ; 0 -=> -10 :0040104D EB1F jmp 0040106E :0040104F 3C01 cmp al, 01 ; 01 :00401051 7508 jne 0040105B :00401053 FF0584314000 inc dword ptr [00403184] ; 01 -=> +1 :00401059 EB13 jmp 0040106E :0040105B 3C02 cmp al, 02 ; 02 :0040105D 7509 jne 00401068 :0040105F 83058431400010 add dword ptr [00403184], 00000010 ; 02 -=> +10 :00401066 EB06 jmp 0040106E :00401068 FF0D84314000 dec dword ptr [00403184] ; Else -=> 03 -=> -1 :0040106E 8B1584314000 mov edx, dword ptr [00403184] :00401074 8A02 mov al, byte ptr [edx] ; AL -=> Our position on the grid :00401076 3C2A cmp al, 2A ; CMP AL, "*" :00401078 7506 jne 00401080 :0040107A 33C0 xor eax, eax :0040107C C9 leave :0040107D C3 ret :00401080 3C58 cmp al, 58 ; CMP AL, "X" :00401082 752F jne 004010B3 :00401084 6A00 push 00000000 :00401086 8D1559334000 lea edx, dword ptr [00403359] :0040108C 52 push edx :0040108D 8D15EC324000 lea edx, dword ptr [004032EC] :00401093 52 push edx :00401094 6A00 push 00000000 :00401096 8D15AC174000 lea edx, dword ptr [004017AC] ; MessageBoxA :0040109C FFD2 call edx :0040109E 8D157B324000 lea edx, dword ptr [0040327B] :004010A4 52 push edx :004010A5 FF3520344000 push dword ptr [00403420] :004010AB 8D15DC174000 lea edx, dword ptr [004017DC] ; SetWindowTextA :004010B1 FFD2 call edxPhew! What a lot of code. But easy to understand when commented properly ;) The last part of this is just a MessageBox to say "Success!", and a SetWindowText to change the "UNREGISTERED!" to "Cracked by : BoomBox". Looking at the commented sections, we have 4 possible values of edx - 00, 01, 02, and 03. It then updates where we are on the grid, and checks if we are on a "*". Now, the grid has 10h lines to a row. So, if we take away 10h from our position, then we are moving up, get it? Likewise for +1, -10, and +10. Let's draw up a little chart: 00 -=> -10 -=> Up 01 -=> +1 -=> Right 02 -=> +10 -=> Down 03 -=> -1 -=> LeftRight, so let's now work out the correct sequence: D,D,D,R,D,D,D,L, D,D,R,R,U,R,U,U, R,R,R,U,U,L,L,L, U,L,U,U,R,R,R,R, R,D,R,R,U,R,R,D, R,R,R,D,D,L,L,D, L,L,U,L,L,D,D,D, L,D,D,R,R,R,U,U, R,R,R,R,D,D,L,LYou have no idea how annoying it is trying to work that out, so I save you the trouble :}. Count them up - 72d moves! Huh? The crackme only reads in 18d bytes....? Hang on... 72/18 = 4. Hmm. So, we have to find a way of combining 4 numbers into 1. Also, these 4 numbers can each only be 0,1,2, or 3. I then popped into HIEW again, and noticed a piece of text saying "Byte" in the scroll bar. I'd experimented with HIEW as soon as I got my paws on it, and knew that it allowed you to enter numbers in binary to form a single byte ... eight of them! A little explaination of binary: Binary ------ In normal counting, base 10, we have 10 numbers before we start another digit. In binary, we only have 2 numbers before we start another digit, IE 1 2 3 4 5 6 7 8 9 10 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 And so on.In binary, a 2-digit number can only have 4 values - 00, 01, 10, and 11. So, let's redraw the table, split up the movements into sections of 4, and solve the crackme: 00 -=> Up 01 -=> Right 10 -=> Down 11 -=> Left Correct sequence: DDDR = 10 10 10 01 = A9 DDDL = 10 10 10 11 = AB DDRR = 10 10 01 01 = A5 URUU = 00 01 00 00 = 10 RRRU = 01 01 01 00 = 54 ULLL = 00 11 11 11 = 3F ULUU = 00 11 00 00 = 30 RRRR = 01 01 01 01 = 55 RDRR = 01 10 01 01 = 65 URRD = 00 01 01 10 = 16 RRRD = 01 01 01 10 = 56 DLLD = 10 11 11 10 = BE LLUL = 11 11 00 11 = F3 LDDD = 11 10 10 10 = EA LDDR = 11 10 10 01 = E9 RRUU = 01 01 00 00 = 50 RRRR = 01 01 01 01 = 55 DDLL = 10 10 11 11 = AFWhich after all that gives you: A9 AB A5 10 54 3F 30 55 65 16 56 BE F3 EA E9 50 55 AFWhich is then XORed with the low byte of your name, giving me my keyfile as: 00000000: 07 42 6F 6F-6D 42 6F 78-1F 1D 13 A6-E2 89 86 E3 BoomBoxªÔëåÒ 00000010: D3 A0 E0 08-45 5C 5F E6-E3 19 - ËáÓE\_µÒ A Keygen... For A Keyfile?Now, let's try and write a KeyFile generator. Let's draw up a plan of what to do:* Get the user's name * Find how long the user's name is * Find the low byte of the total * XOR our key with it * Write the length byte to the file * Write the user's name to the file * Write the XORed key to the fileI'll write a keyfile generator when I can be bothered ;) The End!Thanks for listening to me waffle on. It should be noted that this is nowhere near the ideal way to solve the crackme, I should have been able to figure out everything from the code alone - but I'm just an un-skilled newbie ;). If you have anything to add, complaints, comments, additions, mail them to: BoomBox@FuckYou.co.uk. That's it! |