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 ;)
|