Allez on vous gâte !!! (enfin je vous gâte puisque je suis tout seul). Comme à chaque vacances, je fais pas mal de cracking (because pas accès au net) et je progresse pas mal à chaque fois. Ca m'a permis aux dernières vacances d'étudier une technique de cracking 1000 fois plus élégante que la modification d'un prog : le keygening. C'est juste le fait de trouver en code d'enregistrement valide ou mieux : de créer un prog qui vas générer des serials valides.
Je cherchais les sites de grosses team de cracking comme Razor1911, Deviance, Fairlight, Laxity, Eminence (ben non il font pas que des caleçons... Arf !!! Je suis trop fort, a ouais trop content de ma connerie... ha !! enfin bon) et puis TNT Crackers. Et comme la plupart n'ont pas de sites officiels, je suis tombé sur d'autres sites où ils proposaient des crackmes dont j'en ai pris 3, tous à keygenner. Ou trouver ces crackmes donc ? sur crakmes.cjb.net Bon on commence tout doux avec celui de Duelist.
Comme d'habitude avec les progs à cracker, on cheche l'erreur. Ici on n'a pas le choix : ya que deux boîtes de textes qui sont user name et registration code. Puis en dessous ya 2 boutons : check et close. Déjà on se dit qu'à priori le prog ne vérifie pas le serial au fur et à mesure donc c plus facile :-)
Bon comme ce prog est provocateur (il attend que ça de se faire cracker, salope vas !!!) on entre un nom au pif (Sirius pour moi) et pi un serial au hazard, par exemple 205 car c un sacré numéro (enfin parait). On clique sur check et là on se prend une veste : "Your registration info is invalid..." etc etc. Donc on lance Win32Dasm et là... merde pas de string data ref... Bon je vous avais dit comment faire avec un héditeur hexa et un peu de calcul. Mais ya plus simple, vous cliquez sur le bouton Data Hex et vous avez ça :

:00402088 6E 20 28 77 6F 72 6B 69 n (worki
:00402090 6E 67 20 6F 6E 65 29 20 ng one)
:00402098 74 6F 20 64 75 65 6C 69 to dueli
:004020A0 73 74 40 62 65 65 72 2E st@beer.
:004020A8 63 6F 6D 21 00 20 59 6F com!. Yo
:004020B0 75 72 20 72 65 67 69 73 ur regis
:004020B8 74 72 61 74 69 6F 6E 20 tration
:004020C0 69 6E 66 6F 20 69 73 20 info is
:004020C8 69 6E 76 61 6C 69 64 2E invalid.
:004020D0 2E 2E 20 4E 6F 74 65 20 .. Note
:004020D8 74 68 61 74 20 6D 6F 73 that mos
:004020E0 74 20 6F 66 20 74 68 65 t of the
:004020E8 20 73 70 65 63 69 61 6C special
:004020F0 20 63 68 61 72 73 20 6D chars m
:004020F8 61 79 20 72 61 69 73 65 ay raise
:00402100 20 72 65 67 69 73 74 72 registr
:00402108 61 74 69 6F 6E 20 70 72 ation pr


Oui je sais vous êtes impressionné ;-) Bon fini les conneries. Notre phrase comme en a8, a9, aa, ab... en AE !! Avec la lettre 'Y' soit 59 en hexa. Donc on va chercher dans le prog un "push 004020AE". Non seulement on le trouve mais en plus yen a qu'un. Si vous suivez pas je vous conseille de lire les anciens LOTFREE car ce numéro est déjà bien long et je rajouterais pas d'explications. Le push est à l'adresse 00401224 :

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040113F(C), :00401148(C), :00401163(C), :0040116B(C), :004011B1(C)
|:004011B6(C), :004011F9(U)
|
:0040121A 6800200000 push 00002000
:0040121F 6801204000 push 00402001
:00401224 68AE204000 push 004020AE
:00401229 6A00 push 00000000

* Reference To: USER32.MessageBoxA, Ord:0000h
|
:0040122B E836010000 Call 00401366
:00401230 B800000000 mov eax, 00000000
:00401235 E9DFFEFFFF jmp 00401119


On peut voir qu'en dessous ya un appel à USER32.MessageBoxA qui est donc la fonction qui récupère ce message. Au dessus on voit les references des sauts qui ont pu nous amener ici : 6 conditionnels et 1 inconditionnel. Pas de panique, en général ils se trouvent côtes à côtes et sont justes des vérifications minimes tels que presence du login, presence du pass, longueur du login et du pass, validité du login (par exemple il faut pas qu'il contienne de caractères spéciaux) et puis validité du pass.
Au étudie les premiers jumps :

* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
|
:00401132 E841020000 Call 00401378
:00401137 A3AF214000 mov dword ptr [004021AF], eax
:0040113C 83F800 cmp eax, 00000000 //si eax=0
:0040113F 0F84D5000000 je 0040121A //bad boy
:00401145 83F808 cmp eax, 00000008 //si eax>8
:00401148 0F8FCC000000 jg 0040121A
//bad boy
:0040114E 8BF0 mov esi, eax //important pour la suite

Perso j'utilise beaucoup le débuggeur de Win32Dasm (avec les break : Ctrl+click_gauche) et après quelques tests, on comprend que le SendDlgItemMessageA est une fonction qui renvoie la longeur de notre login dans eax. Avec l'option documentation API et un Step Into on a :

API LONG Arg00 = SendDlgItemMessageA(Arg01,Arg02,Arg03,Arg04,Arg05)
API Address=00401378, API Return Address=00401137
Arg01 = (HWND) 000000b8 (Window"Duelist's Crackme #4")
Arg02 = (int) 00000003
Arg03 = (uMSG) 0000000e ->WM_GETTEXTLENGTH
Arg04 = (WPARAM) 00000000
Arg05 = (LPARAM) 00000000

RESULT for API SendDlgItemMessageA
Arg00 = (LONG) 00000006
Arg01 = (HWND) 000000b8 (Window"Duelist's Crackme #4")
Arg02 = (int) 00000003
Arg03 = (uMSG) 0000000e ->WM_GETTEXTLENGTH
Arg04 = (WPARAM) 00000000
Arg05 = (LPARAM) 00000000


Là ça me paraît plus parlant non ? Le résultat peut se voir dans Arg00 soit une longueur de 6 pour "Sirius". On regarde notre comparaison au dessus --> si longueur(login) est = 0 ou >8 alors erreur. On a déjà niqué 2 conditions. Juste après on a :

* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
|
:0040115B E818020000 Call 00401378
:00401160 83F800 cmp eax, 00000000
:00401163 0F84B1000000 je 0040121A
:00401169 3BF0 cmp esi, eax
:0040116B 0F85A9000000 jne 0040121A

Pareil on se rend compte que c'est la même fonction mais cette fois elle renvoie 3 qui est comme par hazard la longueur de notre serial. Ensuite on vérifie si on a entré quelque chose (cmp eax,0) et puis on compare la longueur du serial avec celle du login qui était en mémoire dans esi. Conclusion : le serial doit faire la même longueur que le login. Déjà 4 saut de niqué ! On entre donc un serial du style 123456. Et on relance le débugueur. On met un break sur la zone :

:00401171 6860214000 push 00402160
:00401176 6A08 push 00000008
:00401178 6A0D push 0000000D
:0040117A 6A03 push 00000003
:0040117C FF7508 push [ebp+08]

* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
|
:0040117F E8F4010000 Call 00401378

Le prog appelle alors une autre fonction (c'est le même call mais pas la même fonction, un peu comme les interruptions et les services ;-) :

:00401171 6860214000 push 00402160
:00401176 6A08 push 00000008
:00401178 6A0D push 0000000D
:0040117A 6A03 push 00000003
:0040117C FF7508 push [ebp+08]

* Reference To: USER32.SendDlgItemMessageA, Ord:0000h
|
:0040117F E8F4010000 Call 00401378

et les APIs donnent :

API LONG Arg00 = SendDlgItemMessageA(Arg01,Arg02,Arg03,Arg04,Arg05)
API Address=00401378, API Return Address=00401184
Arg01 = (HWND) 00000864 (Window"Duelist's Crackme #4")
Arg02 = (int) 00000003
Arg03 = (uMSG) 0000000d ->WM_GETTEXT
Arg04 = (WPARAM) 00000008
Arg05 = (LPARAM) 00402160

RESULT for API SendDlgItemMessageA
Arg00 = (LONG) 00000006
Arg01 = (HWND) 00000864 (Window"Duelist's Crackme #4")
Arg02 = (int) 00000003
Arg03 = (uMSG) 0000000d ->WM_GETTEXT
Arg04 = (WPARAM) 00000008
Arg05 = (LPARAM) 00402160

Comme son nom l'indique cette fonction lit une chaîne de caractères, met le résultat à l'adresse donnée en argument : 00402160
Et de plus, la longueur de la chaine est renvoyée. On clique sur Data Hex (comme tout à l'heure et on apperçoit entre autres notre login à 00402160. Ensuite c'est la même fonction mais avec le serial qui se met en 00402179.
Ensuite on tombe sur un bon morceau de code :

Routine de génération du password :
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011D9(U)
|
:0040119C 41 inc ecx //pointeur sur le username et sur le serial
:0040119D 0FBE8160214000 movsx eax, byte ptr [ecx+00402160] //met le car en cours dans eax
:004011A4 83F800 cmp eax, 00000000 //regarde si la chaine est terminée
:004011A7 7432 je 004011DB //si c'est le cas on arrête
:004011A9 BEFFFFFFFF mov esi, FFFFFFFF
:004011AE 83F841 cmp eax, 00000041 //si le car < 'A' =>Bad Guy
:004011B1 7C67 jl 0040121A
:004011B3 83F87A cmp eax, 0000007A //si le car > 'z' =>Bad Guy
:004011B6 7762 ja 0040121A
:004011B8 83F85A cmp eax, 0000005A //si le car < 'Z'
:004011BB 7C03 jl 004011C0 //on passe a la cle
:004011BD 83E820 sub eax, 00000020 //sinon on passe en majuscules

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004011BB(C), :004011CA(C)
|
:004011C0 46 inc esi //pointeur sur les chaines de calcul
:004011C1 0FBE9617204000 movsx edx, byte ptr [esi+00402017]
:004011C8 3BC2 cmp eax, edx //compare le car du user name avec celui de la chaine
:004011CA 75F4 jne 004011C0 //on recommence jusqu'à ce que ça coincide
:004011CC 0FBE863C204000 movsx eax, byte ptr [esi+0040203C] //l'indice du car dans la chaine de calcul
:004011D3 898194214000 mov dword ptr [ecx+00402194], eax //correspond à l'indice du car dans la seconde chaine (fabrication du serial)
:004011D9 EBC1 jmp 0040119C

Dans cette routine on s'apperçoit que si un des caractères du login n'est pas une lettre alors on a un message d'erreur. De plus si c'est une minuscule alors on la transforme en majuscule. On remarque deux adresses mémoires que l'on avait pas vu auparavant qui sont :

Address: 00402017 is in Module: DUE-CM4.EXE
char[035]:"A1LSK2DJF4HGP3QWO5EIR6UTYZ8MXN7CBV9"
DWORD:534c3141, WORD:3141, BYTE:41
CODE: inc ecx
---------------------------------------------------
Address: 0040203C is in Module: DUE-CM4.EXE
char[035]:"SU7CSJKF09NCSDO9SDF09SDRLVK7809S4NF"
DWORD:43375553, WORD:5553, BYTE:53
CODE: push ebx

Si la lettre en cours est en majuscule alors on peut calculer. Voici un exemple. Premier caractère de la chaîne ("Sirius") = 'S'. On cherche se position dans la chaîne 00402017. La lettre est la 4ème. On récupère alors la 4ème lettre de la seconde chaîne de calcul : 'C'. Ensuite avec les incrémenteurs (esi,ecx) on passe au caractère suivant. On passe à la seconde lettre : 'i'. On la met en majuscule, on cherche dans la chaîne de calcul1 sa position : 20. Puis on prend la 20ème lettre de la chaîne de calcul2, ce qui nous donne : '0'. Et ainsi de suite ; à la fin on obtient la chaine "C090DC". Mais à priori on ne sait pas à quoi elle sert (même si on devine). On le sait vraiment là :

routine de vérification du password (suite directe du prog):
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004011A7(C)
|
:004011DB FF35AF214000 push dword ptr [004021AF]
:004011E1 6894214000 push 00402194 //met le serial genere sur la pile
:004011E6 6879214000 push 00402179 //met votre password sur la pile
:004011EB E854000000 call 00401244 //appelle la fonction de comparaison
:004011F0 83F801 cmp eax, 00000001 //resultat dans eax
:004011F3 0F84DEFEFFFF je 004010D7 //eax=1 =>Good Guy
:004011F9 EB1F jmp 0040121A //=>Bad Guy

Ici on a droit à une bête fonction strcmp qui renvoie 1 si les 2 chaînes passées en arguments sont identiques.
Dans ce cas, la fonction de calcul est très simple et je ne met pas les sources d'un keygen pour cet exemple. On peut aussi bien utiliser le C que le pascal, le php et même le javascript.