Cours n°16

Brute-Forcer un crackme



Objectif :

1° Trouver le bon serial en utilisant une méthode par Brute-Force uniquement en modifiant le crackme.

Niveau :

Moyen - Elevé

Ce qu'il vous faut :

- OllyDbg et les quelques connaissances à son propos que j'ai données dans le cours précédent.

- Le crackme (30 Ko)



Introduction

    L'attaque par brute force est une technique employée généralement lorsque le serial ne peut pas se trouver à la main (ou de tête, ça dépend avec quoi vous réfléchissez d'habitude), le brute force va donc tester toutes les solutions possibles jusqu'à trouver la bonne. C'est efficace mais cela peut aussi être très long, en effet si le mot de passe cherché est assez long (environ plus de 8 lettres) il va falloir des jours, des mois voire des années pour le trouver. Heureusement pour nous, l'auteur de ce crackme n'a pas été trop méchant et a mis un serial d'une longueur correct.

    Ce crackme est tiré d'un site de challenges, le but du jeu : le programme doit nous donner le mot de passe du défi. Mais pour qu'il nous le donne il faut rentrer le bon serial. Donc théoriquement si on trouve uniquement le mot de passe sans le serial c'est bon. Mais nous allons voir qu'ici il faut obligatoirement l'un pour avoir l'autre.

    Ne sachant pas vraiment programmer en assembleur, on ne va pas créer un programme pour brute-forcer le crackme mais utiliser le crackme afin qu'il se brute-force lui-même. Bon alors je vous préviens ce que je vais faire ne sera sans doute pas beau à voir (surtout pour les pros de la programmation en assembleur ;-) ) mais ce qui compte c'est qu'on arrive à trouver la solution. C'est à dire qu'il y a sûrement plus rapide, plus simple peut-être, mais vous aurez l'occasion d'y réfléchir après avoir lu ce tutorial.

    Petit rappel : Qu'est-ce qu'une API ? "API" est l'acronyme de "Application Programming Interface". C'est une interface de programmation d'applications, contenant un ensemble de fonctions courantes de bas niveau, bien documentées, permettant de programmer des applications de « Haut Niveau ». On obtient ainsi des bibliothèques de routines, stockées par exemple dans des DLL ou des NLM (définition du site tout-savoir.net).

    Dernière petite chose avant de commencer : pour ce cours comme pour le précédent nous allons utiliser OllyDbg car WinDasm est vieux et ça fait ringard de cracker avec ;-). Plus sérieusement OllyDbg est beaucoup plus efficace que WinDasm et de plus ce dernier n'évolue plus. Donc mettez WinDasm à la poubelle et prenez donc OllyDbg avec vous lors de vos prochaines soirées de cracking ! :-)


1° Prise de connaissance du crackme

    On exécute le crackme pour voir de quoi il retourne. On voit qu'il faut taper un "keyword". Comme on est poli on tape "bonjour" : "Error !" soit-disant car le mdp (mot de passe) ne fait pas le bon nombre de caractères. En tâtonnant on trouve qu'il doit faire 5 caractères, on retrouvera ce résultat dans OllyDbg par la suite. En tapant un mot de 5 lettres on a le droit à un autre message d'erreur toujours en anglais nous disant cette fois que nous aurons plus de chance la prochaine fois (ça impressionne cette maîtrise de la langue anglaise quand même avec la traduction instantanée que je viens de faire là :-p ). En validant ce message le crackme se ferme. Rien de plus énervant pour cracker ce genre de crackme car en l'analysant on a besoin souvent de revalider le serial pour "refaire un tour". De plus lorsque l'on fait des modifications dans le programme, en le réinitialisant dans OllyDbg celles ci disparaissent. Donc c'est la première chose que nous allons faire : empêcher le crackme de se fermer.

    Pour cela on ne va pas trop se prendre la tête. En ouvrant OllyDbg la première ligne sur laquelle on tombe c'est l'EntryPoint (en 401000) on pourrait donc le faire revenir ici après l'affichage du message d'erreur. Cela aurait pour effet de recréer une nouvelle fenêtre du crackme mais ce serait déjà nettement moins gênant. Mais tentons d'affiner un peu ce saut. En descendant un petit peu on aperçoit les APIs (dans la partie 'Comment' à droite des instructions en assembleur et notées en rouge normalement) LoadIconA, SendMessageA et SendDlgItemMessageA qui correspondent à l'affichage du crackme, on va donc se mettre un peu après. L'instruction suivant la 3ème de ces API est un JMP qui mène en 401141. On essaiera donc cette destination. Maintenant que celle-ci a été choisie il va falloir déterminer le lieu de départ. En regardant vite fait encore un peu en dessous on trouve 3 messagebox :

    004010F5 . 68 44304000 PUSH CrackIt.00403044 ; |Text = "The Solution perhaps is 'AzulGranaEsMiColor'" --> Oh mais serait-il la solution que l'on cherche ? ... Que vous êtes naïf ! ;-) Cela serait beaucoup trop facile sinon. On va voir que "AzulGranaEsMiColor" sera décrypté en fonction du serial, c'est pour cela qu'il faut trouver le bon serial.

    00401112 . 68 ED304000 PUSH CrackIt.004030ED ; |Text = "The Keyword is not n characters long" --> Si le serial ne fait pas le bon nombre de caractères.

    0040112A . 68 B8304000 PUSH CrackIt.004030B8 ; |Text = "Best luck the next time :D" --> Le Bad Boy : si ce n'est pas le bon serial.



    Pour le moment c'est uniquement du dernier qu'on a besoin car c'est après celui-ci que le crackme se ferme et en effet une API EndDialog se trouve juste après. C'est donc ici qu'il va falloir sauter.

00401137 6A 00 PUSH 0
00401139 FF75 08 PUSH DWORD PTR SS:[EBP+8]
0040113C . E8 AF000000 CALL <JMP.&user32.EndDialog> ; \
EndDialog

    On va donc tout simplement remplacer ce morceau par un JMP 00401141. Il va donc rester des instructions qui ne servent à rien car le JMP va juste remplacer les 2 bytes (octets) du PUSH 0, mais de toute façon il ne passera plus par ici donc on peut laisser, ce n'est pas gênant. Pour modifier la ligne on double clique sur l'instruction (pour info si vous double-cliquez sur la partie commentaire vous pourrez en mettre un commentaire, surprenant non ?, très pratique pour se repérer parfois et sur le code en hexa cela pose un BP. Je rappelle également que BP n'est pas une Baguette de Pain mais un BreakPoint). Remarquez qu'on aurait très bien pu nopper toute l'API car la ligne 401141 se trouve juste après, mais alors dans ce cas vive la finesse ! Après modifications on a donc :

00401137 /EB 08 JMP SHORT 00401141
00401139 |FF75 08 PUSH DWORD PTR SS:[EBP+8]
0040113C . |E8 AF000000 CALL <JMP.&user32.EndDialog> ; \
EndDialog

    Une fois cela fait on va enregistrer le fichier, pas forcément car c'est utile, quoique si le programme est réinitialiser on perdra la modification faite, mais surtout pour savoir comment faire pour ceux qui découvrent encore un peu OllyDbg. On fait donc clic droit puis 'Copy to executable' -> 'All modifications', on clique sur Copy All. Là une petite fenêtre s'ouvre on fait clic droit dedans et 'Save File' et on enregistre sous un autre nom de préférence pour avoir une sauvegarde. Maintenant on va pouvoir s'attaquer aux choses sérieuses.




2° Analyse du crackme

    On s'intéresse désormais de plus près aux 3 msgbox qu'on vient de rencontrer :

004010E7 . 3D 9A020000 CMP EAX,29A
004010EC . 75 16
JNZ 00401104 --> Si EAX=29A alors continue sinon on saute au dessus de cette msgbox

004010EE . 6A 30 PUSH 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004010F0 . 68 11304000 PUSH 00403011 ; |Title = "
Congratulations!"
004010F5 . 68 44304000 PUSH 00403044 ; |Text = "
The Solution perhaps is 'AzulGranaEsMiColor'" --> Good boy :)
004010FA . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
004010FD . E8 00010000 CALL <JMP.&user32.MessageBoxA> ; \
MessageBoxA

00401102 . EB 33
JMP 00401137 --> revient en 401141 au "début" du programme
00401104 > 3D 4D010000 CMP EAX,14D
00401109 . 75 18
JNZ 00401123 --> si EAX=14D alors continue sinon on saute au dessus de cette msgbox

0040110B . 6A 10 PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
0040110D . 68 D3304000 PUSH 004030D3 ; |Title = "
Uhhhhhhhhhhhhhh!!!!!!!!!!"
00401112 . 68 ED304000 PUSH 004030ED ; |Text = "
The Keyword is not n characters long" --> mauvaise longueur du serial
00401117 . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
0040111A . E8 E3000000 CALL <JMP.&user32.MessageBoxA> ; \
MessageBoxA

0040111F . EB 20
JMP 00401141--> revient en 401141 au "début" du programme
00401121 . EB 14 JMP 00401137

00401123 > 6A 10 PUSH 10 ; /Style = MB_OK|MB_ICONHAND|MB_APPLMODAL
00401125 . 68 7A304000 PUSH 0040307A ; |Title = "
Uhhhhhhhhhhhhhh!!!!!!!!!!"
0040112A . 68 B8304000 PUSH 004030B8 ; |Text = "
Best luck the next time :D" --> Bad Boy :(
0040112F . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hOwner
00401132 . E8 CB000000 CALL <JMP.&user32.MessageBoxA> ; \
MessageBoxA

00401137 > EB 08 JMP 00401141

 

    C'est donc avec la valeur de EAX que le crackme détermine si le serial est bon ou pas. A partir de ça il est facile de lui faire afficher le bon message, mais le problème comme je l'ai dit c'est qu'il ne nous donnera pas le bon mdp car ce dernier sera calculé en fonction du mauvais serial que nous aurons rentré. Vous n'avez qu'à essayer vous verrez bien qu'on obtient un mdp qui n'a pas de sens.

    Juste au dessus du passage que je viens de détailler il y a une API intéressante :

004010CE . 6A 08 PUSH 8 ; /Count = 8
004010D0 . 68 08304000 PUSH 00403008 ; |Buffer = 00403008
004010D5 . 68 EA030000 PUSH 3EA ; |ControlID = 3EA (1002.)
004010DA . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004010DD . E8 14010000 CALL <JMP.&user32.GetDlgItemTextA> ; \
GetDlgItemTextA

004010E2 . E8 60000000
CALL 00401147

    L'API GetDlgItemTextA permet de récupérer ce qui a été tapé dans une boite de dialogue et ici par conséquent cela doit être notre serial. Et, entre cette API et la première comparaison de EAX, il y a un CALL. Je ne vous cacherai pas que le serial est bien entendu vérifié dans ce CALL. Pour se rendre à l'adresse du CALL il y a deux façons : le bus ou le métro ! ... Je disais donc il y a deux façons : soit on regarde l'adresse de destination, ici 401147, et on y va à la main (bon vous avez de la chance car ici ce n'est pas loin) ou alors on est malin et on appuie sur Entrée après avoir sélectionné la ligne du CALL.

    Au début du CALL il y a :

0040114B |. 68 08304000 PUSH 00403008 ; /String = ""
00401150 |. E8 CB000000 CALL <JMP.&kernel32.lstrlenA> ; \
lstrlenA

00401155 |. 83F8 05
CMP EAX,5
00401158 |. 74 13
JE 0040116D

    L'API lstrlenA permet de mesurer la taille d'une chaîne de caractères, ici il s'agit du serial. Pour vous le prouver posez donc un BP (avec F2 ou en double-cliquant sur le numéro de la ligne) sur la ligne du PUSH en 40114B. Lancez le programme avec F9, on tape un serial bidon "1234" de 4 caractères par exemple (enfin si mes calculs sont bons :-) ). Il breake normalement sur le push et vous voyez apparaître.

0040114B |. 68 08304000 PUSH 00403008 ; /String = "1234"
00401150 |. E8 CB000000 CALL <JMP.&kernel32.lstrlenA> ; \
lstrlenA

    Bon vous me croyez maintenant ? ;-) On notera au passage que notre serial est stocké en 403008, vous pouvez le voir dans la fenêtre du bas à cette adresse, cela aura son importance pour la suite.

     Pendant qu'on est en débogage on va continuer avec. Les raccourcis sont les mêmes que pour le debuggueur de WinDasm c'est à dire : F7 Step into et F8 Step Over. On avance avec F8 (pour éviter de rentrer dans le CALL de l'API) jusqu'au CMP EAX, 5. Là on voit bien que EAX (en haut à droite dans la fenêtre des registres) est égal à 4 soit le nombre de caractères de notre serial. Et donc 4 étant différent de 5 (enfin jusqu'à présent c'est le cas il me semble) le JE ne saute pas et on va continuer tranquillement, après avoir mis EAX à 14D, vers un RETN en 40116C qui a pour but de nous faire sortir du CALL. Ainsi comme EAX = 14D nous allons avoir droit au message comme quoi il n'y a pas le bon nombre de caractères.

    En revanche si le serial fait 5 caractères on va en 40116D. Bon alors maintenant on va passer la vitesse supérieure.

0040116D |> PUSHAD --> sauvegarde tous les registres en les mettant sur la pile
0040116E |. XOR EAX,EAX
--> mise à 0 de EAX
00401170 |. XOR EBX,EBX
--> idem pour EBX
00401172 |. MOV ESI,00403008 ; ASCII "
12345" --> met notre faux serial dans ESI
00401177 |. MOV AL,BYTE PTR DS:[ESI]
--> 1ere lettre du faux serial dans AL
00401179 |. MOV ECX,1
--> initialisation du compteur

0040117E |> /MOV BL,BYTE PTR DS:[ESI+1] --> 1ere lettre du faux serial dans BL
00401181
|. |CDQ --> convertit le mot signé de EAX en un quadruple mot dans <EDX:EAX>
00401182
|. |MUL EBX --> multiplie EAX par EBX, le résultat va dans EAX
00401184
|. |INC ESI --> ESI+1 : lettre suivante
00401185
|. |INC ECX --> ECX+1 : compteur
00401186
|. |CMP ECX,5  --> compare le compteur à 5
00401189
|.^ \JB SHORT 0040117E--> saute quand le compteur est inférieur ou égal à 5

0040118B |. MOV ESI,00403008 ; ASCII "
12345" --> remet le serial bidon dans ESI
00401190 |. XOR ECX,ECX
--> mise à 0 de ECX

00401192 |> /MOV BL,BYTE PTR DS:[ESI] --> 1ere lettre du faux serial dans BL
00401194
|. |ADD EAX,EBX --> EAX = EAX + EBX
00401196
|. |CDQ --> convertit le mot signé de EAX en un quadruple mot dans <EDX:EAX>
00401197
|. |MUL EBX --> EAX = EAX * EBX
00401199
|. |INC ESI --> lettre suivante
0040119A
|. |INC ECX --> compteur + 1
0040119B
|. |CMP ECX,5 --> compare le compteur à 5
0040119E
|.^ \JB SHORT 00401192 --> saute quand le compteur est inférieur ou égal à 5

004011A0 |.
CMP EAX,ED3C9A41 --> compare EAX et ED3C9A41
004011A5 |. POPAD
--> restaure les registres sauvés par le PUSHAD
004011A6 |.
JNZ SHORT 004011B2 --> saute si EAX n'est pas égal à ED3C9A41

004011A8 |.
CALL 004011B3
004011AD |. MOV EAX,29A
--> met EAX à 29A pour avoir le droit au good boy.
004011B2 \>
RETN --> sort du CALL

    A partir du serial donné, deux routines (la jaune et orange) calculent un nombre qui sera placé dans EAX si ce nombre est égal à ED3C9A41 alors le serial est bon. Si c'est le cas on ira en 4011B3 grâce au CALL. Je ne vais pas expliquer en détail ce qu'il y fait car c'est inutile, mais ce CALL va transformer le faux mdp qu'on peut voir dans les strings en un bon mdp grâce au serial valide. On voit effectivement :

004011B4 |. BF 44304000 MOV EDI,crackme.00403044 ; ASCII "The Solution perhaps is 'AzulGranaEsMiColor'"
004011B9 |. BE 08304000 MOV
ESI,crackme.00403008 ; ASCII "12345"

    Et à la suite de ça des opérations entre EDI et ESI (plus ou moins directement) qui ont pour effet de modifier le contenu de EDI (avec des XOR) et donc par conséquent le mdp. Bon mais cette partie est indispensable donc on y touchera pas. On va revenir s'intéresser à la comparaison de EAX et ED3C9A41. Comment à partir des deux routines pourrait-on trouver le bon serial. Si on veut faire ça de tête c'est impossible. Il va donc falloir tester toutes les possibilités jusqu'à trouver la bonne. Si on considère que le serial est composé de chiffres on a 100.000 possibilités, or le crackme nous indique d'entrer un "keyword" donc il se pourrait bien que ce soit un mot. Ce qui fait, si je compte bien, environ 12 millions de keyword différents possibles pour des lettres uniquement en majuscules OU en minuscules et 380 millions pour un mélange des deux. Inutile donc de vous dire que les tester à la main n'est pas la bonne solution. Ou alors commencez maintenant vous gagnerez du temps ! Si on continue dans les calculs d'un niveau d'utilité inversement proportionnel aux nombres trouvés, connaissant le serial je peux vous dire que vous avez pour 15-20 ans non-stop avant de le trouver. ;-) Bon trêve de plaisanterie et passons aux choses sérieuses.




3° Programmation du Brute-Force

    Tout d'abord il faut réfléchir sur comment on va s'y prendre pour lui faire tester toutes les possibilités sans en oublier une. Vous savez compter au moins jusqu'à 10 ? Très bien dans ce cas vous êtes l'homme (ou la femme) de la situation. On va faire comme si le programme comptait mais ici ça sera avec des lettres. On lui rentrera le keyword de valeur la plus basse (correspondant à 00000 si on travaillait avec des chiffres) et ensuite on va incrémenter le 1er rang de 1 à chaque fois, une fois la valeur de celle-ci arrivée à [la valeur la plus haute + 1] on remet la valeur de celle-ci à zéro (enfin à la valeur la plus basse) et on incrémente le 2ème rang...etc... Si vous ne m'avez pas compris c'est soit car j'explique mal ou soit parce que vous m'avez menti et que vous ne savez pas compter. Voici un exemple parlant : on commence avec aaaaa, puis on a aaaab -> aaaac -> ... -> aaaaz -> aaaba -> ...etc... On va donc obtenir des boucles de tests dans des boucles pour tester si chaque rang ne dépasse pas la valeur autorisée. Bon maintenant qu'on sait ce qu'on va faire à peu près (il n'y a plus qu'à mettre en forme tout cela) il va falloir en premier lieu regarder où on va pouvoir mettre ça. Car ça c'est facile donc vite fait. :-)

    En descendant un peu dans le code on se rend compte qu'il y a une énorme quantité de bytes libres. On va donc se placer ici. Allez hop on va s'installer en 40122A. Mais vous vous doutez bien que le programme ne va pas se dire "tiens y a de nouvelles instructions dans le fond là bas je vais voir ce que c'est", non il va falloir lui dire de venir voir ici. Pour cela on va retourner voir juste avant le début de la vérification avec les 2 routines :

0040114B |. 68 08304000 PUSH crackme.00403008 ; /String = "12345"
00401150 |. E8 CB000000 CALL <JMP.&kernel32.lstrlenA> ; \
lstrlenA
00401155 |. 83F8 05
CMP EAX,5
00401158 |. 74 13
JE SHORT crackme.0040116D
0040115A |. 83C0 30 ADD EAX,30
0040115D |. 52 PUSH EDX
0040115E |. BA ED304000 MOV EDX,crackme.004030ED ; ASCII "
The Keyword is not n characters long"
00401163 |. 8842 13 MOV BYTE PTR DS:[EDX+13],AL
00401166 |. B8 4D010000 MOV EAX,14D
0040116B |. 5A POP EDX
0040116C |. C3 RETN
0040116D |> 60 PUSHAD

    La vérification de la taille du serial ne nous sert pas tellement vu que l'on sait maintenant que le serial fait 5 caractères. Mais bon par souci de bien faire on va essayer de faire un truc qui sera un minimum propre tout de même. On va donc emmener le JE 0040116D en 40122A. Le problème c'est qu'il s'agissait d'un saut court et là il serait remplacé en saut long prenant donc 4 bytes de plus ce qui écraserait les instructions situées au dessous. Tant pis on aura le message d'erreur avec un "n" au lieu du nombre de lettres, ce qui n'est pas dramatique. Pour garder ce l'apparition de message il va falloir garder le MOV EAX,14D (car c'est avec cette valeur que le message est affiché) et le RETN uniquement. Donc soit on noppe ce qu'il y a d'inutile entre les 2 ou alors on réécrit à la suite du JE ces deux instructions. J'opte pour la seconde solution mais les deux marchent. Voilà ce que ça nous donne :

00401155 |. CMP EAX,5
00401158 |.
JE crackme.0040122A
0040115E |.
MOV EAX,14D
00401163 |.
RETN
00401164 |.
NOP
00401165 |.
NOP
00401166 |.
MOV EAX,14D
0040116B |.
POP EDX
0040116C |.
RETN
0040116D |> PUSHAD

    Les lignes en jaune ne seront jamais effectuées car à la suite de nos instructions le programme sort du CALL. Et si EAX=5 on saute et on reviendra en 40116D sur le PUSHAD pour la vérification du serial. On retient donc cette adresse, ne vous en faites pas je le fais pour vous sur ce coup là. ;-)


    Passons aux choses sérieuses ! On va mettre en forme plus précisément notre algorithme. Voilà comment on va s'y prendre :

- Incrémenter r1 (le 1er rang)
- Comparer r1 à la plus haute valeur possible + 1 (par exemple si on fait les lettres de A à Z, on va comparer r1 à au caractère suivant le Z)
- Si différent alors on sort
- Sinon incrémenter r2
- Réinitialiser r1
- Comparer r2 à la plus haute valeur possible + 1
- Si différent alors on sort
- Sinon incrémenter r3
- Réinitialiser r2
- ...etc... jusqu'à r5

    Il faut maintenant trouver l'adresse de chaque rang. Ce n'est pas dur car on sait que notre serial est placé en 403008 donc le 1er rang (ou dernier selon le sens où l'on commence, ça revient au même) est en 403008, le second en 403009, puis 40300A, 40300B et enfin 40300C. Reste à savoir maintenant ce qu'on va mettre comme valeur correspondant aux caractères. Disons qu'on va prendre les majuscules et minuscules (bon je triche car je connais la réponse :-p mais au départ je vous conseille plutôt de commencer avec seulement l'un ou l'autre ça ramera moins, et si les deux ne fonctionnent pas testez avec les deux). On regarde donc dans une table ascii (incluse dans HexDecCharEditor avec le bouton avec un carré jaune) les valeurs correspondantes : en hexa 'A' à 'Z' cela fait de 41 à 5A et 'a' à 'z' de 61 à 7A. On ne va donc pas s'embêter pour 8 valeurs entre les deux séries et on va donc les inclure. Ce qui nous donne de 41 à 7A.

    Avec tout ce qu'on a, on va pouvoir commencer à rentrer les instructions en 40122A.

INC BYTE [403008] --> r1+1, "byte" indique qu'il faut incrémenter uniquement un octet (un rang)
CMP BYTE [403008],7B --> compare r1 à la plus grande valeur + 1 c'est à dire à 7A+1=7B
JNZ 0040116D --> retourne au PUSHAD pour vérifier le serial
INC BYTE [403009] --> r2+1
MOV BYTE [
403008],41 --> initialise r1 en mettant sa valeur à 41 (caractère 'A')
CMP BYTE [403009],7B
JNZ 0040116D

INC BYTE [40300A] --> r3+1
MOV BYTE [
403009],41
CMP BYTE [
40300A],7B
JNZ 0040116D

INC BYTE [40300B] --> r4+1
MOV BYTE [
40300A],41
CMP BYTE [
40300B],7B
JNZ 0040116D

INC BYTE [40300C] --> r5+1
MOV BYTE [
40300B],41
CMP BYTE [
40300C],7B
JNZ 0040116D
RETN
--> sort de la routine pour revenir à l'affichage des messagebox, si on arrive ici c'est qu'aucun code testé ne marche.


    Il ne reste plus qu'un élément à rajouter. Vous ne voyez pas ce qu'il faut ? Une fois le serial incrémenté il va vérifier le serial et s'il est invalide il va faire quoi ? Et bien nous emmener au mauvais message. On va donc modifier ce saut pour l'emmener en cas d'échec sur le début de notre routine pour incrémenter notre serial. Le saut à modifier est le suivant :

004011A0 . CMP EAX,ED3C9A41
004011A5 . POPAD
004011A6 . JNZ SHORT crackme.004011B2
004011A8 . CALL crackme.004011B3
004011AD . MOV EAX,29A
004011B2 > RETN

    On va donc remplacer la destination du saut par 40122A. Mais là on a un problème ! Le saut est plus long que prévu et on a donc pas assez de bytes pour écrire le saut. On va donc procéder à un petit déménagement des instructions qui suivent le JNZ. On les déplace en 4012AA là où il y a de la place. Le JNZ reste ici mais en changeant la destination vers 40122A comme on l'avait prévu. En revanche à la place du CALL (enfin à la suite du JNZ) on va mettre un JMP vers 40122AA. J'espère que vous arrivez à suivre un peu. Voici plus clairement ce qu'on change :

004011A0 . CMP EAX,ED3C9A41
004011A5 . POPAD
004011A6 .
JNZ crackme.0040122A
004011AC .
JMP crackme.004012AA
004011B1 .
NOP
004011B2 .
RETN
004011B3 .
PUSHAD

    Les instructions en bleu ne seront jamais effectuées. Et en 4012AA on retrouve :

004012AA CALL crackme.004011B3
004012AF
MOV EAX,29A
004012B4
RETN



    Tout ça étant désormais en place il va falloir désormais tester notre crackme modifié. Je vous conseille, au lieu de lancer le crackme directement, de tester cela dans OllyDbg en posant des BP à certains moments. Et n'oubliez pas de sauvegarder votre fichier maintenant car si vous avez fait des erreurs en tapant les instructions le programme pourrait par exemple tourner indéfiniment en rond ce qui le ferait planter et vous ferait perdre vos modifications.

    Comme vous l'avez vu on n'a pas initialisé le serial au départ donc c'est à nous de le faire en tapant le serial au départ. Ainsi pour que le crackme teste toutes les lettres il faut taper "AAAAA". En fait avec cela la solution "AAAAA" ne sera pas testée car elle sera incrémentée avant la vérification donc si on veut vraiment tester cette solution on le fait à la main ou on commence par "@AAAA" le '@' correspondant à 40h soit 1 de moins que pour le 'A'.

    Pour le tester on va par exemple breaker sur l'incrémentation de r1 et faire ensuite F9 à plusieurs reprises pour qu'il fasse plusieurs tours. Vous vérifierez bien (dans la fenêtre du bas à l'adresse 403008) que ce rang là est bien augmenté et que le passage à la "dizaine" supérieur se déroule comme prévu. Ça fonctionne ? Très bien. Dans ce cas le brute force est opérationnel. Cependant si vous lancez le programme tout seul il risque au mieux de ramer un peu et au pire de planter car il doit tester un nombre important de possibilités. Alors vous pouvez éventuellement poser un BP sur l'incrémentation du dernier rang, ça permettra au programme de respirer un peu :-p. Il suffira d'appuyer sur F9 jusqu'à ce qu'on trouve le bon serial (au maximum 50-60 appuis). Enfin libre à vous de tester comme vous le souhaitez. ;-)

    On trouve donc à un moment un beau messagebox avec marqué "Congratulations - The solution perhaps is 'PobreLorito'" ! On a donc réussi ! Si vous avez lancé le bruteforce avec OllyDbg pour connaître le keyword qui fonctionnait il vous suffit de regarder à l'adresse 403008 où l'on peut voir "ChaOs". Comme dirait l'autre : "another crackme cracked" ! :-p


Conclusion

    Voilà un crackme cracké d'une manière différente de ce qu'on a pu voir dans mes autres cours. La méthode que j'ai utilisée n'est pas forcément la meilleure ou la plus rapide mais elle a au moins le mérite de fonctionner ;-). Comme quoi il est possible de brute forcer sans savoir vraiment programmer. Il faut juste savoir exploiter au maximum les connaissances que l'on a pour résoudre des problèmes.

    J'espère que ce cours vous a plu. N'hésitez pas à faire des remarques (critiques, suggestions, autre méthodes...) par mail : deamon.crack (at) netcourrier.com ou bien sur le forum : www.forumcrack.new.fr . Excusez-moi pour cette fin de cours qui est peut-être un peu confuse car j'étais un peu pris par le temps et de plus j'ai mal géré pour le coup du saut court qui se transforme en saut long. Mais maintenant que vous avez compris vous devriez pouvoir refaire ce crackme en l'améliorant tout cela. :-)



Deamon, le 15-18 février 2005