Transplantation Keygennique
Partie 2 : Mise en pratique
mars 2003
[ Lise_Grim & Presage ]

:: menu ::
:: introduction ::
::
localisation de la routine ::
::
désenregistrement ::
::
délimitation ::
::
rippage de la routine ::
::
rebranchement ::
::
source finale ::
::
conclusion ::
::
greetings ::


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."

Lise_Grim / Know[l]edge & Presage / FFF - 2003