[SyntaxError's keygenme #7 ]
par Amenesia

 

Analyse du code:
  Le programme est compressé avec UPX ( nom des sections ) donc "UPX -d.... " puis, recherche des 2 GetDlgItemTextA :



Premiere information le Serial comporte 10h caracteres et visiblement la verification Nom/Serial est faite au niveau des calls CalClefSerial / Check...

_text:00401B75                 mov     edi, ds:GetDlgItemTextA
_text:00401B7B                 push    64h
_text:00401B7D                 push    offset Serial
_text:00401B82                 push    3E9h
_text:00401B87                 push    esi
_text:00401B88                 call    edi               ; GetDlgItemTextA
_text:00401B8A                 cmp     eax, 10h
_text:00401B8D                 jnz     TailleInvalide
_text:00401B8F                 push    64h
_text:00401B91                 push    offset Name
_text:00401B96                 push    3E8h
_text:00401B9B                 push    esi
_text:00401B9C                 call    edi              ; GetDlgItemTextA
_text:00401B9E                 call    CalClefSerial
_text:00401BA3                 call    Check
_text:00401BA8 TailleInvalide: 
_text:00401BA8                 pop     edi
_text:00401BA9                 xor     eax, eax
_text:00401BAB                 pop     esi
_text:00401BAC                 retn    10h


CalClefSerial:

Dans un premier temps, CalClefSerial transforme toutes les lettres du Serial en majuscule...

_text:00401D97                 push    offset Serial 
_text:00401D9C                 xor     edi, edi
_text:00401D9E                 mov     esi, 1
_text:00401DA3                 call    ds:CharUpperA

Puis calcule une clef grace à ses 8 premiers caracteres...

_text:00401DA9                 mov     eax, 7
_text:00401DAE                 mov     bl, 0D0h
_text:00401DB0 
_text:00401DB0 BoucleClef1: 
_text:00401DB0                 mov     cl, [ Serial + eax ]
_text:00401DB6                 cmp     cl, 39h
_text:00401DB9                 jle     PasChiffre1
_text:00401DBB                 sub     cl, 7
_text:00401DBE                 mov     [ Serial + eax ], cl
_text:00401DC4 
_text:00401DC4 PasChiffre1: 
_text:00401DC4                 mov     cl, [ Serial + eax ]
_text:00401DCA                 add     cl, bl
_text:00401DCC                 mov     [ Serial + eax ], cl
_text:00401DD2                 movsx   ecx, cl
_text:00401DD5                 imul    ecx, esi
_text:00401DD8                 add     edi, ecx
_text:00401DDA                 shl     esi, 4 
_text:00401DDD                 dec     eax
_text:00401DDE                 jns     BoucleClef1

_text:00401DE0                 mov     [Clef_tmp], edi
_text:00401DE3                 mov     eax, [Clef_tmp]
_text:00401DE6                 mov     [ Clef1 ], eax

Ainsi pour chaque caractere:
1_ on recupere sa valeur ascii et on lui retranche 7 si elle est superieur à 39h
2_ on calcule Clef1 += ( valCar + D0h )*16^( position caractere )
 


Puis  une seconde grace à ses 8 derniers...

_text:00401DEB                 mov     ecx, 1
_text:00401DF0                 xor     esi, esi
_text:00401DF2                 mov     eax, 0Fh
_text:00401DF7 
_text:00401DF7 BoucleClef2: 
_text:00401DF7                 mov     dl, [ Serial + eax ]
_text:00401DFD                 cmp     dl, 39h
_text:00401E00                 jle     PasChiffre2
_text:00401E02                 sub     dl, 7
_text:00401E05                 mov     [ Serial + eax ], dl
_text:00401E0B 
_text:00401E0B PasChiffre2: 
_text:00401E0B                 mov     dl, [ Serial+eax ]   ; recupere un caractere
_text:00401E11                 add     dl, bl               ; + 0Dh
_text:00401E13                 mov     [ Serial+eax ], dl
_text:00401E19                 movsx   edx, dl
_text:00401E1C                 imul    edx, ecx 
_text:00401E1F                 add     esi, edx
_text:00401E21                 shl     ecx, 4
_text:00401E24                 dec     eax
_text:00401E25                 cmp     eax, 8
_text:00401E28                 jge     BoucleClef2
_text:00401E2A                 mov     [Clef_tmp], esi
_text:00401E2D                 mov     eax, [Clef_tmp]
_text:00401E30                 mov     [ Clef2 ], eax

Ainsi pour chaque caractere:
1_ on recupere sa valeur ascii et on lui retranche 7 si elle est superieur à 39h
2_ on calcule Clef2 += ( valCar + D0h )*16^( position caractere - 8)

Ces deux routines permettent de convertir une chaine ( representant une valeur hexadecimal) en un valeur numerique...  : 
0 = ( ascii('0') + D0h) mod 100h = ( 30h + D0h) mod 100h  = 100h mod 100h
...
9 = ( ascii('9') + D0h) mod 100h = ( 39h + D0h) mod 100h  = 109h mod 100h
A = ( ascii('A') -7 + D0h) mod 100h = ( 41h -7 + D0h) mod 10h  =  (3Ah + D0h) mod 100h
...
F = ( ascii('F') -7 + D0h) mod 100h = ( 46h -7 + D0h) mod 10h  =  (3Fh + D0h) mod 100h



Check:

Le call situé en 00401C80 utilise les valeurs typiques des fonctions de hash SHA-1 / MD5 / Ripe-MD :
67452301h, 0EFCDAB89h ,98BADCFEh et 10325476h... On calcule donc le differents hash possibles du nom entré ( merci "CrypTool" ;) ) et on observe les differentes valeurs renvoyées par les calls suivant en esperant qu'une de ces valeurs corresponde a un des hash calculé...

_text:00401C7C                 lea     eax, [ebp+BuffHashNom]
_text:00401C7F                 push    eax
_text:00401C80                 call    IniHash



Effectivement, quelques lignes plus bas le call renvoit le hash MD5 du nom entré... 

_text:00401CAB                 lea     edx, [ MD5 ]
_text:00401CAE                 push    edx
_text:00401CAF                 lea     eax, [ Buff ]
_text:00401CB2                 push    eax
_text:00401CB3                 call    MD5



Puis chacun des  4 dword composant le hash est recopié ( on calcule par la meme occasion MD5_1 xor MD5_3 ):
     HASH_MD5 = [MD5_1][MD5_2][MD5_3][MD5_4]

_text:00401CBB                 mov     ebx, [MD5_1]
_text:00401CBE                 mov     [MD5_1], ebx

_text:00401CC1                 xor     ebx, [MD5_3]
_text:00401CC4                 mov     [ MD5_1xorMD5_3 ], ebx

_text:00401CCA                 mov     ebx, [ MD5_2 ]
_text:00401CCD                 mov     [ MD5_2c ], ebx

_text:00401CD0                 mov     ebx, [ MD5_3 ]
_text:00401CD3                 mov     [ MD5_3c ], ebx

_text:00401CD6                 mov     ebx, [ MD5_4 ]
_text:00401CD9                 mov     [ MD5_4c ], ebx



Ces 5 valeurs, ainsi que les 2 clefs créées precedement, sont utilsées pour calculer 2 nouvelles valeurs:

_text:00401CDC                 mov     ebx, [Clef2]
_text:00401CE2                 mov     [ Buffer2 ], ebx     ; Buffer2 = Clef2

_text:00401CE5                 mov     ebx, [ Clef1 ]
_text:00401CEB                 mov     [ Buffer1 ], ebx   ; Buffer1 = Clef1

_text:00401CEE                 mov     ecx, [ Buffer2 ]
_text:00401CF1                 shl     ecx, 4
_text:00401CF4                 add     ecx, [ MD5_3c ]       ; MD5_3 + Clef2*16

_text:00401CF7                 mov     edx, [ Buffer2 ]
_text:00401CFA                 add     edx, [ MD5_1xorMD5_3 ] ; Clef2 + MD5_1xorMD5_3

_text:00401D00                 xor     ecx, edx

_text:00401D02                 mov     eax, [ Buffer2 ]
_text:00401D05                 shr     eax, 5
_text:00401D08                 add     eax, [ MD5_4c ]      ; MD5_4 + Clef2/64

_text:00401D0B                 xor     ecx, eax

_text:00401D0D                 mov     edx, [ Buffer1 ]
_text:00401D10                 sub     edx, ecx
_text:00401D12                 mov     [ Buffer1 ], edx

On a alors:
Buffer1 = Clef1 - [ (MD5_3 + Clef2*16) xor ( Clef2 + MD5_1xorMD5_3 ) xor ( MD5_4 + Clef2/64) ]
Pour  simplifier on appellera cette expression A... ;)

_text:00401D15                 mov     eax, [ Buffer1 ]
_text:00401D18                 shl     eax, 4
_text:00401D1B                 add     eax, [ MD5_1c ]                 ;  A*16 + MD5_1c

_text:00401D1E                 mov     ecx, [ Buffer1 ]
_text:00401D21                 add     ecx, [ MD5_1xorMD5_3 ]  ;  A + MD5_1xorMD5_3

_text:00401D27                 xor     eax, ecx

_text:00401D29                 mov     edx, [ Buffer1 ]
_text:00401D2C                 shr     edx, 5
_text:00401D2F                 add     edx, [ebp+MD5_2c]       ;  A/64 + MD5_2c

_text:00401D32                 xor     eax, edx

_text:00401D34                 mov     ecx, [ Buffer2 ]
_text:00401D37                 sub     ecx, eax
_text:00401D39                 mov     [ Buffer2 ], ecx

Resultat:
Buffer2 = Clef2 - [ (MD5_1c + A*16) xor ( A + MD5_1xorMD5_3 ) xor ( MD5_2 + A/64) ]


Puis le programme s'assure que les 2 valeurs sont valides

_text:00401D3C                 cmp     [ Buffer2 ], 6779656Bh
_text:00401D43                 jnz     MauvaisSerial
_text:00401D45                 cmp     [ Buffer1 ], 656D6E65h
_text:00401D4C                 jnz     MauvaisSerial
_text:00401D4E                 push    40h 
_text:00401D50                 push    offset aKeygenme_0
_text:00401D55                 push    offset aCongratsYouDidIt 

Autrement dit pour que le serial soit valide il faut que:
6779656Bh = Clef2 - [ (MD5_1c + A*16) xor ( A + MD5_1xorMD5_3 ) xor ( MD5_2 + A/64) ]
656D6E65h = A = Clef1 - [ (MD5_3 + Clef2*16) xor ( Clef2 + MD5_1xorMD5_3 ) xor ( MD5_4 + Clef2/64) ]

On en deduit que:
Clef2 = 6779656Bh + [ (MD5_1c + A*16) xor ( A + MD5_1xorMD5_3 ) xor ( MD5_2 + A/64) ]
avec A = 656D6E65h

Une fois Clef2 determiner, aucun probleme pour evaluer Clef1...
Clef1 =  656D6E65h + [ (MD5_3 + Clef2*16) xor ( Clef2 + MD5_1xorMD5_3 ) xor ( MD5_4 + Clef2/64) ]

Il ne reste plus qu'a transformer la valeur de Clef1 et de Clef2 en chaine pour obtenir le bon serial...



 

Amusez vous bien ;)