Intro
Another GenoCide crackme packed with UPX. So you just unpack it..... Anyway, it's another Delphi program swollen beyond all proportions :) and 90% of the code that is there is ignorable. It is full of bloat for class type initialisation and type and error checking and it's easy to get lost in a debugger tracing through seemingly endless calls to a library stringlength type function. After a while cracking Delphi apps you become familiar and learn to ignore it for the best part. Delphi however does leave some nice remnants in memory which enable you to start cracking Delphi apps faster :).
Target
The crackme is quite a nice little program when it is run. There are three stages and I will crack each one in detail below. The first stage asks you to set some spinners to a number, the second asks for two words and the third asks your name and has an array of buttons to press. Once you're used to Delphi apps it is pretty simple to crack this one.
Analysis - Stage1
OK, we are presented with four sliders and a number and a big red 'Invalid' at the side of the screen. The idea is to set the sliders to the correct number and change the 'Invalid' into 'Valid' before you can go on to the next screen. In the disassembly of the program I have just shown the mainly relevant parts of the code, in a more relevant order for me to talk about.
s__VALID: ; XREFS First: 1000:004347b3 Number : 1 1000:00434810 2056414c494400 ds " VALID" s_INVALID: 1000:00434820 494e56414c494400 ds "INVALID" ; XREFS First: |
Two interesting strings and just prior to this there is a typical Delphi routine with the relevant code, and of course the thing which really stands out in the code is the xor........ |
;****************************************************************** ; typical Delphi entry code before here ;****************************************************************** level1_value_is_here_xor: 1000:0043479c 3597250000 xor eax, 2597h 1000:004347a1 3d1a2b0000 cmp eax, 2b1ah 1000:004347a6 751e jnz 4347c6h ;****************************************************************** ; typical Delphi exitcode after here ;****************************************************************** |
Now this really does stand out like a sore thumb, and out of interest you xor the values together to get 0e8dh or 3725, which when set on the spinners lets us past the first stage. |
Analysis - Stage2
Well, stage 1 really wasnt too bad, so on to stage 2. If you look around after stage 1 you won't have to look too far before you reach the stage2 code. Anyway stage2 is asking for two strings and the lame thing to do now would probably be to look for strings in the program. I actually just paged through the program a bit and noticed the next interesting piece of code (just prior to some messages of the form 'Success', 'Good Work', 'Failure', 'Come on isnt it easy' type messages. There were in fact two pieces of code of interest (and two strings, now isnt that coincidence ? Hehe,
1000:00434a25 8a55f4 mov dl, [ebp-0ch] 1000:00434a28 84d2 test dl, dl 1000:00434a2a 7614 jbe 434a40h 1000:00434a2c 8d45f5 lea eax, [ebp-0bh] ; XREFS First: 1000:00434a3e Number : 1 1000:00434a2f 33c9 xor ecx, ecx 1000:00434a31 8a08 mov cl, [eax] 1000:00434a33 69c959010000 imul eax, ecx, 159h 1000:00434a39 03f1 add esi, ecx 1000:00434a3b 40 inc eax 1000:00434a3c feca dec dl 1000:00434a3e 75ef jnz 434a2fh ; XREFS First: 1000:00434a2a Number : 1 1000:00434a40 8bc6 mov eax, esi 1000:00434a42 35fe9e0c00 xor eax, c9efeh 1000:00434a47 3d90120900 cmp eax, 91290h 1000:00434a4c 7509 jnz 434a57h 1000:00434a4e c6056f77430001 mov byte ptr [43776fh], 01h 1000:00434a55 eb07 jmp 434a5eh ; XREFS First: 1000:00434a4c Number : 1 1000:00434a57 c6056f77430000 mov byte ptr [43776fh], 00h |
Now looking at this we have a loop (multiply each by 159h and add them all together) and a check at the end to value 0c9efeh xor 91290h which is 58c6eh or 363630 and 159h is 345 and then 363630/345 is 1054. So this just looks like a checksumming routine, the same as the next one. I immediately converted it to a string consisting of 9*105 + 109, or 'iiiiiiiiim' and entered it into the first box. |
1000:00434aec 84d2 test dl, dl 1000:00434aee 7614 jbe 434b04h 1000:00434af0 8d45f5 lea eax, [ebp-0bh] ; XREFS First: 1000:00434b02 Number : 1 1000:00434af3 33c9 xor ecx, ecx 1000:00434af5 8a08 mov cl, [eax] 1000:00434af7 69c991020000 imul eax, ecx, 291h 1000:00434afd 03f1 add esi, ecx 1000:00434aff 40 inc eax 1000:00434b00 feca dec dl 1000:00434b02 75ef jnz 434af3h ; XREFS First: 1000:00434aee Number : 1 1000:00434b04 8bc6 mov eax, esi 1000:00434b06 359e100f00 xor eax, f109eh 1000:00434b0b 3d105f0b00 cmp eax, b5f10h 1000:00434b10 7509 jnz 434b1bh 1000:00434b12 c6057077430001 mov byte ptr [437770h], 01h 1000:00434b19 eb07 jmp 434b22h ; XREFS First: 1000:00434b10 Number : 1 1000:00434b1b c6057077430000 mov byte ptr [437770h], 00h |
There's really not much more to say than the comment above since they are all but identical except the values used. This time (0f109eh xor 0b5f10h)/291h is 282510/657 or 430 exactly, which is 5*86 or 'VVVVV' which I entered into box 2. With both strings entered the check button says congratulations and we are on our way to stage3.
Before anyone asks I have no idea to what strings (the original 'strings') it all refers, but then thats what its all about isnt it ? You could always make some up, hours of fun ;) |
Analysis - Stage3
The final stage now, we are asked to enter a name and there are some buttons there. I could have written a keygen for this, but as you'll see it is hardly worth it. Again we have some interesting code (all in a line :))
1000:00434c77 b206 mov dl, 06h 1000:00434c79 b860774300 mov eax, offset 437760h ; XREFS First: 1000:00434c84 Number : 1 1000:00434c7e c60000 mov byte ptr [eax], 00h 1000:00434c81 40 inc eax 1000:00434c82 feca dec dl 1000:00434c84 75f8 jnz 434c7eh ;******************************************************************* ; boring gap ;******************************************************************* 1000:00434d4e b301 mov bl, 01h 1000:00434d50 be68774300 mov esi, offset 437768h ; XREFS First: 1000:00434d7f Number : 1 1000:00434d55 33c0 xor eax, eax 1000:00434d57 8ac3 mov al, bl 1000:00434d59 8b1558774300 mov edx, [437758h] 1000:00434d5f 8b5208 mov edx, [edx+08h] 1000:00434d62 0fb64402ff movzx eax, [edx-01h+eax] 1000:00434d67 b907000000 mov ecx, 07h 1000:00434d6c 99 cdq 1000:00434d6d f7f9 idiv ecx 1000:00434d6f 8bca mov ecx, edx 1000:00434d71 880e mov [esi], cl 1000:00434d73 84c9 test cl, cl 1000:00434d75 7503 jnz 434d7ah 1000:00434d77 c60603 mov byte ptr [esi], 03h ; XREFS First: 1000:00434d75 Number : 1 1000:00434d7a 43 inc ebx 1000:00434d7b 46 inc esi 1000:00434d7c 80fb07 cmp bl, 07h 1000:00434d7f 75d4 jnz 434d55h |
Now in stage 3 we will be concerned with 2 main areas. The area at 437760h, and that at 437768h. You will see below that these are compared to each other and we pass the tests if they are the same. If they are unequal then we fail :( I have shown the initialisation of 437760 in the code above, and you'll see a lot of messing with nearby offsets around here as well. I decided to trace breakpoint some judicious spots with SoftIce in this rather than my usual disassembler analysis. We see the nice little routine above, breakpointed nicely for me and my entered string of 'CRONOS' was soon divided byte by ascii byte by 7 and a small array of the numbers 4,5,2,1,2,6 was soon produced from the remainders (notice the treatment of exact divides which are changed into 3's though).
The next breakpoints were placed in the routine below to see what was happening down there :) |
1000:00434db5 b001 mov al, 01h 1000:00434db7 ba60774300 mov edx, offset 437760h 1000:00434dbc b968774300 mov ecx, offset 437768h ; XREFS First: 1000:00434ddc Number : 1 1000:00434dc1 8a1a mov bl, [edx] 1000:00434dc3 3a19 cmp bl, [ecx] 1000:00434dc5 7409 jz 434dd0h 1000:00434dc7 c6057377430000 mov byte ptr [437773h], 00h 1000:00434dce eb0e jmp 434ddeh ; XREFS First: 1000:00434dc5 Number : 1 1000:00434dd0 c6057377430001 mov byte ptr [437773h], 01h 1000:00434dd7 40 inc eax 1000:00434dd8 41 inc ecx 1000:00434dd9 42 inc edx 1000:00434dda 3c07 cmp al, 07h 1000:00434ddc 75e3 jnz 434dc1h ; XREFS First: 1000:00434dce Number : 1 1000:00434dde 803d7377430001 cmp byte ptr [437773h], 01h 1000:00434de5 7513 jnz 434dfah 1000:00434de7 6a00 push 00h 1000:00434de9 680c4e4300 push offset s_Bravo_cracker_ 1000:00434dee 681c4e4300 push offset s_Hey__man__You_re_the_best____You_ve_ 1000:00434df3 6a00 push 00h 1000:00434df5 e8d609fdff call _MessageBoxA |
This is the final compare routine, and I soon noticed that the (name mod 7) string produced above is compared to a set of numbers from the buttons which you can press (press all six in sequence and you have 1,2,3,4,5,6. So the challenge is simply to press the buttons in a sequence the same as your name mod 7 in ascii!! ah, so 4,5,6,1,2,6 for CRONOS with the buttons numbered 1 to 6 from left to right. And that is the the third challenge completed. We are done. |
To summarise the stages and give the **exact** way of getting through it all:
stage1: set the splitters to 3725 and the message should change to VALID. Press 'Next Level->' and another tab appears.
stage2:In the first box enter 'iiiiiiiiim' (without the quotes, that is 9 i's and 1 m). In the second box enter 'VVVVV' without the quotes. Press check and get the 'Good Work' message. Press 'Next Level->' and another tab appears.
stage3:type CRONOS in the box and press the start button. Now hit the following buttons in order: button 4,5,2,1,2,6 counting from left to right. (which should be ?,no entry, arrows, X,arrows,tick). A final message should pop up.
Conclusions
This crackme wasnt really that hard, the hardest part for most people will probably be understanding how Delphi compiles programs. It was an interesting crackme though with some nice, and original ideas and I thoroughly enjoyed cracking it :)
{Cronos}