Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Friday, April 03 2020 @ 07:17 AM CEST

Keygenning a serialroutine which uses CRC32

   

TutorialsLevel : intermediate

Keygenning an application that uses a customized CRC32 algorithm to create a serial for a name + companyname.

TARGET = FireGraphicXP v4.0
TOOLS USED = Softice / IDA
Intro
We start the program, and a registerbox pops up :) The Check button is disabled though, so we can suspect the serial is checked 'on the fly' as we enter it in the editboxes.

Fill the name, company, and serial field with your data, but leave last digit of serial open. Now, put a breakpoint on hmemcpy, or getwindowtext. And enter the final digit. It breaks!
After a bit of tracing in softice, we notice that we land right in the checking routine that keeps looping as long that the registration dialogbox is on.
This is the code we land in :

The code
.text:00414440 checkforenable  proc near
.text:00414440                 push    ebx
.text:00414441                 push    edi
.text:00414442                 push    63h
.text:00414444                 push    1
.text:00414446                 push    1
.text:00414448                 lea     edi, [esi+20h]  ; Name
.text:0041444B                 push    64h
.text:0041444D                 push    43Ch
.text:00414452                 mov     ebx, edi        ; ebx = Name
.text:00414454                 mov     eax, edi
.text:00414456                 call    check_inputlength_routine ; Test length

.text:0041445B                 test    eax, eax
.text:0041445D                 jz      skiplengthchecks
.text:00414463                 push    63h
.text:00414465                 push    1
.text:00414467                 push    1
.text:00414469                 push    64h
.text:0041446B                 lea     ebx, [esi+84h]  ; Company Name
.text:00414471                 push    43Dh
.text:00414476                 mov     eax, edi        ; Name
.text:00414478                 call    check_inputlength_routine
.text:0041447D                 test    eax, eax
.text:0041447F                 jz      skiplengthchecks
.text:00414485                 push    4
.text:00414487                 push    1
.text:00414489                 push    1
.text:0041448B                 push    5
.text:0041448D                 lea     ebx, [esi+0E8h] ; Serial part 1
.text:00414493                 push    43Eh
.text:00414498                 mov     eax, edi
.text:0041449A                 call    check_inputlength_routine
.text:0041449F                 test    eax, eax
.text:004144A1                 jz      skiplengthchecks
.text:004144A7                 push    4
.text:004144A9                 push    1
.text:004144AB                 push    1
.text:004144AD                 push    5
.text:004144AF                 lea     ebx, [esi+0EDh] ; serial part 2
.text:004144B5                 push    4ADh
.text:004144BA                 mov     eax, edi
.text:004144BC                 call    check_inputlength_routine
.text:004144C1                 test    eax, eax
.text:004144C3                 jz      short skiplengthchecks
.text:004144C5                 push    4
.text:004144C7                 push    1
.text:004144C9                 push    1
.text:004144CB                 push    5
.text:004144CD                 lea     ebx, [esi+0F2h] ; serial part 3
.text:004144D3                 push    4AEh
.text:004144D8                 mov     eax, edi
.text:004144DA                 call    check_inputlength_routine
.text:004144DF                 test    eax, eax
.text:004144E1                 jz      short skiplengthchecks
.text:004144E3                 push    4
.text:004144E5                 push    1
.text:004144E7                 push    1
.text:004144E9                 push    5
.text:004144EB                 lea     ebx, [esi+0F7h] ; serial part 4

.text:004144F1                 push    4AFh
.text:004144F6                 mov     eax, edi
.text:004144F8                 call    check_inputlength_routine
.text:004144FD                 test    eax, eax
.text:004144FF                 jz      short skiplengthchecks
.text:00414501                 push    4
.text:00414503                 push    1
.text:00414505                 push    1
.text:00414507                 push    5
.text:00414509                 lea     ebx, [esi+0FCh] ; serial part 5
.text:0041450F                 push    4B0h
.text:00414514                 mov     eax, edi
.text:00414516                 call    check_inputlength_routine
.text:0041451B                 test    eax, eax
.text:0041451D                 jz      short skiplengthchecks
.text:0041451F                 push    4
.text:00414521                 push    1
.text:00414523                 push    1
.text:00414525                 push    5
.text:00414527                 lea     ebx, [esi+101h] ; serial part 6
.text:0041452D                 push    4B1h
.text:00414532                 mov     eax, edi
.text:00414534                 call    check_inputlength_routine
.text:00414539
.text:00414539 skiplengthchecks:                       ; CODE XREF: checkforenable+1Dj
.text:00414539                                         ; checkforenable+3Fj ...
.text:00414539                 lea     eax, [esi+101h] ; serial part 6
.text:0041453F                 push    eax
.text:00414540                 lea     ecx, [esi+0FCh] ; serial part 5

.text:00414546                 push    ecx
.text:00414547                 lea     edx, [esi+0F7h] ; serial part 4
.text:0041454D                 push    edx
.text:0041454E                 lea     eax, [esi+0F2h] ; serial part 3
.text:00414554                 push    eax
.text:00414555                 lea     ecx, [esi+0EDh] ; serial part 2
.text:0041455B                 push    ecx
.text:0041455C                 lea     edx, [esi+0E8h] ; serial part 1
.text:00414562                 push    edx
.text:00414563                 lea     ecx, [esi+84h]  ; company name
.text:00414569                 push    offset byte_0_525B08
.text:0041456E                 mov     edx, edi        ; edx = Name

.text:00414570                 call    check_serial    ; eax == 1 if serial OK
.text:00414575                 neg     al
.text:00414577                 push    1               ; nIDDlgItem
.text:00414579                 sbb     eax, eax
.text:0041457B                 neg     eax
.text:0041457D                 mov     edi, eax
.text:0041457F                 mov     eax, [esi+4]
.text:00414582                 push    eax             ; hDlg
.text:00414583                 call    ds:GetDlgItem
.text:00414589                 push    edi             ; bEnable (depends on check_serial call))
.text:0041458A                 push    eax             ; hWnd
.text:0041458B                 call    ds:EnableWindow ; Enable/disable the register button

.text:00414591                 pop     edi
.text:00414592                 xor     eax, eax
.text:00414594                 pop     ebx
.text:00414595                 retn    10h
.text:00414595 checkforenable  endp
Basically it checks if the serial is complete, and then the call check_serial is executed. If we trace over it, we see that it returns 0 for our fake serial. It is pretty clear that the serial routine is in that routine :)

Ok, let's take a look inside that routine :

.text:00446D70                 sub     esp, 120h
.text:00446D76                 mov     eax, dword_0_5232D0
.text:00446D7B                 xor     eax, [esp+120h]
.text:00446D82                 push    esi
.text:00446D83                 push    edi
.text:00446D84                 mov     esi, edx
.text:00446D86                 push    offset unk_0_5188AB ; lpString2
.text:00446D8B                 push    esi             ; lpString1
.text:00446D8C                 mov     [esp+130h+var_4], eax
.text:00446D93                 mov     edi, ecx
.text:00446D95                 call    ds:lstrcmpA
.text:00446D9B                 test    eax, eax
.text:00446D9D                 jnz     short go_on
.text:00446D9F
.text:00446D9F serial_not_complete:
.text:00446D9F
.text:00446D9F                 xor     al, al
.text:00446DA1                 jmp     skipsome
.text:00446DA6 ; ---------------------------------------------------------------------------
.text:00446DA6
.text:00446DA6 go_on:                                  ; CODE XREF: check_serial+2Dj
.text:00446DA6                 mov     eax, [esp+128h+arg_18] ; serial part 6
.text:00446DAD                 mov     ecx, [esp+128h+arg_14] ; serial part 5

.text:00446DB4                 mov     edx, [esp+128h+arg_10] ; serial part 4
.text:00446DBB                 push    eax
.text:00446DBC                 mov     eax, [esp+12Ch+arg_C] ; serial part 3
.text:00446DC3                 push    ecx
.text:00446DC4                 mov     ecx, [esp+130h+arg_8] ; serial part 2
.text:00446DCB                 push    edx
.text:00446DCC                 mov     edx, [esp+134h+arg_4] ; serial part 1
.text:00446DD3                 push    eax
.text:00446DD4                 push    ecx
.text:00446DD5                 push    edx
.text:00446DD6                 lea     eax, [esp+140h+sz]
.text:00446DDA                 push    offset aSSSSSS  ; "%s%s%s%s%s%s"
.text:00446DDF                 push    eax
.text:00446DE0                 call    paste_routine   ; paste serial together

What happened so far? All the serial parts are pasted together as a string. There is no need to trace that routine, it just pastes together whatever is pushed right before it. (Nobody stops you from tracing in that paste routine though :p)
.text:00446DE5                 add     esp, 20h
.text:00446DE8                 lea     ecx, [esp+128h+sz] ; serial
.text:00446DEC                 push    ecx             ; lpString (serial)
.text:00446DED                 call    ds:lstrlenA
.text:00446DF3                 cmp     eax, 18h        ; serial length == 24 ?
.text:00446DF6                 jnz     short serial_not_complete
.text:00446DF8                 lea     edx, [esp+128h+sz] ; edx = Serial
.text:00446DFC                 push    edx             ; lpsz

.text:00446DFD                 call    ds:CharUpperA
.text:00446E03                 test    edi, edi        ; company name
.text:00446E05                 mov     eax, edi
.text:00446E07                 jnz     short start_hashing
.text:00446E09                 mov     eax, offset unk_0_5188AB
the string built from the serialparts is converted to uppercase
.text:00446E0E
.text:00446E0E start_hashing:                          ; CODE XREF: check_serial+97j
.text:00446E0E                 push    241D30Bh        ; 0037868299
.text:00446E13                 push    eax             ; company name

.text:00446E14                 push    esi             ; Name
.text:00446E15                 lea     eax, [esp+134h+var_104]
.text:00446E19                 push    offset aSSD     ; "%s%s%d"
.text:00446E1E                 push    eax
.text:00446E1F                 call    paste_routine   ; paste name + company name + digits
.text:00446E24                 lea     eax, [esp+13Ch+var_104] ; eax = paste result userdata
.text:00446E28                 call    Create_hash_table
Here the paste routine is called again, this time with a fixed number, company name, and name they are pasted all after eachother. From now on I call this string 'userdata'
Then this string is used as parameter in the nex call, in that call a CRC32 hashing table is built, and the userdata is hashed with that table. We will take a look at this call in a minute. ;)
You can also notice the "%s%s%d" string, which is pretty simular to the strings u need for wsprintf API. Check it out in your win32api.hlp if u like ;)
.text:00446E2D                 push    eax             ; CRC32 hash 1
.text:00446E2E                 lea     ecx, [esp+140h+var_104]
.text:00446E32                 push    offset a08x     ; "%08X"
.text:00446E37                 push    ecx             ; userdata
.text:00446E38                 call    paste_routine   ; overwrite userdata with CRC32 Hash 1 (in ascii)
.text:00446E3D                 add     esp, 20h
.text:00446E40                 mov     ecx, 2          ; counter = 2

.text:00446E45                 lea     edi, [esp+128h+var_104] ; userdata
.text:00446E49                 lea     esi, [esp+128h+sz] ; serial part 1 & 2
.text:00446E4D                 xor     edx, edx
.text:00446E4F                 repe cmpsd              ; CRC32 hash 1 == serial digit 1 - 8   (2 dwords) 
.text:00446E51                 jnz     serial_not_complete
next the hash created in the create_hash_table routine( which is returned in eax), is pushed and converted to an ascii string. this means we have converted one DWORD to an 8 digits long hex number in ascii, so 8 bytes long.
Next there is a compare done of 2 DWORDS (8bytes) between serialpart1&2 and our hash (created from the userdata).
.text:00446E57                 mov     eax, [esp+128h+var_118] ; lpSerial part 3 
.text:00446E5B                 mov     ecx, [esp+128h+var_114] ; lpSerial part 4 
.text:00446E5F                 mov     [esp+128h+var_104], eax ; overwrite dword CRC32 hash 1 with serial part 3
.text:00446E63                 lea     eax, [esp+128h+var_104]
.text:00446E67                 mov     [esp+128h+var_100], ecx ; overwrite 2nd dword CRC32 hash 1 with serial part 4
.text:00446E6B                 call    Create_hash_table
.text:00446E70                 push    eax             ; CRC32 Hash 2
.text:00446E71                 lea     edx, [esp+12Ch+var_104] ; edx = userdata

.text:00446E75                 push    offset a08x     ; "%08X"
.text:00446E7A                 push    edx
.text:00446E7B                 call    paste_routine   ; overwrite userdata with CRC32 Hash 2 (in ascii) 
.text:00446E80                 add     esp, 0Ch
.text:00446E83                 xor     eax, eax
.text:00446E85                 mov     ecx, 2
.text:00446E8A                 lea     edi, [esp+128h+var_104] ; userdata   
.text:00446E8E                 lea     esi, [esp+128h+var_110] ; serial part 6 & 7 
.text:00446E92                 repe cmpsd              ; CRC32 hash 2 == serial digit 16 - 24 (2 dwords)  
.text:00446E94                 setz    al

Same thing happens here, only now the 3rd and 4th part of the serial are used to create a hash. and it is checked with part 5 & 6 of our serial. So it seems part 3,4,5 and 6 don't depend on our data...kinda strange :/ When this check is also correct al is set to 1
.text:00446E97
.text:00446E97 skipsome:                               ; CODE XREF: check_serial+31j
.text:00446E97                 mov     ecx, [esp+128h+var_4]
.text:00446E9E                 xor     ecx, [esp+128h]
.text:00446EA5                 pop     edi             ; Name
.text:00446EA6                 pop     esi
.text:00446EA7                 call    sub_0_4D6F4A    ; internal appcheck 
.text:00446EAC                 add     esp, 120h
.text:00446EB2                 retn    1Ch
.text:00446EB2 check_serial    endp

So this function calculates serial part 1 & 2 out of our userdata, and calculate serial part 5 & 6 out of serial part 3 & 4. It returns 1 if serial was ok, else 0.

Let's have a closer look at the Create_hash_table routine :
.text:004036F0 Create_hash_table proc near
.text:004036F0
.text:004036F0
.text:004036F0 var_400         = dword ptr -400h
.text:004036F0
.text:004036F0                 sub     esp, 400h
.text:004036F6                 push    ebx
.text:004036F7                 push    esi
.text:004036F8                 push    edi
.text:004036F9                 mov     edi, eax        ; userdata
.text:004036FB                 xor     ebx, ebx
.text:004036FD                 lea     ecx, [ecx+0]
.text:00403700
.text:00403700 hash_table_loop:                        ; CODE XREF: Create_hash_table+137j
.text:00403700                 xor     ecx, ecx
.text:00403702                 test    bl, 1
.text:00403705                 jz      short loc_0_40370C
.text:00403707                 mov     ecx, 80h
.text:0040370C
.text:0040370C loc_0_40370C:                           ; CODE XREF: Create_hash_table+15j
.text:0040370C                 mov     eax, ebx
.text:0040370E                 shr     eax, 1
.text:00403710                 test    al, 1
.text:00403712                 jz      short loc_0_403717
.text:00403714                 or      ecx, 40h
.text:00403717
.text:00403717 loc_0_403717:                           ; CODE XREF: Create_hash_table+22j
.text:00403717                 shr     eax, 1
.text:00403719                 test    al, 1
.text:0040371B                 jz      short loc_0_403720
.text:0040371D                 or      ecx, 20h
.text:00403720
.text:00403720 loc_0_403720:                           ; CODE XREF: Create_hash_table+2Bj
.text:00403720                 shr     eax, 1
.text:00403722                 test    al, 1
.text:00403724                 jz      short loc_0_403729
.text:00403726                 or      ecx, 10h
.text:00403729
.text:00403729 loc_0_403729:                           ; CODE XREF: Create_hash_table+34j
.text:00403729                 shr     eax, 1
.text:0040372B                 test    al, 1
.text:0040372D                 jz      short loc_0_403732
.text:0040372F                 or      ecx, 8
.text:00403732
.text:00403732 loc_0_403732:                           ; CODE XREF: Create_hash_table+3Dj
.text:00403732                 shr     eax, 1
.text:00403734                 test    al, 1
.text:00403736                 jz      short loc_0_40373B
.text:00403738                 or      ecx, 4
.text:0040373B
.text:0040373B loc_0_40373B:                           ; CODE XREF: Create_hash_table+46j
.text:0040373B                 shr     eax, 1
.text:0040373D                 test    al, 1
.text:0040373F                 jz      short loc_0_403744
.text:00403741                 or      ecx, 2
.text:00403744
.text:00403744 loc_0_403744:                           ; CODE XREF: Create_hash_table+4Fj
.text:00403744                 test    al, 2
.text:00403746                 jz      short loc_0_40374B
.text:00403748                 or      ecx, 1
.text:0040374B
.text:0040374B loc_0_40374B:                           ; CODE XREF: Create_hash_table+56j
.text:0040374B                 shl     ecx, 18h
.text:0040374E                 mov     eax, ecx
.text:00403750                 and     eax, 80000000h
.text:00403755                 neg     eax
.text:00403757                 sbb     eax, eax
.text:00403759                 add     ecx, ecx
.text:0040375B                 and     eax, 4C11DB7h
.text:00403760                 xor     eax, ecx
.text:00403762                 mov     ecx, eax
.text:00403764                 and     ecx, 80000000h
.text:0040376A                 neg     ecx
.text:0040376C                 sbb     ecx, ecx
.text:0040376E                 and     ecx, 4C11DB7h
.text:00403774                 lea     edx, [eax+eax]
.text:00403777                 xor     ecx, edx
.text:00403779                 mov     eax, ecx
.text:0040377B                 and     eax, 80000000h
.text:00403780                 neg     eax
.text:00403782                 sbb     eax, eax
.text:00403784                 add     ecx, ecx
.text:00403786                 and     eax, 4C11DB7h
.text:0040378B                 xor     eax, ecx
.text:0040378D                 mov     ecx, eax
.text:0040378F                 and     ecx, 80000000h
.text:00403795                 neg     ecx
.text:00403797                 sbb     ecx, ecx
.text:00403799                 and     ecx, 4C11DB7h
.text:0040379F                 lea     edx, [eax+eax]
.text:004037A2                 xor     ecx, edx
.text:004037A4                 mov     eax, ecx
.text:004037A6                 and     eax, 80000000h
.text:004037AB                 neg     eax
.text:004037AD                 sbb     eax, eax
.text:004037AF                 add     ecx, ecx
.text:004037B1                 and     eax, 4C11DB7h
.text:004037B6                 xor     eax, ecx
.text:004037B8                 mov     ecx, eax
.text:004037BA                 and     ecx, 80000000h
.text:004037C0                 neg     ecx
.text:004037C2                 sbb     ecx, ecx
.text:004037C4                 and     ecx, 4C11DB7h
.text:004037CA                 lea     edx, [eax+eax]
.text:004037CD                 xor     ecx, edx
.text:004037CF                 mov     eax, ecx
.text:004037D1                 and     eax, 80000000h
.text:004037D6                 neg     eax
.text:004037D8                 sbb     eax, eax
.text:004037DA                 add     ecx, ecx
.text:004037DC                 and     eax, 4C11DB7h
.text:004037E1                 xor     eax, ecx
.text:004037E3                 mov     ecx, eax
.text:004037E5                 and     ecx, 80000000h
.text:004037EB                 neg     ecx
.text:004037ED                 sbb     ecx, ecx
.text:004037EF                 and     ecx, 4C11DB7h
.text:004037F5                 lea     edx, [eax+eax]
.text:004037F8                 xor     ecx, edx
.text:004037FA                 mov     [esp+ebx*4+40Ch+var_400], ecx
.text:004037FE                 mov     eax, ecx
.text:00403800                 xor     edx, edx
.text:00403802                 mov     ecx, 1Fh
.text:00403807
.text:00403807 loc_0_403807:                           ; CODE XREF: Create_hash_table+12Aj
.text:00403807                 test    al, 1
.text:00403809                 jz      short loc_0_403814
.text:0040380B                 mov     esi, 1
.text:00403810                 shl     esi, cl
.text:00403812                 or      edx, esi
.text:00403814
.text:00403814 loc_0_403814:                           ; CODE XREF: Create_hash_table+119j
.text:00403814                 shr     eax, 1
.text:00403816                 dec     ecx
.text:00403817                 cmp     ecx, 0FFFFFFFFh
.text:0040381A                 jg      short loc_0_403807
.text:0040381C                 mov     [esp+ebx*4+40Ch+var_400], edx ; store in hash table
.text:00403820                 inc     ebx
.text:00403821                 cmp     ebx, 0FFh       ; hash table completed ?
.text:00403827                 jle     hash_table_loop
All the above code is only to create a hash table. We will need to copy this piece of code for our keygen, or you can just dump the table. Both ways are good, because the table does not depend on variables.
.text:0040382D                 mov     ecx, edi        ; userdata

.text:0040382F                 or      eax, 0FFFFFFFFh
.text:00403832                 lea     esi, [ecx+1]    ; p(userdata)+1
.text:00403835
.text:00403835 loop_data:                              ; CODE XREF: Create_hash_table+14Aj
.text:00403835                 mov     dl, [ecx]
.text:00403837                 inc     ecx
.text:00403838                 test    dl, dl          ; char (userdata) == 0 ?
.text:0040383A                 jnz     short loop_data
.text:0040383C                 sub     ecx, esi        ; ecx = userdata length
.text:0040383E                 mov     edx, edi        ; edx = userdata
.text:00403840                 jz      short loc_0_40385C ;
.text:00403840
.text:00403840; **userdata = name + company name + 37868299
.text:00403842
.text:00403842 process_more_data:                      ; CODE XREF: .text:0040385Aj
.text:00403842                 movzx   edi, byte ptr [edx] ; edi = (char) userdata

.text:00403845                 mov     esi, eax        ; esi = hash 
.text:00403847                 and     esi, 0FFh       ; esi = char (hash) 
.text:0040384D                 xor     esi, edi        ; esi = char (esi)  XOR char (userdata)
.text:0040384F                 mov     esi, [esp+esi*4+40Ch+var_400] ; esi = hashtable [esi]
.text:00403853                 shr     eax, 8          ; eax = eax/256
.text:00403853 Create_hash_table endp
.text:00403853
.text:00403856                 xor     eax, esi        ; hash = hash XOR hashtable dword

.text:00403858                 inc     edx             ; pointer ++
.text:00403859                 dec     ecx             ; counter --
.text:0040385A                 jnz     short process_more_data
.text:0040385C
.text:0040385C loc_0_40385C:                           ; CODE XREF: Create_hash_table+150j
.text:0040385C                 pop     edi
.text:0040385D                 pop     esi
.text:0040385E                 not     eax             ; NOT hash
.text:00403860                 pop     ebx
.text:00403861                 add     esp, 400h
.text:00403867                 retn
.text:00403867 ; ---------------------------------------------------------------------------
.text:00403868                 align 10h
.text:00403870                 mov     eax, offset unk_0_524F28
.text:00403875                 retn
.text:00403875 ; ---------------------------------------------------------------------------

First Conclusion
If you traced the above code with softice, then you are able to construct a valid serial for your data. The first 2 parts of the serial are equal to the first hash(at VA 00446E2Dh in eax) and the 5th and 6th part are equal to the hash of the 3rd and 4th part. (at VA 00446E70h in eax). 3rd and 4th part of serial can be chosen at random... a bit weird... Ok, enter the serials, the buttons gets enabled ;) it works!!! :))) Quit the app completly, and restart it, baammm the registration box is back :/ It seems the 3rd and 4th serial part are probably not so random afterall...
The attack part 2 ;)
There must be a second larger check in the startup of the program...
How can we reach it? we can use the softice symbol loader, and bpx on the pasteroutine or even the hashing table routine. (This won't work if they used a copy of those routines for the startup check).
Another way is to fire up regmon, and check where the serial key is stored in the registry. Even more simple is just to do a search for a part of ur serial in the registry (using regedit). and then bpx that regkey. Anyway u do it, u should land in the following code somewhere :
.text:00446EC0                 sub     esp, 204h
.text:00446EC6                 mov     eax, dword_0_5232D0
.text:00446ECB                 xor     eax, [esp+204h]
.text:00446ED2                 push    esi
.text:00446ED3                 push    edi
.text:00446ED4                 mov     esi, edx
.text:00446ED6                 push    offset unk_0_5188AB ; lpString2
.text:00446EDB                 push    esi             ; lpString1
.text:00446EDC                 mov     [esp+214h+var_4], eax
.text:00446EE3                 mov     edi, ecx
.text:00446EE5                 call    ds:lstrcmpA
.text:00446EEB                 test    eax, eax
.text:00446EED                 jnz     short loc_0_446EF6
.text:00446EEF
.text:00446EEF loc_0_446EEF:                           ; CODE XREF: sub_0_446EC0+40j
.text:00446EEF                                         ; sub_0_446EC0+8Ej ...
.text:00446EEF                 xor     al, al
.text:00446EF1                 jmp     loc_0_446FD0
.text:00446EF6 ; ---------------------------------------------------------------------------
.text:00446EF6
.text:00446EF6 loc_0_446EF6:                           ; CODE XREF: sub_0_446EC0+2Dj
.text:00446EF6                 push    ebx             ; lpString
.text:00446EF7                 call    ds:lstrlenA
.text:00446EFD                 cmp     eax, 18h
.text:00446F00                 jnz     short loc_0_446EEF
.text:00446F02                 test    edi, edi
.text:00446F04                 mov     eax, edi
.text:00446F06                 jnz     short loc_0_446F0D
.text:00446F08                 mov     eax, offset unk_0_5188AB
.text:00446F0D
.text:00446F0D loc_0_446F0D:                           ; CODE XREF: sub_0_446EC0+46j
.text:00446F0D                 push    241D30Bh        ; fixed digits

.text:00446F12                 push    eax
.text:00446F13                 push    esi
.text:00446F14                 lea     eax, [esp+218h+var_204]
.text:00446F18                 push    offset aSSD     ; "%s%s%d"
.text:00446F1D                 push    eax
.text:00446F1E                 call    paste_routine
.text:00446F23                 lea     eax, [esp+220h+var_204]
.text:00446F27                 call    Create_hash_table ; create hash 1
.text:00446F2C                 push    eax
.text:00446F2D                 lea     ecx, [esp+224h+var_204]
.text:00446F31                 push    offset a08x     ; "%08X"
.text:00446F36                 push    ecx
.text:00446F37                 call    paste_routine   ; userstring = hash1
.text:00446F3C                 add     esp, 20h
.text:00446F3F                 mov     ecx, 2
.text:00446F44                 lea     edi, [esp+20Ch+var_204]
.text:00446F48                 mov     esi, ebx
.text:00446F4A                 xor     edx, edx
.text:00446F4C                 repe cmpsd              ; check serial part 1 & 2 == hash 1
.text:00446F4E                 jnz     short loc_0_446EEF
.text:00446F50                 push    3EE24C3h        ; second fixed digits
.text:00446F55                 lea     eax, [esp+210h+var_204]
.text:00446F59                 push    eax
.text:00446F5A                 lea     ecx, [esp+214h+var_104]
.text:00446F61                 push    offset aS08x    ; "%s%08X"

.text:00446F66                 push    ecx
.text:00446F67                 call    paste_routine
.text:00446F6C                 lea     eax, [esp+21Ch+var_104]
.text:00446F73                 call    Create_hash_table
.text:00446F78                 push    eax             ; hash 2
.text:00446F79                 lea     edx, [esp+220h+var_204]
.text:00446F7D                 push    offset a08x     ; "%08X"
.text:00446F82                 push    edx
.text:00446F83                 call    paste_routine
.text:00446F88                 add     esp, 1Ch
.text:00446F8B                 mov     ecx, 2
.text:00446F90                 lea     edi, [esp+20Ch+var_204]
.text:00446F94                 lea     esi, [ebx+8]
.text:00446F97                 xor     eax, eax
.text:00446F99                 repe cmpsd              ; check serial part 3 & 4 == hash 2
.text:00446F9B                 jnz     loc_0_446EEF
.text:00446FA1                 lea     eax, [esp+20Ch+var_204]
.text:00446FA5                 call    Create_hash_table
.text:00446FAA                 push    eax
.text:00446FAB                 lea     ecx, [esp+210h+var_204]
.text:00446FAF                 push    offset a08x     ; "%08X"
.text:00446FB4                 push    ecx
.text:00446FB5                 call    paste_routine
.text:00446FBA                 add     esp, 0Ch
.text:00446FBD                 mov     ecx, 2
.text:00446FC2                 lea     edi, [esp+20Ch+var_204]
.text:00446FC6                 lea     esi, [ebx+10h]
.text:00446FC9                 xor     edx, edx
.text:00446FCB                 repe cmpsd
.text:00446FCD                 setz    al
.text:00446FD0
.text:00446FD0 loc_0_446FD0:                           ; CODE XREF: sub_0_446EC0+31j
.text:00446FD0                 mov     ecx, [esp+20Ch+var_4]
.text:00446FD7                 xor     ecx, [esp+20Ch]
.text:00446FDE                 pop     edi
.text:00446FDF                 pop     esi
.text:00446FE0                 call    sub_0_4D6F4A
.text:00446FE5                 add     esp, 204h
.text:00446FEB                 retn    4
.text:00446FEB sub_0_446EC0    endp

I have commented this routine less than the other one, but you can see it is very simular. The only difference, is that the 3rd and 4th part of the serial are calculated by adding some fixed numbers to the first hash, and rehash that. This gives us a valid 3rd and 4th part of the serial, and the 5th and 6th part remain the same, just a hashing of the 2nd calculated hash.
heh, ok maybe this is a bit confusing, let's try to put this in a scheme :
		name+company+37868299
			|
			|
		  create hash 1
		  	|
		  hash1 == serial part 1 & 2
		  	|
		  	|
		  hash1+03ee24c3
		  	|
		  	|
		  create hash 2
		  	|
		  hash2 == serial part 3 & 4
		  	|
		  	|
		     hash 2
		     	|
		     	|
                   create hash 3
                   	|
                  hash3 == serial part 5 & 6
Ok, now we now that, all we have to do is rip the create_hash_table routine (which includes the data hashing) and use it 3 times in our keygen. That's all ;)
Our keygen routine
This routine is ripped and slightly adjusted. It's the Create_hash_table routine.
;-------------------------------------------------------------------+
;                    key-generation procedure                       |
;-------------------------------------------------------------------+
GenerateKey PROC aname:DWORD, aserial:DWORD
;--------------------------------------------------------------------
; ALGO RECONSTRUCTION (CRC32 TABLE + HASHING)
;--------------------------------------------------------------------
mov     edi, aname
xor     ebx, ebx
lea     ecx, [ecx+0]
hash_table_loop:
xor     ecx, ecx
test    bl, 1
jz      jump1
mov     ecx, 80h

jump1:
mov     eax, ebx
shr     eax, 1
test    al, 1
jz      jump2
or      ecx, 40h

jump2:
shr     eax, 1
test    al, 1
jz      jump3
or      ecx, 20h

jump3:
shr     eax, 1
test    al, 1
jz      jump4
or      ecx, 10h

jump4:
shr     eax, 1
test    al, 1
jz      jump5
or      ecx, 8

jump5:
shr     eax, 1
test    al, 1
jz      jump6
or      ecx, 4

jump6:
shr     eax, 1
test    al, 1
jz      jump7
or      ecx, 2

jump7:
test    al, 2
jz      jump8
or      ecx, 1

jump8:
shl     ecx, 18h
mov     eax, ecx
and     eax, 80000000h
neg     eax
sbb     eax, eax
add     ecx, ecx
and     eax, 4C11DB7h
xor     eax, ecx
mov     ecx, eax
and     ecx, 80000000h
neg     ecx
sbb     ecx, ecx
and     ecx, 4C11DB7h
lea     edx, [eax+eax]
xor     ecx, edx
mov     eax, ecx
and     eax, 80000000h
neg     eax
sbb     eax, eax
add     ecx, ecx
and     eax, 4C11DB7h
xor     eax, ecx
mov     ecx, eax
and     ecx, 80000000h
neg     ecx
sbb     ecx, ecx
and     ecx, 4C11DB7h
lea     edx, [eax+eax]
xor     ecx, edx
mov     eax, ecx
and     eax, 80000000h
neg     eax
sbb     eax, eax
add     ecx, ecx
and     eax, 4C11DB7h
xor     eax, ecx
mov     ecx, eax
and     ecx, 80000000h
neg     ecx
sbb     ecx, ecx
and     ecx, 4C11DB7h
lea     edx, [eax+eax]
xor     ecx, edx
mov     eax, ecx
and     eax, 80000000h
neg     eax
sbb     eax, eax
add     ecx, ecx
and     eax, 4C11DB7h
xor     eax, ecx
mov     ecx, eax
and     ecx, 80000000h
neg     ecx
sbb     ecx, ecx
and     ecx, 4C11DB7h
lea     edx, [eax+eax]
xor     ecx, edx
mov     [ebx*4+offset hashtable], ecx
mov     eax, ecx
xor     edx, edx
mov     ecx, 1Fh

jump9:
test    al, 1
jz      jump10
mov     esi, 1
shl     esi, cl
or      edx, esi

jump10:
shr     eax, 1
dec     ecx
cmp     ecx, 0FFFFFFFFh
jg      jump9
mov     [ebx*4+offset hashtable], edx ; store in hash table
inc     ebx
cmp     ebx, 0FFh       ; hash table completed ?
jle     hash_table_loop
mov     ecx, edi        ; name + company name + digits
or      eax, 0FFFFFFFFh
lea     esi, [ecx+1]    ; lp(name + company name + digits)+1

loop_data:
mov     dl, [ecx]
inc     ecx
test    dl, dl          ; char (name + company name + digits) == 0 ?
jnz     short loop_data
sub     ecx, esi        ; ecx = name + company name + digits length
mov     edx, edi        ; edx = name + company name + digits
jz      jump11

process_more_data:
movzx   edi, byte ptr [edx] ; edi = (char) userdata
mov     esi, eax        ; esi = hash
and     esi, 0FFh       ; esi = char (hash)
xor     esi, edi        ; esi = char (esi)  XOR char (userdata)
mov     esi, [esi*4+offset hashtable] ; esi = hashtable [esi]
shr     eax, 8          ; eax = eax/256
xor     eax, esi        ; hash = hash XOR hashtable dword
inc     edx             ; pointer ++
dec     ecx             ; counter --
jnz     short process_more_data

jump11:
not     eax             ; NOT hash

;--------------------------------------------------------------------------
; END OF CRC32 HASHING ALGO   ---> HASH in EAX
;--------------------------------------------------------------------------

invoke wsprintf, aserial, addr ToHexa, eax
ret
GenerateKey ENDP
When we have made this, all we need is to reconstruct the little scheme above :

pushad
invoke lstrcat, ADDR thename, ADDR thecompany
invoke lstrcat, ADDR thename, ADDR thedigits
invoke GenerateKey, addr thename, addr theserial    ; pointer to name, pointer to buffer
mov esi, offset theserial
mov edi, offset buffer
xor ecx, ecx
.WHILE ecx 
	That's all we need, now write a nice GUI for it, and you have a working keygen ;)
	Check out the source, it's pretty straightforward if u understand what happens in
	the application.
	
Outtro
This algo is based on a CRC32 hashing table, and as soon as u r able to reconstruct this table in your keygen, it is pretty easy to keygen it. In this case the same table was used 3 times.

Greetz to all BiW members ;)
Special thanx to fuss :)


Greetz,
Detten
Detten (at) reversing (dot) be




What's Related

Story Options

Keygenning a serialroutine which uses CRC32 | 0 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
 Copyright © 2020 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 0.73 seconds