introduction
Le première partie vous a exposé les principes
et quelques méthodes, maintenant, on va passer à la pratique pour que cela soit plus clair.
Mon tut s'axe sur le debugger et IDA, mais vous pouvez utiliser aussi W32Dasm si cela vous permet de mieux cerner
les choses.
Pour le debugger, choisissez celui que vous voulez, même ceux en mousse (1)
comme Ollydebug.
La cible sera Gif Movie Gear 3.0.2 Version Française de Gamani Software, car il donne tous les cas de figure
exposés dans la partie 1 ( Rippage ASM, rebranchement constante, variables et identification de calls, problème
de format... )
Attention, la version anglophone n'a pas le même type de schéma à keygenner. ( c'est même
encore plus simple ! )
Pour reprendre les principes du tut d'avant, on suivra les étapes suivantes :
- Localisation de la routine.
- Moyen de désenregistrement pour repasser en "non enregistré".
- Délimitation de la routine.
- Rippage de la routine.
- Rebranchements des "flux" d'informations et identification.
Pour la vérification du Keygen, vous pourrez le faire vous-même.
Dernière chose :
Comme pour la partie 1, tout est montré en détail.
Le tut est long mais cela vient surtout de la présence des captures de Softice et des Rippages, sinon dans
la pratique, vous verrez que c'est ultra rapide.
localisation de la routine
La boite d'enregistrement apparait dans" Aide > S'enregistrer maintenant ".
On tape :
Nom = Lise_Grim honneur aux Dames, "Monsieur Presage" :)
Serial = 321654
On pose un " BPX GetWindowTextA ", on clique sur " Ok " et Softice breake.
On méne une petite analyse standard en passant tout en TRACE OVER.
-------------------------------------------------------------------------PROT32- |
015F:00431D89 MOV EBX,[USER32!GetWindowTextA] |
015F:00431D8F PUSH EAX |
015F:00431D90 CALL EBX <--- call GetWindowTextA |
015F:00431D92 LEA EDX,[ESP+000000C4] |
015F:00431D99 PUSH 64 |
015F:00431D9B PUSH EDX |
015F:00431D9C PUSH 00000450 |
015F:00431DA1 PUSH ESI |
015F:00431DA2 CALL EDI <--- ? call 1 |
015F:00431DA4 PUSH EAX <--- ? call 2 |
015F:00431DA5 CALL EBX |
015F:00431DA7 LEA EAX,[ESP+000000C4] |
015F:00431DAE LEA ECX,[ESP+60] |
015F:00431DB2 PUSH EAX <--- "321654" |
015F:00431DB3 PUSH ECX <--- "Lise_Grim" |
015F:00431DB4 CALL 004318A0 <--- ? call 3 |
015F:00431DB9 ADD ESP,08 |
015F:00431DBC TEST EAX,EAX <--- eax = 0 ? |
015F:00431DBE JZ 00431E84 <--- oui = Bad Serial |
------------------------------------MOVGEAR!.text+00030D86---------------------- |
|
|
Les calls 1 et 2 sont peut-être à
étudier mais ce qui semble le plus intéressant, c'est le call 3 qui prend nos informations.
Si on renverse le saut qui suit avec " rfl
z ", on a une boite : "Votre logiciel
est maintenant enregistré. Félicitations."
Ok, on se fixe donc comme premier Interval de travail : [
00431DB2 / 00431DBE ] que je note sur un papier.
On est pas encore convaincu que ce call 3 contienne la routine, mais un schéma comme celui-ci est très
prometteur :
push informations
call ****
test eax, eax
jz Bad_Serial
On décide de rentrer dans ce call 3.
-------------------------------------------------------------------------PROT32- |
015F:0043189F NOP |
015F:004318A0 MOV EAX,[ESP+08] |
015F:004318A4 MOV ECX,[ESP+04] |
015F:004318A8 PUSH EAX <--- "321654" |
015F:004318A9 PUSH ECX <--- "Lise_Grim" |
015F:004318AA CALL 004318C0 <--- ? call 4 |
015F:004318AF ADD ESP,08 |
015F:004318B2 RET |
015F:004318B3 NOP |
------------------------------------MOVGEAR!.text+0003089F---------------------- |
|
|
On rentre donc dans le call 4... Rien de transcendant...
-------------------------------------------------------------------------PROT32- |
015F:004318C0 SUB ESP,000000D8 |
015F:004318C6 OR ECX,-01 |
015F:004318C9 XOR EAX,EAX |
015F:004318CB LEA EDX,[ESP+10] |
015F:004318CF PUSH EBX |
015F:004318D0 PUSH EBP |
015F:004318D1 PUSH ESI |
015F:004318D2 PUSH EDI |
015F:004318D3 MOV EDI,[ESP+000000EC] |
015F:004318DA REPNZ SCASB <--- tiens :) |
015F:004318DC NOT ECX |
015F:004318DE SUB EDI,ECX |
015F:004318E0 MOV EAX,ECX |
015F:004318E2 MOV ESI,EDI |
015F:004318E4 MOV EDI,EDX |
015F:004318E6 SHR ECX,02 |
015F:004318E9 REPZ MOVSD <--- tiens :) |
015F:004318EB MOV ECX,EAX |
015F:004318ED AND ECX,03 |
015F:004318F0 REPZ MOVSB <--- tiens :) |
015F:004318F2 LEA ECX,[ESP+20] |
015F:004318F6 PUSH ECX |
015F:004318F7 CALL [USER32!CharUpperA] <--- tiens :) |
015F:004318FD MOV AL,[ESP+20] |
015F:00431901 LEA ESI,[ESP+20] |
015F:00431905 TEST AL,AL |
015F:00431907 LEA EDI,[ESP+20] |
015F:0043190B JZ 0043192F |
015F:0043190D MOVSX EDX,BYTE
PTR [ESI] |
------------------------------------MOVGEAR!.text+000308C0---------------------- |
|
|
Déjà, du premier coup d'oeil, il y a des choses
qui semblent intéressantes ( manipulations sur Strings et un CharUpperA ... )
Comme le tut partie 1 l'a montré, on se fiche de savoir ce que va faire le logiciel comme calcul,... tout
ce que l'on veut, c'est une routine qui prend le nom et le transforme en serial.
On va maintenant s'attacher à trouver cela...
Afin d'avoir un meilleur aperçu, on descend dans la fenêtre code ( sans tracer ) pour voir ce qu'il
y a jusqu'au RET ( car on est dans un call, je rappelle. )
Une fois que j'ai trouvé, je regarde et je vois 2 RET :
- (1) eax = 1 + RET ---> bon serial.
- (2) eax = 0 + RET ---> mauvais serial.
Ok, alors on regarde un peu avant pour trouver quelquechose d'intéressant. Pour cela, je mets un BPX 00431A05
puis F5 pour ne pas à avoir à tracer...
Je regarde aussi vers quoi pointent les registres qui sont utilisés.
--------------------------------------------------byte--------------PROT---(0)-- |
0167:006BF2CC 44 42 34
38 35 42 2D 44-43 4B 55 52 46 2D 35 37 DB485B-DCKURF-57 |
0167:006BF2DC 53 34 4B
55 2D 44 54 52-55 44 46 00 28 00 97 01 S4KU-DTRUDF.(... |
-------------------------------------------------------------------------PROT32- |
015F:004319FE MOV EBX,EDI |
015F:00431A00 CMP EBX,18 |
015F:00431A03 JL 004319A9 |
015F:00431A05 MOV EAX,[ESP+000000F0] <--- "321654" |
015F:00431A0C MOV BYTE
PTR [EBP+00],00 |
015F:00431A10 LEA ESI,[ESP+00000084] <--- voir ci-dessus |
015F:00431A17 MOV DL,[EAX] |
015F:00431A19 MOV BL,[ESI] |
............. ... ........ |
............. ... ........ |
015F:00431A44 POP EDI |
015F:00431A45 POP ESI |
015F:00431A46 POP EBP |
015F:00431A47 POP EBX |
015F:00431A48 TEST EAX,EAX |
015F:00431A4A JNZ 00431A58 |
015F:00431A4C MOV EAX,00000001 <--- EAX = 1 ( BON ) |
015F:00431A51 ADD ESP,000000D8 |
015F:00431A57 RET <--- RET 1 |
015F:00431A58 XOR EAX,EAX <--- EAX = 0 ( MAUVAIS ) |
015F:00431A5A ADD ESP,000000D8 |
015F:00431A60 RET <--- RET 2 |
------------------------------------MOVGEAR!.text+000309FE---------------------- |
|
|
En 00431A10, ESI pointe vers ce qui semblerait être
un serial ( string en rouge ). On le teste et cela marche.
Ok, on a trouvé notre routine.
Nouvel interval de travail : [ Début
de ce call = 004318C0 / Là où on voit le serial = 00431A05 ]
On s'est enregistré en testant le serial... Il faut pouvoir revenir en mode "non enregistré".
désenregistrement
On regarde dans la Base des registres avec Regedit de Windows...
Et on trouve les informations dans HKEY_CURRENT_USER\Software\gamani\GIFMovieGear\2.0
- "RegName3"="Lise_Grim"
- "RegCode3"="DB485B-DCKURF-57S4KU-DTRUDF"
Dans Regedit, on clique sur " Registre
> Exporter un fichier du registre "
et on sauvegarde ce fichier *.REG.
On a dedans les informations suivantes :
REGEDIT4
[HKEY_CURRENT_USER\Software\gamani\GIFMovieGear\2.0]
"RegName3"="Lise_Grim"
"RegCode3"="DB485B-DCKURF-57S4KU-DTRUDF"
"Placement"=hex:2c,00,00,00,00,00,00,00,01,00,00,00,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,\
ff,ff,ff,ff,06,01,00,00,68,00,00,00,5d,03,00,00,c3,01,00,00
"Pref"=hex:0a,00,00,00,00,00,00,00,20,00,00,00,00,00,00,00,00,00,00,00,6e,04,\
00,00,00,20,82,00,00,00,00,00,07,00,00,00,01,00,00,00,01,00,88,00,00,00,00,\
00,64,00,00,00,00,00,00,00
"NPref"=dword:0000000e
On efface ce qui nous sert pas, et on conserve :
REGEDIT4
[HKEY_CURRENT_USER\Software\gamani\GIFMovieGear\2.0]
"RegName3"=""
"RegCode3"=""
Toutes les fois que l'on cliquera sur ce fichier, on reviendra en "non enregistré"... Aussi simple
que cela !
délimitation
C'est bientôt l'été, le "Diktat"
de la cure d'amaigrissement est en vigeur, alors la routine ne va pas y couper non plus.
Autant ne ripper que le strict nécessaire.
On reprend notre routine à son point de départ et on passe tout en TRACE OVER.
-------------------------------------------------------------------------PROT32- |
015F:004318C0 SUB ESP,000000D8 <--- on s'en fiche |
015F:004318C6 OR ECX,-01 <--- on s'en fiche |
015F:004318C9 XOR EAX,EAX <--- on s'en fiche |
015F:004318CB LEA EDX,[ESP+10] <--- on s'en fiche |
015F:004318CF PUSH EBX <--- on s'en fiche |
015F:004318D0 PUSH EBP <--- on s'en fiche |
015F:004318D1 PUSH ESI <--- on s'en fiche |
015F:004318D2 PUSH EDI <--- on s'en fiche |
015F:004318D3 MOV EDI,[ESP+000000EC] <--- on s'en fiche |
015F:004318DA REPNZ SCASB <--- on s'en fiche |
015F:004318DC NOT ECX <--- on s'en fiche |
015F:004318DE SUB EDI,ECX <--- on s'en fiche |
015F:004318E0 MOV EAX,ECX <--- on s'en fiche |
015F:004318E2 MOV ESI,EDI <--- on s'en fiche |
015F:004318E4 MOV EDI,EDX <--- on s'en fiche |
015F:004318E6 SHR ECX,02 <--- on s'en fiche |
015F:004318E9 REPZ MOVSD <--- on s'en fiche |
015F:004318EB MOV ECX,EAX <--- on s'en fiche |
015F:004318ED AND ECX,03 <--- on s'en fiche |
015F:004318F0 REPZ MOVSB <--- on s'en fiche |
015F:004318F2 LEA ECX,[ESP+20] |
015F:004318F6 PUSH ECX <--- "Lise_Grim" (1) |
015F:004318F7 CALL [USER32!CharUpperA] <--- CharUpperA |
015F:004318FD MOV AL,[ESP+20] <--- AL="L" de "LISE_GRIM" |
015F:00431901 LEA ESI,[ESP+20] <--- "LISE_GRIM" |
015F:00431905 TEST AL,AL <--- AL vide ? |
015F:00431907 LEA EDI,[ESP+20] <--- "LISE_GRIM" |
015F:0043190B JZ 0043192F <--- oui ? alors saute |
015F:0043190D MOVSX EDX,BYTE PTR [ESI] (2) |
015F:00431910 PUSH EDX <--- LISE_GRIM |
015F:00431911 PUSH 0044E62C <--- KSDF23489754..... |
015F:00431916 CALL 0043F5C0 <--- ? |
015F:0043191B ADD ESP,08 <--- ? |
015F:0043191E TEST EAX,EAX <--- ? |
015F:00431920 JZ 00431927 <--- ? |
015F:00431922 MOV AL,[ESI] <--- ? |
015F:00431924 MOV [EDI],AL <--- ? |
015F:00431926 INC EDI <--- ? |
015F:00431927 MOV AL,[ESI+01] <--- ? |
............. ... ............. |
............. ... ............. |
015F:004319F2 JNZ 004319FE <--- ? |
015F:004319F4 CMP EBX,17 <--- ? |
015F:004319F7 JGE 004319FE <--- ? |
015F:004319F9 MOV BYTE
PTR [EBP+00],2D <---
? |
015F:004319FD INC EBP <--- ? |
015F:004319FE MOV EBX,EDI <--- ? |
015F:00431A00 CMP EBX,18 <--- ? |
015F:00431A03 JL 004319A9 <--- ? |
015F:00431A05 MOV EAX,[ESP+000000F0] <--- on s'en fiche (3) |
015F:00431A0C MOV BYTE
PTR [EBP+00],00 <---
on s'en fiche (4) |
015F:00431A10 LEA ESI,[ESP+00000084] <--- bon serial |
------------------------------------MOVGEAR!.text+000309ED---------------------- |
|
(1) En passant tout avec TRACE OVER, on cherche un "ancrage" pour un début d'opération
sur le nom.
Ici, il est particulièrement flagrant : passer le nom en majuscule ( API CharUpperA ) qui est confirmé
si vous regardez ce qu'est devenu le nom après le passage de ce call. On a notre borne de début : 004318F6
(2) A partir d'ici, on voit que l'on commence à passer à quelquechose de "sérieux"
( enfin, il ne faut pas pousser, non plus. ).
Plus vous keygennerez d'applications, plus certaines choses deviendront clairs en terme d'instructions ou de successions
d'instructions en ASM.
Cette compréhension progressive vous permmettra de ripper encore moins de choses et d'aller plus vite dans
l'identification d'actions, etc...
(3) Ici, on ne prend pas cette instruction car elle ne modifie pas le serial qui est déjà
affiché. ( on le voit en passant dessus. )
(4) Cette instruction mets juste un "0" hexa ( fin de String ) derrière le Serial.
Dans cette mise en pratique du Rippage ASM, on va ripper de façon basique sans chercher à comprendre
le moindre calcul.
Interval de travail : [ 004318F6 = début
du passage en majuscules / 00431A10 = Apparition du serial ]
rippage de la routine
Afin de ne léser personne avec Screendump/Softice face
aux "debuggers en mousse" et aussi parce que c'est plus simple, on va ripper avec IDA.
Vous désassemblez donc movgear.exe avec IDA.
On va ripper notre Interval de travail : [ 004318F6
- 00431A10 ]
- Cliquez sur dans le menu sur " Jump > Jump to Adress " et mettez " 4318F6 ".
- Sélectionnez l'interval dans IDA + " Ctrl + C "
- Collez la sélection dans un fichier TXT.
- Enlevez les 2 instructions de fin qui ne servent à rien. ( en 00431A05 et 00431A0C
)
- Nettoyez le Rippage de ses colonnes adresses et opcodes.
- Si vous avez effacé les labels loc_*******, recommencez tout ! ( à quoi cela sert qu'on utilise
IDA, sinon ?! )
Vous obtenez le résultat suivant :
NB : les commentaires de IDA ( "
; xxxxxxx " ) sont enlevés juste
pour que cela soit plus clair sur ce tut, mais vous pouvez les laisser de votre côté.
push ecx
call ds:CharUpperA
mov al,
[esp+0E8h+sz]
lea esi, [esp+0E8h+sz]
test al, al
lea edi, [esp+0E8h+sz]
jz short
loc_43192F
loc_43190D:
movsx edx, byte ptr
[esi]
push edx
push offset
aKsdf234897544u
call _strchr
add esp,
8
test eax, eax
jz short
loc_431927
mov al,
[esi]
mov [edi],
al
inc edi
loc_431927:
mov al,
[esi+1]
inc esi
test al, al
jnz short
loc_43190D
loc_43192F:
mov byte
ptr [edi], 0
lea edi,
[esp+0E8h+sz]
or ecx,
0FFFFFFFFh
xor eax,
eax
repne scasb
not ecx
dec ecx
cmp ecx,
18h
jge short
loc_43195B
mov eax,
ecx
loc_431947:
mov edx,
eax
sub edx,
ecx
inc eax
cmp eax,
18h
mov dl,
byte ptr aKsdf234897544u[edx]
mov [esp+eax+0E8h+var_C9],
dl
jl short
loc_431947
loc_43195B:
mov edi,
offset aKsdf234897544u
or ecx,
0FFFFFFFFh
xor eax,
eax
xor esi,
esi
repne scasb
mov eax,
1
xor ebx,
ebx
sub eax,
offset aKsdf234897544u
mov [esp+0E8h+var_AF],
0
not ecx
mov [esp+0E8h+var_D0],
eax
mov eax,
offset aKsdf234897544u
dec ecx
dec eax
mov [esp+0E8h+var_D4],
eax
lea eax,
[esp+0E8h+sz]
dec eax
mov [esp+0E8h+var_64],
0
mov [esp+0E8h+var_D8],
ecx
lea ebp,
[esp+0E8h+var_64]
mov [esp+1Ch],
eax
jmp short
loc_4319AD
loc_4319A9:
mov eax,
[esp+1Ch]
loc_4319AD:
movsx eax, byte ptr
[eax+ebx+1]
lea edi,
[ebx+1]
push eax ; int
push offset
aKsdf234897544u
call _strchr
mov edx,
[esp+0F0h+var_D0]
mov ecx,
eax
add ecx,
edx
add esp,
8
lea eax,
[ecx-1]
imul eax, ecx
add eax,
esi
mov ecx,
[esp+0E8h+var_D4]
cdq
idiv [esp+0E8h+var_D8]
mov eax,
edi
inc edx
inc ebp
mov esi,
edx
mov dl,
[ecx+esi]
mov ecx,
6
mov [ebp-1],
dl
cdq
idiv ecx
test edx, edx
jnz short
loc_4319FE
cmp ebx,
17h
jge short
loc_4319FE
mov byte
ptr [ebp+0], 2Dh
inc ebp
loc_4319FE:
mov ebx,
edi
cmp ebx,
18h
jl short
loc_4319A9
lea esi,
[esp+0E8h+var_64] |
Vous pouvez copié-collé votre rippage ASM dans
votre source ASM de Keygen.
Le travail n'est pas terminé, mais on n'en est pas loin :)
rebranchements et identification
* Rebranchement du début.
Dans le Shareware, quand on arrive au début de notre routine, les registres sont déjà avec
des valeurs. ( nombre ,offsets, ... )
Ce qui nous intéresse, c'est d'avoir la même chose dans notre Keygen.
Déjà, on peut exclure ESP et EBP, qui sont rarement utilisés comme registre de calcul pour
une routine, exception faite pour la pile.
Toutefois, toutes les valeurs ne sont pas utiles à récupérer.
Pour voir cela, il suffit d'utiliser le debugger de tracer le début de la routine, de voir ce qui est pris
ou initialisé et d'être un peu logique.
-------------------------------------------------------------------------PROT32- |
015F:004318F6 PUSH ECX <--- utilise ECX |
015F:004318F7 CALL [USER32!CharUpperA] <--- initialise EAX |
015F:004318FD MOV AL,[ESP+20] |
015F:00431901 LEA ESI,[ESP+20] <--- initialise ESI |
015F:00431905 TEST AL,AL |
015F:00431907 LEA EDI,[ESP+20] <--- initialise EDI |
015F:0043190B JZ 0043192F |
015F:0043190D MOVSX EDX,BYTE PTR [ESI] <--- initialise EDX |
|
Il reste à voir pour EBX.
Et on voit que le registre EBX n'est pas utilisé jusqu'à :
-------------------------------------------------------------------------PROT32- |
015F:00431967 REPNZ SCASB |
015F:00431969 MOV EAX,00000001 |
015F:0043196E XOR EBX,EBX <--- initialise EBX |
015F:00431970 SUB EAX,0044E62C |
015F:00431975 MOV BYTE
PTR [ESP+39],00 |
|
|
Au final :
On a besoin de connaitre ECX au début de la routine.
On n'a pas besoin de connaitre EAX, EBX, EDX, ESI, EDI car la première chose que fait la routine avec eux,
est de leur donner une nouvelle valeur.
En outre, ECX = offset NOM au lancement, on ajoute au début de notre routine :
lea ecx, offset NOM
push ecx
call ds:CharUpperA
mov al,
[esp+0E8h+sz]
lea esi, [esp+0E8h+sz]
.... ..............
.... ..............
.... .............. |
Le rebranchement du début est fait.
* Rebranchement des constantes, tables...
L'avantage avec IDA, c'est que son désassemblage montre très rapidement les constantes, etc.
Si on reprend notre Rippage, on le voit vite :
.... ..........
.... ..........
push offset aKsdf234897544u
.... ..........
.... ..........
mov dl,
byte ptr aKsdf234897544u[edx]
.... ..........
.... ..........
mov edi,
offset aKsdf234897544u
.... ..........
.... ..........
sub eax,
offset aKsdf234897544u
.... ..........
.... ..........
mov eax,
offset aKsdf234897544u
.... ..........
.... ..........
push offset
aKsdf234897544u
.... ..........
.... .......... |
C'est la même constante. ( aKsdf234897544u )
On a donc une valeur à définir.
Pour l'obtenir, on revient sur IDA, on se place sur l'une des instructions ci-dessus et on clique sur cette constante.
On atterit sur la définition de la constante. Il n'y a qu'à la récupérer.
On ajoute cela à notre source :
aKsdf234897544u db 'KSDF234897544URTFBBMNZXCSDKA',0
lea ecx, offset
NOM
push ecx
call ds:CharUpperA
mov al,
[esp+0E8h+sz]
lea esi, [esp+0E8h+sz]
.... ..............
.... ..............
.... .............. |
* Rebranchement des variables.
On va utiliser la méthode de Presage
pour coller au maximum au Shareware.
Reprenons notre Rippage et identifions les variables qui nécessitent une définition.
Pour cela :
- repassez sous le debugger.
- localisez les variables comme [ESP + nombre].
- notez leurs emplacements en mémoire et ce qu'elles contiennent.
- pour les opérations ( mul, imul, div, idiv, ... ), notez le format ( byte, word, dword ...) qui est pris
en compte car IDA ne le fait pas ! ( important )
Avec Screendump, au moins, on n'a pas ce problème :P
-------------------------------------------------------------------------PROT32- |
015F:004318F6 PUSH ECX |
015F:004318F7 CALL [USER32!CharUpperA] |
015F:004318FD MOV AL,[ESP+20] <-- "6BF268" = offset NOM |
015F:00431901 LEA ESI,[ESP+20] <-- "6BF268" = déjà fait |
015F:00431905 TEST AL,AL |
015F:00431907 LEA EDI,[ESP+20] <-- "6BF268" = déjà fait |
............. .... ........ |
............. .... ........ |
015F:00431932 LEA EDI,[ESP+20] <-- "6BF268" = déjà fait |
............. .... ........ |
............. .... ........ |
015F:00431955 MOV [EAX+ESP+1F],DL <-- "6BF267" = 1 |
............. .... ........ |
............. .... ........ |
015F:00431975 MOV BYTE
PTR [ESP+39],00 <-- "6BF281" = 0 |
............. .... ........ |
015F:0043197C MOV [ESP+18],EAX <-- "6BF260" = 0 |
............. .... ........ |
............. .... ........ |
015F:00431987 MOV [ESP+14],EAX <-- "6BF25C" = 0 |
015F:0043198B LEA EAX,[ESP+20] <-- "6BF268" = déjà fait |
............. .... ........ |
015F:00431990 MOV BYTE
PTR [ESP+00000084],00 <--
"6BF2CC" = 0 |
015F:00431998 MOV [ESP+10],ECX <-- "6BF258" = 0 |
015F:0043199C LEA EBP,[ESP+00000084] <-- "6BF2CC" = déjà fait |
015F:004319A3 MOV [ESP+1C],EAX <-- "6BF264" = 0 |
............. .... ........ |
015F:004319A9 MOV EAX,[ESP+1C] <-- "6BF264" = déjà fait |
............. .... ........ |
............. .... ........ |
015F:004319C0 MOV EDX,[ESP+20] <-- "6BF260" = déjà fait |
............. .... ........ |
............. .... ........ |
015F:004319D3 MOV ECX,[ESP+14] <-- "6BF25C" = déjà fait |
............. .... ........ |
015F:004319D8 IDIV DWORD PTR [ESP+10] <-- "6BF258" = déjà fait
(1) |
............. .... ........ |
............. .... ........ |
015F:00431A10 LEA ESI,[ESP+00000084] <-- "6BF2CC" = déjà fait:serial |
|
(1) Attention, ici, c'est un DWORD qui subit l'opération. Il faut le reporter sur le Rippage issu
d'IDA.
Maintenant, continuons la méthode en remplaçant ce qu'il faut dans la source :
- définir les variables comme dans la mémoire.
- remplacer tout ce qu'il faut dans la source du Keygen.
aKsdf234897544u
db 'KSDF234897544URTFBBMNZXCSDKA',0
;
Remarques :
;
;
NOM = BUF_6BF268
; SERIAL = BUF_6BF2CC
BUF_6BF258 db 0,0,0,0 ; 6BF25C - 6BF258 = 4 bytes
BUF_6BF25C db 0,0,0,0 ; 6BF260 - 6BF25C = 4 "
BUF_6BF260 db 0,0,0,0 ; 6BF264 - 6BF260 = 4 "
BUF_6BF264 db 0,0,0 ; 6BF267 - 6BF264 = 3 "
BUF_6BF267 db 1 ; 6BF268 - 6BF267 = 1 "
BUF_6BF268 db 0,0,0,0,0,0,0,0,0,0 ; 6BF281 - 6BF268 = 19 "
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0
BUF_6BF281 db 0,0,0,0,0,0,0,0,0,0 ; 6BF2CC - 6BF281 = 75 "
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0,0,0,0,0,0
db 0,0,0,0,0
BUF_6BF2CC db 40 dup (0) ; <-- c'est moi qui donne ce nombre
;
je prévois large pour le serial
lea ecx, offset
BUF_6BF268 ; simplement remplacer "NOM"
push ecx
call ds:CharUpperA
mov al,
[BUF_6BF268]
lea esi, [BUF_6BF268]
test al, al
lea edi, [BUF_6BF268]
jz short
loc_43192F
loc_43190D:
movsx edx, byte ptr
[esi]
push edx
push offset
aKsdf234897544u
call _strchr
add esp,
8
test eax, eax
jz short
loc_431927
mov al,
[esi]
mov [edi],
al
inc edi
loc_431927:
mov al,
[esi+1]
inc esi
test al, al
jnz short
loc_43190D
loc_43192F:
mov byte
ptr [edi], 0
lea edi,
[BUF_6BF268]
or ecx,
0FFFFFFFFh
xor eax,
eax
repne scasb
not ecx
dec ecx
cmp ecx,
18h
jge short
loc_43195B
mov eax,
ecx
loc_431947:
mov edx,
eax
sub edx,
ecx
inc eax
cmp eax,
18h
mov dl,
byte ptr aKsdf234897544u[edx]
mov [BUF_6BF267 + eax], dl
jl short
loc_431947
loc_43195B:
mov edi,
offset aKsdf234897544u
or ecx,
0FFFFFFFFh
xor eax,
eax
xor esi,
esi
repne scasb
mov eax,
1
xor ebx,
ebx
sub eax,
offset aKsdf234897544u
mov [BUF_6BF281], 0
not ecx
mov [BUF_6BF260], eax
mov eax,
offset aKsdf234897544u
dec ecx
dec eax
mov [BUF_6BF25C], eax
lea eax,
[BUF_6BF268]
dec eax
mov [BUF_6BF2CC], 0
mov [BUF_6BF258], ecx
lea ebp,
[BUF_6BF2CC]
mov [BUF_6BF264], eax
jmp short
loc_4319AD
loc_4319A9:
mov eax,
[BUF_6BF264]
loc_4319AD:
movsx eax, byte ptr
[eax+ebx+1]
lea edi,
[ebx+1]
push eax ; int
push offset
aKsdf234897544u
call _strchr
mov edx,
[BUF_6BF260]
mov ecx,
eax
add ecx,
edx
add esp,
8
lea eax,
[ecx-1]
imul eax, ecx
add eax,
esi
mov ecx,
[BUF_6BF25C]
cdq
idiv DWORD
PTR [BUF_6BF258] ; ATTENTION A NE PAS OUBLIER DWORD !
mov eax,
edi
inc edx
inc ebp
mov esi,
edx
mov dl,
[ecx+esi]
mov ecx,
6
mov [ebp-1],
dl
cdq
idiv ecx
test edx, edx
jnz short
loc_4319FE
cmp ebx,
17h
jge short
loc_4319FE
mov byte
ptr [ebp+0], 2Dh
inc ebp
loc_4319FE:
mov ebx,
edi
cmp ebx,
18h
jl short
loc_4319A9
lea esi,
[BUF_6BF2CC] |
C'est presque fini !
La derniére chose a faire reste d'identifier les calls inconnus.
* Rebranchement de call
On regarde rapidement dans le Rippage à la recherche de calls mystérieux... et on en trouve plusieurs
fois le même :
.... ..............
call _strchr
.... ..............
call _strchr
.... .............. |
En cas de call mystérieux, on a 2 cas :
- on identifie l'action du call puis on essaye de le retranscrire dans le langage de son Keygen ou à trouver
une source sur Internet fesant la même chose.
ou
- on n'identifie pas l'action du call : on est obligé de ripper son contenu.
Pour mettre la cerise sur le gâteau, on fera semblant de ne pas comprendre ce call afin de montrer un Rippage
de call. :)
( Rassurez-vous c'est un copié-collé sans rien d'autre ! )
En 2 mn, montre en main, à partir de maintenant, on va terminer notre Keygen et pouvoir l'assembler !
( pour la petite information, ce call sert à savoir où un caractére donné se trouve
dans une String, mais fesons comme si on ne l'avait pas vu. )
On reprend IDA, on se place sur le call et on clique dessus et on tombe dans son désassemblage.
Là, ne faites pas dans la demi-mesure, rippez le avec toute sa définition.
Au final, vous aurez :
_strchr proc near
arg_0
= dword ptr 4
arg_4 = byte ptr 8
xor eax, eax
mov al, [esp+arg_4]
___from_strstr_to_strchr:
push ebx
mov ebx, eax
shl eax, 8
mov edx, [esp+4+arg_0]
test edx, 3
jz short loc_0_43F5EB
loc_0_43F5D8:
mov cl, [edx]
inc edx
cmp cl, bl
jz short loc_0_43F5B0
test cl, cl
jz short loc_0_43F634
test edx, 3
jnz short loc_0_43F5D8
loc_0_43F5EB:
or ebx, eax
push edi
mov eax, ebx
shl ebx, 10h
push esi
or ebx, eax
loc_0_43F5F6:
mov ecx, [edx]
mov edi, 7EFEFEFFh
mov eax, ecx
mov esi, edi
xor ecx, ebx
add esi, eax
add edi, ecx
xor ecx, 0FFFFFFFFh
xor eax, 0FFFFFFFFh
xor ecx, edi
xor eax, esi
add edx, 4
and ecx, 81010100h
jnz short loc_0_43F638
and eax, 81010100h
jz short loc_0_43F5F6
and eax, 1010100h
jnz short loc_0_43F632
and esi, 80000000h
jnz short loc_0_43F5F6
loc_0_43F632:
pop esi
pop edi
loc_0_43F634:
pop ebx
xor eax, eax
retn
loc_0_43F638:
mov eax, [edx-4]
cmp al, bl
jz short loc_0_43F675
test al, al
jz short loc_0_43F632
cmp ah, bl
jz short loc_0_43F66E
test ah, ah
jz short loc_0_43F632
shr eax, 10h
cmp al, bl
jz short loc_0_43F667
test al, al
jz short loc_0_43F632
cmp ah, bl
jz short loc_0_43F660
test ah, ah
jz short loc_0_43F632
jmp short loc_0_43F5F6
loc_0_43F660:
pop esi
pop edi
lea eax, [edx-1]
pop ebx
retn
loc_0_43F667:
lea eax, [edx-2]
pop esi
pop edi
pop ebx
retn
loc_0_43F66E:
lea eax, [edx-3]
pop esi
pop edi
pop ebx
retn
loc_0_43F675:
lea eax, [edx-4]
pop esi
pop edi
pop ebx
retn
_strchr endp |
Attention !!! Il y a un label qui n'est pas dans ce call,
mais juste au-dessus. ( erreur de compilation ou autre ... ) Il faut le ripper aussi car le call en a besoin.
( Si vous ne l'aviez pas vu, pas d'inquiétudes ; votre assembleur MASM, TASM, ... vous préciserait
qu'il manque un label. )
loc_0_43F5B0: ; label manquant
lea eax, [edx-1]
pop ebx
retn |
Vous ajoutez ce Rippage en plus à votre source....
et vous pouvez assembler :)
Il y a de fortes chances selon votre assembleur qu'une erreur bête apparaisse :
" invalid instruction operands "
C'est juste de ne pas oublier de rajouter les formats :
mov [BUF_6BF260], eax --> mov dword ptr [BUF_6BF260], eax
mov [BUF_6BF25C], eax --> mov dword ptr [BUF_6BF25C], eax
mov [BUF_6BF258], ecx --> mov dword ptr [BUF_6BF258], ecx
etc ...
mov eax, [BUF_6BF264] --> mov eax, dword
ptr [BUF_6BF264]
etc ...
Il faut préciser quel format ( byte, word, dword, etc ), la variable doit recevoir ou donner sinon l'assembleur
peut pas le deviner.
Et C'EST FINI !
Vous assemblez, vous corrigez s'il reste quelques erreurs de syntax ou autres ( ce qui ne doit plus être
le cas si vous avez suivi ce tut. )
Votre Keygen est prêt à être testé.
Dernier point : Le test est absolument INDISPENSABLE ! Cela a été précisé dans la partie
1 : On peut avoir l'apparence d'un vrai serial de généré et il est faux car une variable a
été mal défini, ou dans le nettoyage de la source, vous avez effacé aussi une instruction...
source finale
La source est normallement adjointe à ces tuts.
C'est compilable avec MASM. ( un debugger en mousse (1) :) )
conclusion
Ce tut conclue la " Transplatation Keygennique ".
La Partie 3 n'est juste que quelques trucs de base en ASM ( transformer une valeur registre en String, etc ) pour
ne pas se casser la tête à chercher comment programmer cela.
.
remerciements
Merci à The Analyst, DeeDee, aux membres des groupes
FFF et Know[l]edge et à tous ceux qui ont crée outils, tuts et Keygenmes ( Christal & GdT, Thigo,
Syntax, Roy|Fleur, Kahel et Fnaax que j'allais oublier :P,... ) pour faire progresser le Keygenning francophone.
(1) "Debugger en carton pâte" et "Debugger en mousse"
sont des marques déposées par BOFH.
BOFH : "Parce qu'il y en marre des debuggers en mousse."
|