GenoCide Crackme 11 [Gandalf]

by

{Cronos}

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}