Solution de Julio eL NeGRo keygenme2
par Mister MoG - 17/07/2003



Outils utilisés : WinDbg v6.1 (ou n'importe quel autre debugger)
Damn Hash Calculator v1.51
WinHex v10.9


1. Premières analyses
2. Recherche de la routine de calcul du serial
3. Réalisation du keygen
4. Salutations

1. Premières analyses ^haut
J'ouvre l'exécutable avec WinHex et me rend compte que le keygenme est compressé , les deux sections ont été renommé.
Il suffit de modifier les offsets 1b8 et 1e0 en UPX0 et UPX1 et ensuite faire upx -d pour avoir un dump fonctionnel (/bin/dump.exe).
Je charge le keygenme avec windbg et j'obtiens une boite de message me demandant de décharger softice, je n'utilise pas softice donc cette protection doit s'appliquer normalement à tous les debugger.
Je met un breakpoint à l'entrypoint du programme et vois ceci :

00401000 6800304000 push 0x403000
00401005 e84c050000 call image00400000+0x1556 (00401556) ; dexor les chaines
0040100a 684f104000 push 0x40104f
0040100f 64ff3500000000 push dword ptr fs:[00000000]
00401016 64892500000000 mov fs:[00000000],esp
0040101d bd00484342 mov ebp,0x42434800 ;BCHK
00401022 66b80400 mov ax,0x4
00401026 cc int 3
00401027 6a00 push 0x0

1) on remarque que toutes les chaines sont chiffrées.
2) la protection antidebugger utilisé est la méthode BCHK

On continue en traçant:

00401029 682f314000 push 0x40312f
0040102e 6812314000 push 0x403112
00401033 6a00 push 0x0
00401035 e88c0e0000 call image00400000+0x1ec6 (00401ec6) ;MessageBoxA
0040103a 648f0500000000 pop fs:[00000000]
00401041 83c404 add esp,0x4
00401044 53 push ebx
00401045 bbf61e4000 mov ebx,0x401ef6
0040104a 6a00 push 0x0
0040104c ffd3 call ebx ; ExitProcess
0040104e 5b pop ebx
0040104f c705a5314000a21e4000 mov dword ptr [image00400000+0x31a5 (004031a5)],0x401ea2

On met un nop aux adresses 0040104c et 0040104d pour ne pas quitter le processus, on resume et nous sommes à présent dans la fenêtre principale du keygenme.



2. Recherche de la routine de calcul du serial ^haut

Je place des breakpoint sur GetDlgItemTextA et GetWindowTextA, j'entre comme username "toddmog" et comme serial "999999999" (9), on clique sur register et on break sur GetWindowTextA :

00401476 e8330a0000 call image00400000+0x1eae (00401eae) ; GetWindowTextA
0040147b 83f806 cmp eax,0x6
0040147e 0f86b4000000 jbe image00400000+0x1538 (00401538) ; si serial <= 6 on part
00401484 6878324000 push 0x403278 ; pointeur
00401489 50 push eax ; longueur de username
0040148a 68b1314000 push 0x4031b1 ; pointeur
0040148f e8e6000000 call image00400000+0x157a (0040157a) ; hash


On mémorise les adresses qui sont en paramêtres, ensuite dans le call :

004015ce c70601234567 mov dword ptr [esi],0x67452301 ; sha1,md5,ripemd?
004015d4 c7460489abcdef mov dword ptr [esi+0x4],0xefcdab89
004015db c74608fedcba98 mov dword ptr [esi+0x8],0x98badcfe
004015e2 c7460c76543210 mov dword ptr [esi+0xc],0x10325476

On voit tout de suite l'initialisation d'un hash sha1, md5 ou ripemd donc on avance assez loin jusqu'à 00401db4 après le loop.

00401db4 8b7510 mov esi,[ebp+0x10] ss:0023:0012fd0c=00403278 ; hash
00401dba ff7608 push dword ptr [esi+0x8] ; formatage
00401dbd ff7604 push dword ptr [esi+0x4] ; ....
00401dc0 ff36 push dword ptr [esi] ; ....
00401dc2 6865324000 push 0x403265
00401dc7 ff7508 push dword ptr [ebp+0x8]
00401dca e8bb000000 call image00400000+0x1e8a (00401e8a) ; wsprintf

Le hash va être convertit en hexadécimal grâce à wsprintf.
On sort du call et on affiche le contenu de l'adresse 0x4031b1 qui était en paramêtre tout à l'heure :

004031b1 61 63 35 32 35 62 32 36-36 39 36 35 35 61 32 37 ac525b2669655a27
004031c1 37 36 32 65 63 33 37 63-61 66 33 64 66 37 39 66 762ec37caf3df79f
004031d1 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
004031e1 00 00 00 00 00 00 00 00-38 00 00 00 00 00 00 00 ........8.......
004031f1 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00403201 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00403211 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00403221 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

C'est notre hash en hexa, je lance damn hash calculator pour hasher la chaine "toddmog" :

SHA-160 : 20272A9CC15821ECA19C9519DF159B712837E200
SHA-256 : E82E2A82C17801C83B60C1F180B7592BABF0D3DA55D1F12AEF250FE1E6211834
SHA-384 : 5E093AE8FEE836257B21D991A99B0DF6CDEC962ED6530E298ED1A3D8D25159EE1FEA334A1D....
MD5 : AC525B2669655A27762EC37CAF3DF79F
RIPEMD-160 : 9036960028D8E3B89306A2E017211AEC3C53D7BC

Nous avons donc affaire à du md5.
note : ripemd-128 n'est apparement pas implémenté dans damn hash calculator.
Continuons :

00401494 33c0 xor eax,eax
00401496 33c9 xor ecx,ecx
00401498 33d2 xor edx,edx
0040149a 8a81b1314000 mov al,[ecx+0x4031b1] ; prend le caractère courrant du hash
004014a0 83f839 cmp eax,0x39
004014a3 7607 jbe image00400000+0x14ac (004014ac) ; si <= 0x39 on ne retiens pas ce caractère
004014a5 8882ed314000 mov [edx+0x4031ed],al ; place le caractère courrant dans 0x4031ed
004014ab 42 inc edx
004014ac 41 inc ecx
004014ad 80b9b131400000 cmp byte ptr [ecx+0x4031b1],0x0
004014b4 75e4 jnz image00400000+0x149a (0040149a)

Après cette routine on déduit que du hash il ne restera que les caractères dont les valeurs ascii sont supérieurs à 0x39, pour moi : acbaeccafdff (on appellera ça hash_s).
Ensuite le programme fait un deuxième GetWindowTextA pour récupèrer notre serial et on voit ceci :

004014c9 68ed314000 push 0x4031ed
004014ce e8350a0000 call image00400000+0x1f08 (00401f08) ; lstrlen
004014d3 5b pop ebx
004014d4 3bd8 cmp ebx,eax
004014d6 7560 jnz image00400000+0x1538 (00401538) ; si hash_s est != longueur serial on part

Je n'ai entré que 9 char au lieu de 12, je retape mon serial en entrant 12 fois le caractère 9 et je reviens à l'étape ou nous sommes.

004014da 33c9 xor ecx,ecx
004014dc 8a8129324000 mov al,[ecx+0x403229] ; caractère courant du serial dans al
004014e2 3c30 cmp al,0x30
004014e4 7c52 jl image00400000+0x1538 (00401538) ; < 0x30 on part
004014e6 3c39 cmp al,0x39
004014e8 7f4e jg image00400000+0x1538 (00401538) ; > 0x39 on part
004014ea 41 inc ecx
004014eb 80b92932400000 cmp byte ptr [ecx+0x403229],0x0
004014f2 75e8 jnz image00400000+0x14dc (004014dc)

A partir d'ici nous savons donc que notre serial ne doit se composer que de chiffres.
Nous arrivons à présent la :

004014f4 33c0 xor eax,eax
004014f6 b930000000 mov ecx,0x30 ; ecx=0x30
004014fb 33db xor ebx,ebx
004014fd 8a8329324000 mov al,[ebx+0x403229] ; al = char courant de serial
00401503 99 cdq
00401504 f7f9 idiv ecx ; edx=eax%0x30
00401506 8a83ed314000 mov al,[ebx+0x4031ed] ; al = char courant hash_s
0040150c 03c2 add eax,edx ; eax=eax+edx
0040150e 83f866 cmp eax,0x66
00401511 7525 jnz image00400000+0x1538 (00401538) ; si eax!=0x66 on sort
00401513 43 inc ebx
00401514 80bb2932400000 cmp byte ptr [ebx+0x403229],0x0
0040151b 75e0 jnz image00400000+0x14fd (004014fd)

Resumons rapidement cette routine (1 passe) :
- met le caractère courant du serial dans al => eax=9
- divise eax par 0x30 et place le reste de la division dans edx => edx=9
- récupère le caractère courant du hash retravaillé et le place dans al => eax=0x61
- ajoute edx à eax et compare avec 0x66 => eax=0x6a

Donc il nous suffit de faire 0x66 - 0x61 pour obtenir le bon numero c'est à dire 5.
On va un peu plus loin dans le programme et on se rend compte qu'il n'y a plus de routine pour calculer le serial.



3. Réalisation du keygen ^haut
Maintenant que nous avons tous les éléments nous pouvons codé un keygen, il vous faut une implémentation de l'algorithme md5.
J'ai écrit ce keygen en c et j'ai utilisé une implementation trouvée sur http://www.cr0.net:8040/code/crypto/md5.php

void GenerateKey(char *name, char *serial)
{
        int i=0;
        struct md5_context ctx;
        unsigned char md5sum[16];
        char buf[32], *p;

        if (lstrlen(name) <= 6)
        {
                lstrcpy(serial, "Pas assez de caractère man");
                return;
        }

        md5_starts(&ctx);
        md5_update(&ctx, (uint8 *)name, lstrlen(name));
        md5_finish(&ctx, md5sum);

        for (i=0; i<16; i++)
                wsprintf(buf+i*2, "%02x", md5sum[i]);
        p = serial;
        for (i=0; i<sizeof(buf); i++)
                if (buf[i] > 0x39)
                        wsprintf(p++, "%d", 0x66-buf[i]);
        (*p++) = NULL;
        return;
}


Le keygen se trouve dans /bin/keygen.exe.



4. Salutations ^haut
A toute la Team FFF,
aux gens sympa du chan #fff sur irc.recycled-irc.org,
... et aussi (sans ordre particulier) :
RaGeZ, Striff, JR, luckylyk, artoo the wu, Normak, AnAc et Lagouna et à vous qui lisez ce tutorial.
Je vous souhaites une bonne journée/soirée.




[EOF]