GenoCide Crackme 7 [Gandalf]

by

{Cronos}

Intro

GenoCide seem to like packing their crackmes with UPX it appears, I can't think why personally. Why do they want to hide the code ? So you just unpack it..... Anyway, it's a 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 asks for a name and serial. If you look through the code and used purely a Soft-Ice approach to the program then you could fall into a nice trap set by the programmer which involves an entirely false piece of 'serial checking code' which even generates a string in memory that you might think is the right serial.

Analysis

The approach I took, as usual with these crackmes was to disassemble and look back through the program, find some interesting messages and some interesting routines and track back from there, occasionally using Soft-Ice to verify conjectures and check things and to gain a broader understanding of subroutines. Anyway, I have shown the main routine in the disassembly, which includes our success messages at the bottom. The routine is actually difficult to follow in that the serial itself is used in checking the serial. This can lead to names for which there is no serial, and I will look at a shorter analysis method and some shortcomings of the serial routine after the main attack.
;****************************************************************
; all the standard Delphi entry code is here.........
;****************************************************************
;                                                 XREFS First: 1000:00429b75 Number : 1
1000:004298c8 55                   push   ebp
1000:004298c9 8bec                 mov    ebp, esp
1000:004298cb 81c4ecfeffff         add    esp, fffffeech
1000:004298d1 53                   push   ebx
1000:004298d2 56                   push   esi
1000:004298d3 57                   push   edi
1000:004298d4 33c0                 xor    eax, eax
1000:004298d6 8985ecfeffff         mov    [ebp-114h], eax
1000:004298dc 8945f0               mov    [ebp-10h], eax
1000:004298df 33c0                 xor    eax, eax
1000:004298e1 55                   push   ebp
1000:004298e2 68f99a4200           push   429af9h
1000:004298e7 64ff30               push   dword ptr fs:[eax]
1000:004298ea 648920               mov    fs:[eax], esp
1000:004298ed 33db                 xor    ebx, ebx
1000:004298ef c645f500             mov    byte ptr [ebp-0bh], 00h
1000:004298f3 a130b74200           mov    eax, [42b730h]
1000:004298f8 8b80f8010000         mov    eax, [eax+1f8h]
1000:004298fe 33d2                 xor    edx, edx
1000:00429900 e8d3f8ffff           call   4291d8h
1000:00429905 a130b74200           mov    eax, [42b730h]
1000:0042990a 8b80f8010000         mov    eax, [eax+1f8h]
1000:00429910 e8dbf6ffff           call   428ff0h
1000:00429915 8845ff               mov    [ebp-01h], al
1000:00429918 8d55f0               lea    edx, [ebp-10h]
1000:0042991b a130b74200           mov    eax, [42b730h]
1000:00429920 8b80e0010000         mov    eax, [eax+1e0h]
1000:00429926 e8adc4feff           call   415dd8h
1000:0042992b 8b55f0               mov    edx, [ebp-10h]
1000:0042992e a130b74200           mov    eax, [42b730h]
1000:00429933 8b08                 mov    ecx, [eax]
1000:00429935 ff5118               call   dword ptr [ecx+18h]
; these three instructions are standard Delphi 'access string pointer' code
1000:00429938 8d55f0               lea    edx, [ebp-10h]
1000:0042993b a130b74200           mov    eax, [42b730h]
1000:00429940 8b80e8010000         mov    eax, [eax+1e8h]
1000:00429946 e88dc4feff           call   415dd8h
1000:0042994b 8b55f0               mov    edx, [ebp-10h]
1000:0042994e 8d85f0feffff         lea    eax, [ebp-110h]
1000:00429954 b9ff000000           mov    ecx, ffh
1000:00429959 e8269efdff           call   403784h
1000:0042995e 8d95f0feffff         lea    edx, [ebp-110h]
1000:00429964 8d45f6               lea    eax, [ebp-0ah]
1000:00429967 b108                 mov    cl, 08h
1000:00429969 e8928dfdff           call   402700h
1000:0042996e 8a45ff               mov    al, [ebp-01h]
1000:00429971 84c0                 test   al, al
1000:00429973 0f8234010000         jc     429aadh
;****************************************************************
; here we start to get serious.......
;****************************************************************
1000:00429979 40                   inc    eax
1000:0042997a 8845f4               mov    [ebp-0ch], al		; set up loop
;                                                 XREFS First: 1000:00429aa7 Number : 1
1000:0042997d 33c0                 xor    eax, eax
1000:0042997f 8ac3                 mov    al, bl		; bl is an index into our serial
;****************************************************************
; next follows some code which uses one char and generates four numbers based on :-
;   key[1]=char xor 3d7;
;   key[3]=char xor 144;
;   key[2]=char xor 2a3;
;   key[0]=char xor ae;
;****************************************************************
1000:00429981 8b1530b74200         mov    edx, [42b730h]
1000:00429987 8b5208               mov    edx, [edx+08h]
1000:0042998a 8a5402ff             mov    dl, [edx-01h+eax]
1000:0042998e 33c0                 xor    eax, eax
1000:00429990 8ac2                 mov    al, dl
1000:00429992 8bc8                 mov    ecx, eax
1000:00429994 6681f1d703           xor    cx, 3d7h
1000:00429999 0fb7c9               movzx  ecx, cx
1000:0042999c 890d38b74200         mov    [42b738h], ecx	; key[1] done
1000:004299a2 33c9                 xor    ecx, ecx
1000:004299a4 8acb                 mov    cl, bl
1000:004299a6 8b3530b74200         mov    esi, [42b730h]
1000:004299ac 8b7608               mov    esi, [esi+08h]
1000:004299af 8bc8                 mov    ecx, eax
1000:004299b1 6681f14401           xor    cx, 144h
1000:004299b6 0fb7c9               movzx  ecx, cx
1000:004299b9 890d40b74200         mov    [42b740h], ecx	; key[3] done
1000:004299bf 33c9                 xor    ecx, ecx
1000:004299c1 8acb                 mov    cl, bl
1000:004299c3 8b3530b74200         mov    esi, [42b730h]
1000:004299c9 8b7608               mov    esi, [esi+08h]
1000:004299cc 6635a302             xor    ax, 2a3h
1000:004299d0 0fb7c0               movzx  eax, ax
1000:004299d3 a33cb74200           mov    [42b73ch], eax	; key[2] done
1000:004299d8 33c0                 xor    eax, eax
1000:004299da 8ac3                 mov    al, bl
1000:004299dc 8b0d30b74200         mov    ecx, [42b730h]
1000:004299e2 8b4908               mov    ecx, [ecx+08h]
1000:004299e5 80f2ae               xor    dl, aeh
1000:004299e8 33c0                 xor    eax, eax
1000:004299ea 8ac2                 mov    al, dl
1000:004299ec a334b74200           mov    [42b734h], eax	; key[0] done
;****************************************************************
; now we divide each by 10 continually until they are all
; less than 10.
;****************************************************************
1000:004299f1 b301                 mov    bl, 01h
1000:004299f3 bf34b74200           mov    edi, offset 42b734h
;                                                 XREFS First: 1000:00429a1f Number : 2
1000:004299f8 8b07                 mov    eax, [edi]
1000:004299fa b90a000000           mov    ecx, 0ah
1000:004299ff 99                   cdq    
1000:00429a00 f7f9                 idiv   ecx
1000:00429a02 8bf0                 mov    esi, eax
1000:00429a04 8937                 mov    [edi], esi
1000:00429a06 8d95ecfeffff         lea    edx, [ebp-114h]
1000:00429a0c 8bc6                 mov    eax, esi
1000:00429a0e e8d1cafdff           call   4064e4h
1000:00429a13 8b85ecfeffff         mov    eax, [ebp-114h]
1000:00429a19 e88a9dfdff           call   4037a8h
1000:00429a1e 48                   dec    eax
1000:00429a1f 75d7                 jnz    4299f8h
1000:00429a21 43                   inc    ebx
1000:00429a22 83c704               add    edi, 04h
1000:00429a25 80fb05               cmp    bl, 05h
1000:00429a28 75ce                 jnz    4299f8h
;****************************************************************
; now just check the serial is in an appropriate form for comparison
;****************************************************************
1000:00429a2a 8a5df6               mov    bl, [ebp-0ah]
1000:00429a2d 84db                 test   bl, bl
1000:00429a2f 762a                 jbe    429a5bh
1000:00429a31 8d7df7               lea    edi, [ebp-09h]
1000:00429a34 be54b74200           mov    esi, 42b754h
;                                                 XREFS First: 1000:00429a59 Number : 1
1000:00429a39 8d85ecfeffff         lea    eax, [ebp-114h]
1000:00429a3f 8a17                 mov    dl, [edi]
1000:00429a41 e88a9cfdff           call   4036d0h
1000:00429a46 8b85ecfeffff         mov    eax, [ebp-114h]
1000:00429a4c e8c3cafdff           call   406514h
1000:00429a51 8906                 mov    [esi], eax
1000:00429a53 83c604               add    esi, 04h
1000:00429a56 47                   inc    edi
1000:00429a57 fecb                 dec    bl
1000:00429a59 75de                 jnz    429a39h
;                                                 XREFS First: 1000:00429a2f Number : 1
;****************************************************************
; finally compare the two digit by digit
;****************************************************************
1000:00429a5b b301                 mov    bl, 01h
1000:00429a5d b834b74200           mov    eax, offset 42b734h
1000:00429a62 ba54b74200           mov    edx, offset 42b754h
;                                                 XREFS First: 1000:00429a81 Number : 1
1000:00429a67 8b08                 mov    ecx, [eax]
1000:00429a69 3b0a                 cmp    ecx, [edx]
1000:00429a6b 7406                 jz     429a73h
;****************************************************************
; note that if the comparison fails then we leave the loop early
; the point at which it failed is used as an index for the next
; char for generating a new number to check.......and so on and so on
; for a total of loop times (which is quite high......)
;****************************************************************
1000:00429a6d c645f500             mov    byte ptr [ebp-0bh], 00h
1000:00429a71 eb10                 jmp    429a83h
;                                                 XREFS First: 1000:00429a6b Number : 1
1000:00429a73 c645f501             mov    byte ptr [ebp-0bh], 01h
1000:00429a77 43                   inc    ebx
1000:00429a78 83c204               add    edx, 04h
1000:00429a7b 83c004               add    eax, 04h
1000:00429a7e 80fb05               cmp    bl, 05h
1000:00429a81 75e4                 jnz    429a67h
;                                                 XREFS First: 1000:00429a71 Number : 1
1000:00429a83 a130b74200           mov    eax, [42b730h]
1000:00429a88 8bb0f8010000         mov    esi, [eax+1f8h]
1000:00429a8e 8bc6                 mov    eax, esi
1000:00429a90 e893f5ffff           call   429028h
1000:00429a95 8bd0                 mov    edx, eax
1000:00429a97 42                   inc    edx
1000:00429a98 8bc6                 mov    eax, esi
1000:00429a9a e839f7ffff           call   4291d8h
1000:00429a9f e86cfbffff           call   429610h
;****************************************************************
; finally it continues for quite a while
;****************************************************************
1000:00429aa4 fe4df4               dec    byte ptr [ebp-0ch]
1000:00429aa7 0f85d0feffff         jnz    42997dh
;****************************************************************
; until we are fed up and decide it was the last time
;****************************************************************
;                                                 XREFS First: 1000:00429973 Number : 1
1000:00429aad 807df501             cmp    byte ptr [ebp-0bh], 01h
1000:00429ab1 7513                 jnz    429ac6h
1000:00429ab3 6a00                 push   00h
1000:00429ab5 68089b4200           push   offset s_Success
1000:00429aba 68109b4200           push   offset s_You_ve_found_right_serial_
1000:00429abf 6a00                 push   00h
1000:00429ac1 e83abafdff           call   _MessageBoxA
;                                                 XREFS First: 1000:00429ab1 Number : 1
;****************************************************************
; and the rest is boring Delphi exit code ;)
;****************************************************************
1000:00429ac6 a130b74200           mov    eax, [42b730h]
1000:00429acb 8b80f8010000         mov    eax, [eax+1f8h]
1000:00429ad1 33d2                 xor    edx, edx
1000:00429ad3 e800f7ffff           call   4291d8h
1000:00429ad8 33c0                 xor    eax, eax
1000:00429ada 5a                   pop    edx
1000:00429adb 59                   pop    ecx
1000:00429adc 59                   pop    ecx
1000:00429add 648910               mov    fs:[eax], edx
1000:00429ae0 68009b4200           push   429b00h
;                                                 XREFS First: 1000:00429afe Number : 1
1000:00429ae5 8d85ecfeffff         lea    eax, [ebp-114h]
1000:00429aeb e83c9afdff           call   40352ch
1000:00429af0 8d45f0               lea    eax, [ebp-10h]
1000:00429af3 e8349afdff           call   40352ch
1000:00429af8 c3                   ret    
1000:00429af9 e9d294fdff           jmp    402fd0h
1000:00429afe ebe5                 jmp    429ae5h
1000:00429b00 5f                   pop    edi
1000:00429b01 5e                   pop    esi
1000:00429b02 5b                   pop    ebx
1000:00429b03 8be5                 mov    esp, ebp
1000:00429b05 5d                   pop    ebp
1000:00429b06 c3                   ret    
1000:00429b07 00                   db     00
s_Success:
;                                                 XREFS First: 1000:00429ab5 Number : 1
1000:00429b08 5375636365737300     ds     "Success"
s_You_ve_found_right_serial_:
;                                                 XREFS First: 1000:00429aba Number : 1
1000:00429b10 596f7527766520666f.. ds     "You've found right serial!"
I have indicated in the comments the main algorithm in use. Basically we start at the beginning of the name string and generate a number from the char we are looking at. We then compare this to the serial and the point at which it fails determines the char for the next generation. After a number of loops it all ends and we take the last check as final.......

Now its difficult to see how you can generate a serial for a given name from this, but I will give three ways, one the first method I used and the other two methods I thought of later. Method 1 involved breakpointing with SoftIce as the serial was generated. I entered 'Cronos' as the name and '1234' as the code. First loop, 1963 was generated. So I entered that as the code, but now later in the loop '1972' was generated and so I entered that as the code and started again. This soon popped the message up 'You've found the right serial!'. So that was little work with Soft-Ice. Method 2 is to take the first five characters of your nick and generate the code for each. Then try them all....... Method 3 is based on an analysis of the formulae. You will see that the formulae are quite limited and key[1] for example is between 300h and 3ffh. This means the character is either 7,8,9 or 1. In fact the possibilities are 0-2 for key[0], 5-7 for key[2] and 2-5 for key[3]. Now we can limit this further by checking only lowercase possibilities. Since the loop must end on one of the characters 1-5 of your name, if we input a lowercase name only then we have reduced the possibilities to 1 or 2 for key[0], 8 or 9 for key[1], 7 for key[2] and 2 or 3 for key[3]. Hence there are only 8 possible serial numbers for a lowercase name. These are 1872, 2872, 1972, 2972, 1873, 2873, 1973 and 2973. It doesnt take long to check them all.

Now there are some other points to note in the serial routine. Firstly if we do succeed then we are likely to reach a stable point on character 5. With 'Cronos' we end up at character 5 since we match the serial at one point, and so the next character used for the serial generation in the loop is character 4+1=character 5..... and so we do not move from there. Other possibilities include moving back and forth between two characters, and this can lead to failure of the serial generation (for example using character 2 and matching 3 chars so using character 4 and matching 1, so using character 2........etc etc). This can mean that although you can generate 5 possible codes from the names characters none will work because the last one generated is different in each case........

And so there you have it, name:Cronos, serial:1972.

Conclusions

This crackme was quite hard, although the hardest part for most people will probably be understanding how Delphi compiles programs. The serial checking routine was interesting although limited in the number of valid codes and although some names may not lead to valid serials there are many ways to analyse the routine.

{Cronos}