Cours n°15

Crackme avec KeyFile



Objectif :

1° Méthode brute : le cracker tout bêtement ! :-)

2° Méthode plus "intelligente" je dirais : générer un keyfile valide pour ce crackme.


Niveau :

Moyen - Elevé

Ce qu'il vous faut :

- Un éditeur hexadécimal

- OllyDbg (qui va remplacer WinDasm ; aucune connaissance de ce logiciel est nécessaire dans ce cours, je vous expliquerais le fonctionnement au fur et à mesure ; la version utilisée ici est la 1.09d)

- La calculatrice Windows (ou tout autre qui permet de faire des calculs hexadécimaux)

- Le crackme (9 Ko)


Introduction

    Nous allons dans ce cours étudier un type d'enregistrement différent de ceux que nous avons rencontré au cours de mes anciens tutoriaux. Ici pas de serial, ni de nag-screen ou autres mais c'est de KeyFile qu'il est question. Pour ceux qui ne connaissent pas, le fonctionnement est le suivant : pour vérifier si le programme est enregistré celui-ci va regarder dans un fichier (le plus souvent c'est un .ini, .dat, ...) qui contient une clé. Si la clé est valide le programme est enregistré.


1° Cracker ce crackme (pléonasme devenu habituel dans mes cours !)

    Nous allons donc lancer le crackme pour voir ce qu'il nous demande. Il nous affiche une message box affichant : "Your time-trial has ended... Please register and copy the keyfile sent to you to this directory". Pour les anglophobes voici la traduction : "La période d'essai est terminé... Veuillez vous enregistrer et copier le fichier clé, que vous avez reçu, dans ce dossier". Même pas eu le temps d'utiliser ce crackme et la période d'essai est déjà terminée ! Quelle arnaque ! :-P

    Bon alors pour ce cours pas de WinDasm, on va apprendre à utiliser OllyDbg car je trouve qu'il est mieux, plus puissant et de plus en plus utilisé. D'ailleurs c'est moins facile de cracker ce crackme avec WinDasm car il ne trouve aucune String Data References. Donc on lance ce logiciel et on ouvre notre crackme qui tremble déjà de peur, son heure approche ! :-D
Normalement après avoir désassemblé le crackme vous devriez tomber nez à nez avec notre vilain message. Mais on va faire comme si on l'avait pas vu pour ne pas éveiller les soupçons... On va plutôt le prendre à revers ! :-p Alors discrètement on va faire clic droit dans le cadre où il y a les lignes de code, puis "Search For" --> "All referenced strings text", ce qui correspond au Strings Data References de WinDasm. Autre méthode : cliquer sur l'icône du "R" sur fond bleu (mais il faut obligatoirement avoir préalablement déjà chargé les SDR au moins une fois avec le menu). Une fois la fenêtre des Strings ouverte, double-cliquez sur le mauvais message pour vous y rendre. Bon vous allez tomber au même endroit qu'il y a 30 secondes, mais c'est juste pour vous montrez comment rechercher les messages.

Voici une vue globale d'OllyDbg :



1) Cette flèche signifie que l'instruction peut sauter vers un autre endroit, pour voir où le saut ira on peut cliquer sur la ligne et une flèche nous montrera. En mode pas à pas lorsque la flèche est grise cela signifie que le saut n'est pas effectué.
2) Cette flèche indique que cette instruction est appelée par un saut, c'est l'arrivée d'un saut.
3) La ligne 004010C1 appelé par un saut, étant sélectionnée, une flèche nous montre quel saut nous amène sur cette instruction.
4) Très utile car cela nous indique les valeurs que le programme manipule, ainsi pour l'instruction
MOV AL,BYTE PTR DS:[EBX+40211A] , dans cette fenêtre est indiquée la valeur de DS:[EBX+40211A] (ici 33, en hexa bien sur) et celle de AL (AL est une partie de EAX et correspond aux bits de poids faibles, pour simplifier AL est les 2 premiers chiffres en partant de la droite de EAX donc ici 01). Quand c'est possible on nous donne également le caractère ascii auquel correspond la valeur, ici on nous indique que 33h correspond au caractère "3". Ce qui est utile quand le programme manipule les caractères d'un serial par exemple. C'est ici aussi que vous pouvez voir si un saut s'effectue ou non ("Jump is taken" ou "Jump is not taken").
5) Bouton pour voir les Strings Data References.
6) Les registres et ses valeurs. Peut aussi afficher en ascii pour un nom ou serial par exemple.
7) Les flags. Peut être utile pour inverser un saut. Par exemple si un JZ (=JE) ne saute pas mais vous voulez qu'il saute il suffit de double-cliquez sur la valeur du Flag Z pour l'inverser.
8) Nous indique ce qui est poussé sur la pile (avec PUSH). On peut y voir passer des serials parfois ! :-P
9) Permet de voir si le programme qu'on débuggue est en exécution, en pause (s'il break)...

    Donc voilà les points importants d'OllyDbg. Maintenant on va regarder comment cracker notre petit programme. On voit à l'endroit du message :

00401078 . 83F8 FF     CMP EAX,-1 ; |
0040107B . 75 1D     JNZ SHORT due-cm2.0040109A ; |
0040107D . 6A 00     PUSH 0 ; |/Style = MB_OK|MB_APPLMODAL
0040107F . 68 01204000     PUSH due-cm2.00402001 ; ||Title = "Duelist's Crackme #2"
00401084 . 68 17204000     PUSH due-cm2.00402017 ; ||Text = "Your time-trial has ended... Please register and copy the keyfile sent to you to this directory!"
00401089 . 6A 00     PUSH 0 ; ||hOwner = NULL
0040108B . E8 D7020000     CALL <JMP.&USER32.MessageBoxA> ; |\MessageBoxA
00401090 . E8 24020000     CALL <JMP.&KERNEL32.ExitProcess> ; \ExitProcess
00401095 . E9 28010000     JMP due-cm2.004011C2
0040109A > 6A 00     PUSH 0 ; /pOverlapped = NULL


    Il y a donc un test pour savoir si EAX = -1 et si ce n'est pas le cas il saute après le mauvais message. Nous verrons par la suite à quoi cela correspond. On va donc remplacer le JNZ par un JMP (à noter que JZ=JE et JNZ=JNE, ce sont les mêmes sauts, sous OllyDbg ils sont plutôt notés JZ/JNZ et sous WinDasm JE/JNE). Pour tester dans OllyDbg on double-clique sur l'instruction JNZ et on remplace JNZ SHORT 0040109A par JMP SHORT 0040109A. On valide et on regarde si ça marche. On lance donc le crackme dans OllyDbg avec F9 (les raccourcis claviers des fonctions principales sont identiques à celle du débuggeur de windasm : F2 pour poser un BreakPoint, F9 pour Run, F7 : Step Into, F8 : Step Out...).

    Alors maintenant le message n'est plus le même puisqu'il nous dit : "Your current keyfile is invalid... Please obtain a valid one from the software author!". Message que nous retrouvons un peu en dessous du précédent. Le programme s'étant fermé, il faut réinitialiser OllyDbg en appuyant sur CTRL+F2.

    Bon donc ce saut ne suffit pas, mais il faut tout de même le modifier. Mais après la réinitialisation les modifications sont annulées (de toute façon les modifications dans OllyDbg tout comme le débuggeur de WinDasm ne sont que temporaires). Donc il va falloir refaire la modification sur le JNZ. On va pour cela poser un breakpoint dessus. On Run, il breake sur le JNZ. Là on va modifier le flag Z en double-cliquant sur sa valeur qui est ici à 1 pour le faire passer à 0. La flèche du saut devient rouge et le "Jump is NOT taken" se transforme en "Jump is taken". Et maintenant on va avancer en pas à pas avec F8 jusqu'au prochain saut qui pourrait nous emmener sur le mauvais message. Et on arrive ici :

004010AE . 85C0 TEST EAX,EAX
004010B0 . 75 02 JNZ SHORT due-cm2.004010B4
004010B2 . EB 43 JMP SHORT due-cm2.004010F7
004010B4 > 33DB XOR EBX,EBX


    Donc là si le JNZ saute (ce qu'il ne fera pas si vous avez bien regardé) il passera au dessus du JMP. Ce dernier sautera à l'adresse 004010F7. Regardons ce qui se trouve à cette adresse : notre mauvais message "Your current keyfile is invalid...". Il faut donc faire sauter ce JNZ. Alors lorsqu'on s'arrête sur cette ligne en avançant en pas à pas, on fait la même chose que tout à l'heure : modifier le flag Z pour qu'il saute. Puis on tombe sur :

004010BF . 7C 36 JL SHORT due-cm2.004010F7

    Il saute sur l'adresse du mauvais message donc on le noppera ou l'inversera. Pour modifier le flag ce n'est plus le Z car le saut est JL (Jump if Less = Saute si moins/inférieur) mais le flag S (Sign Flag). Ici même topo :

004010D6 . 7C 1F JL SHORT due-cm2.004010F7


    Et donc juste avant le mauvais message il y en a encore un autre (le dernier ?) à transformer en JMP et qu'on passera avec le flag Z :

004010F5 . 74 1D JE SHORT due-cm2.00401114


    Maintenant on croit en avoir fini, car on a déjà dû modifier 6 sauts pour en arriver là, mais non il en reste encore un plus bas qui saute lui aussi vers 004010F7 :

00401155 .75 A0 JNZ SHORT due-cm2.004010F7

    Et voilà ça devrait être bon ! :-) Il ne semble pas y en avoir d'autre donc après avoir modifié le flag Z pour ce dernier on fait F9 pour continuer le programme... Et là : "This program is registered to our dear user :". Bon forcément il n'y a pas notre nom. Si vous voulez afficher votre nom lisez la suite :-) on va faire ça dans la deuxième partie de ce cours.

    Donc pour résumer voici les sauts que nous avons à modifier :

0040107B . 75 1D JNZ SHORT due-cm2.0040109A ---> JMP
004010B0 . 75 02 JNZ SHORT due-cm2.004010B4
---> JMP
004010BF . 7C 36 JL SHORT due-cm2.004010F7
---> JNL ou NOP
004010D6 . 7C 1F JL SHORT due-cm2.004010F7
---> JNL ou NOP
004010F5 . 74 1D JE SHORT due-cm2.00401114
---> JMP
00401155 . 75 A0 JNZ SHORT due-cm2.004010F7
---> NOP ou JZ

    Mais maintenant que nous avons vu que le bon message commençait ici :  [00401157 . 6A 00 PUSH 0 ; /lParam = NULL] on peut donc mettre à la place du premier saut (0040107B . 75 1D JNZ SHORT due-cm2.0040109A) un JMP 00401157 pour ainsi sauter directement vers le bon message. Ça marche et il n'y a qu'un endroit à modifier. Même si ça empiète sur le premier mauvais message ce n'est pas grave car il ne passera plus jamais par là ! :-P

    Ce qui donne à la place de :

0040107B /75 1D JNZ SHORT due-cm2.0040109A
0040107D |6A 00 PUSH 0
0040107F |68 01204000 PUSH due-cm2.00402001 ; ASCII "Duelist's Crackme #2"


    On aura :

0040107B /E9 D7000000 JMP due-cm2.00401157
00401080 |90 NOP
00401081 |90 NOP
00401082 |90 NOP
00401083 |90 NOP


    Petite info : dans OllyDbg lorsque vous modifiez une ligne, le fait de cocher "Fill with NOP's" remplit les octets vides par des NOP comme pour l'exemple au-dessus. Par exemple si vous avez un JMP qui donne [E9D7000000] en hexa et que vous voulez mettre un petit JNZ de type [751D], vous aurez 3 octets de "libre" du JMP. Ceux là seront remplacés par des NOP pour ne pas perturber le programme. Donc le mieux est de laisser cette case cochée. OllyDbg pemet d'enregistrer les modifications effectuées sans avoir à passer par un éditeur hexa. Pour cela on fait clic droit dans le cadre du listing des instructions puis 'Copy to executable' -> 'All modifications' et 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.



2° Créer un KeyFile valide

    Bon on recommence tout depuis le début. On ouvre avec OllyDbg le crackme (et pas celui déjà modifié ;-) ). On va vers notre premier message d'erreur ("Your time-trial..."). Et on remarque que juste au dessus l'API CreateFileA, donc l'accès à un fichier. Et dans les paramètres de l'API que voit-on ?

0040106E . 68 79204000 PUSH due-cm2.00402079 ; ||FileName = "due-cm2.dat"
00401073 . E8 0B020000 CALL <JMP.&KERNEL32.CreateFileA> ; |\CreateFileA

    FileName="due-cm2.dat" !! Ça ne serait pas notre keyfile par hasard ? Pour le savoir on va créer un fichier nommé "due-cm2.dat" dans le dossier du crackme. Mais on ne va pas le laisser vide on va (avec le bloc-notes par exemple) mettre "123456" dedans (sans les guillemets bien sûr !). Vous enregistrez et vous lancez le crackme (en dehors d'OllyDbg). Et là le deuxième message d'erreur apparaît : "Your current keyfile is invalid... Please obtain a valid one from the software author!". Bon on va voir pourquoi le contenu du fichier n'est pas bon. Pour cela direction OllyDbg.

    Donc là on comprend mieux à quoi sert le saut situé entre le CreateFileA et le 1er message :

00401078 . 83F8 FF CMP EAX,-1
0040107B 75 1D JNZ SHORT due-cm2.0040109A
--> si le fichier n'existe pas, alors ne saute pas et passe par le 1er message.

    Ce saut nous emmène sur l'API ReadFile (tout le monde aura deviné à quoi cette API sert :-) ) :

0040109A > 6A 00 PUSH 0 ;                                          /pOverlapped = NULL
0040109C . 68 73214000 PUSH due-cm2.00402173 ;       |pBytesRead = due-cm2.00402173
004010A1 . 6A 46 PUSH 46 ;                                         |BytesToRead = 46 (70.)
004010A3 . 68 1A214000 PUSH due-cm2.0040211A ;       |Buffer = due-cm2.0040211A
004010A8 . 50 PUSH EAX ;                                            |hFile
004010A9 . E8 2F020000 CALL JMP.&KERNEL32.ReadFile ; \ReadFile
004010AE . 85C0 TEST EAX,EAX

    On va donc breaker juste après l'API sur le TEST EAX, EAX. On sélectionne la ligne est on fait F2 (le bout de la ligne devient rouge). On Run (F9) et ensuite on réfléchit. On peut voir (dans la fenêtre numérotée 4 ou 6 sur le schéma au-dessus) que EAX est à 1. On va avancer en pas à pas avec F8 (ou F7 mais ce dernier va vous faire rentrer dans les CALL et là on n'en aura sans doute pas besoin). Pour mieux vous repérer vous pouvez mettre des commentaires sur les lignes en faisant clic-droit et "Comment" (;) sur la ligne que vous souhaitez.

    Le saut JNZ de la ligne suivante saute au-dessus du JMP qui nous mène au bad message. Ce qui est bien donc. Ensuite on trouve deux XOR sur EBX lui-même et ESI également ce qui a pour effet de mettre ces registres à 0. Les XOR sont souvent utilisés pour réinitialiser les registres. On arrive après les XOR ici :

004010B8 . 833D 73214000  CMP DWORD PTR DS:[402173],12 --> compare DS:[402173] à 12h
004010BF . 7C 36                 JL SHORT due-cm2.004010F7
--> saute vers le mauvais message si DS:[402173] est inférieur à 12h

    A ce moment DS:[402173] vaut 6, si on se souvient bien on à mis le nombre 123456 dans le keyfile. 6 correspond donc à son nombre de caractères. Il faut donc que le serial du keyfile fasse 12h lettres minimum (12 en hexa correspond à 18 en décimal, donc 18 caractères minimum). On note donc cette condition et on continue en changeant le flag S pour éviter le saut du JL. Et là on arrive sur une petite routine :

004010C1  MOV AL,BYTE PTR DS:[EBX+40211A] --> met le caractère dans AL
004010C7  CMP AL,0 --> compare le caractère à 00h
004010C9  JE SHORT due-cm2.004010D3 --> sort de la routine si le caractère = 00h
004010CB  CMP AL,1 --> compare le caractère à 01h
004010CD  JNZ SHORT due-cm2.004010D0 --> saute au dessus de INC ESI si le caractère est différent de 01h
004010CF  INC ESI --> incrémente ESI
004010D0  INC EBX --> incrémente EBX = caractère suivant
004010D1  JMP SHORT due-cm2.004010C1 --> boucle au début de la routine
004010D3  CMP ESI,2 --> compare ESI à 2
004010D6  JL SHORT due-cm2.004010F7 --> Saute vers le Bad Boy si ESI < 2


    J'espère qu'avec les couleurs c'est plus compréhensible ! :-) J'ai fait du mieux que j'ai pu.
Donc pour résumer en rose/rouge c'est la routine qui va d'abord mettre à chaque passage un caractère (le 1er puis le 2ème...etc) dans AL. Ensuite si le caractère est 00 (en hexa) il sort de la routine. Il trouvera un 00 quand il n'y aura plus de caractères. Après cela si le caractère = 01h il ne saute pas et incrémente ESI, sinon pour tout autre caractère il n'incrémente pas ESI car il saute par dessus. Et enfin il incrémente EBX pour passer au caractère suivant.

    Quand vous êtes sur la ligne du MOV, vous pouvez voir la valeur du caractère étudié, le premier sera le "1", mais attention "1" est un caractère ascii et non une valeur. Sa valeur est 31h.

    Donc pour éviter le mauvais message il faut que ESI soit égal ou supérieur à 2. C'est à dire qu'il y ait au moins deux caractères 01h dans le serial. On va donc mettre un serial valide. Pour mettre 01h il faut donc utiliser l'éditeur hexadécimal. Vous ne pourrez pas modifier le .dat à cet instant car le crackme est en exécution dans OllyDbg, on va donc noter à quel ligne on repartira (004010D6) et on réinitialise OllyDbg avec CTRL+F2. Si un message s'affiche faîtes "Oui", une erreur peut survenir mais c'est normal dans ce cas les breakpoints et les modifications seront annulés mais ce n'est pas grave. Une fois initialisé on peut modifier le .dat. On va donc mettre 18 caractères dont deux 01h. Dans HexDecCharEditor pour ajouter des bytes il faut faire "Edit" --> "Insert Bytes" ou la touche Ins.

    On va donc mettre par exemple (en ascii) : 123456 789ABC DEFG (les 01h, ne pouvant pas être représentés en ascii, correspondent ici aux espaces).
Ce qui donne en hexa : 31|32|33|34|35|36|01|37|38|39|41|42|43|01|44|45|46|47
Je vous déconseille de mettre des caractères identiques dans le serial car après vous aurez des difficultés à savoir sur quel caractère travaille le crackme dans ses routines et ses tests. Bon donc on enregistre et on va regarder ce qu'il nous veut encore, ce crackme.

    On pose un BreakPoint en 004010D6 comme on l'avait noté, car les tests d'avant doivent être corrects. On Run, ça break normalement et sur le JL on voit : "Jump is NOT taken" (enfin si vous avez bien rempli correctement le .dat). Le crackme a donc bien compté les deux 01h. On avance en pas à pas toujours : XOR EBX, EBX et XOR ESI, ESI : mise à 0 de ces deux registres. Et ensuite juste avant le deuxième mauvais message on rencontre une routine quasi-semblable à la précédente :

004010DC MOV AL,BYTE PTR DS:[EBX+40211A] --> met le caractère dans AL
004010E2 CMP AL,0 --> compare le caractère à 00h
004010E4 JE SHORT due-cm2.004010EF --> sort de la routine si le caractère = 00h
004010E6 CMP AL,1 --> compare le caractère à 01h
004010E8 JE SHORT due-cm2.004010EF --> sort de la routine si le caractère = 01h
004010EA ADD ESI,EAX --> ajoute la valeur du caractère à ESI
004010EC INC EBX --> caractère suivant
004010ED JMP SHORT due-cm2.004010DC --> boucle au début de la routine : et c'est reparti pour un tour :-p !
004010EF CMP ESI,1D5 --> compare ESI à 1D5h
004010F5 JE SHORT due-cm2.00401114  --> saute au dessus du bad boy si ESI = 1D5h

    Explication pour ceux qui ne comprennent toujours pas mes belles routines colorées :-) : chaque caractère est placé tour à tour dans AL (donc dans EAX car je répète que AL est une partie de EAX). Si le caractère est 00h ou 01h alors il sort de la routine. Sinon il ajoute la valeur hexa du caractère dans ESI. Ainsi on aura dans ESI, à la fin de la routine, la somme des caractères situés avant le premier 01h ou 00h qui joue le rôle de séparateur (pour notre .dat ce sera 01h qui arrivera avant le 00h). Après la routine il vérifie que ESI (donc la somme des caractères) soit égale à 1D5h. Et si ce n'est pas le cas : PAF! Un "Your keyfile is invalid..." vous sautera à la figure ! Pour résumer : il faut donc que la somme des caractères situés avant le premier 01h soit égale à 1D5h.

    Bon on note où on en est (004010F5) si on ne veut pas se perdre, on réinitialise tout ça et on va faire les modifications nécessaires. Pour notre exemple nous avons mis 6 caractères avant le 01h. On va donc prendre la calculette pour se débrouiller à avoir une somme de 1D5h (et oui car de tête c'est pas pratique le calculs hexadécimaux :-/ ). Pour la calculette mettez vous en Mode Scientifique et en "Hex". On va faire 1D5/6 ce qui donne 4E. Mais en hexa il n'y a pas de décimal donc 4E correspond à la partie entière. Pour trouver le reste vous devez savoir faire je pense ! :-) Bon je vais vous aider quand même ! ;-) On fait 4E*6 on trouve 1D4. Et 1D5-1D4=1 (cette soustraction à été calculée de tête là ! Balèze hein ? :-P ) donc on aura cinq 4E et un 4F (car 4E+1=4F). Ce qui nous fait un total de 1D5 ! Le compte est bon ! :-) On a donc dans l'éditeur hexa : 4E|4E|4E|4E|4E|4F|01|37|38|39|41|42|43|01|44|45|46|47.

    On retourne à OllyDbg en posant un breakpoint sur 004010F5, on Run, ça break et on voit sur le JE : "Jump is taken" ! Ouais ça marche ! Et là on se dit que c'est fini car on saute après le deuxième mauvais message, mais comme on l'a vu en première partie il reste encore des vérifications ! Donc on avance prudemment en faisant attention aux sauts qui, tels des mines, peuvent vous faire sauter n'importe où ! :-P On passe par dessus le bad boy, on tombe sur une mise à 0 de ESI et une incrémentation de EBX. Pourquoi changer EBX ? Car EBX correspond toujours au numéro de position des caractères et donc il passe au caractère suivant. Rappelez vous on s'était arrêté au 01h à la routine précédente ! Et là à partir du INC EBX on tombe encore une routine, c'est donc reparti pour une routine colorée par mes soins :-) :

00401116 INC EBX --> caractère suivant
00401117 MOV AL,BYTE PTR DS:[EBX+40211A] --> met le caractère dans AL
0040111D CMP AL,0 --> compare le caractère à 00h
0040111F JE SHORT due-cm2.00401139 --> sort de la routine si le caractère = 00h
00401121 CMP AL,1 --> compare le caractère à 01h
00401123 JE SHORT due-cm2.00401139 --> sort de la routine si le caractère = 01h
00401125 CMP ESI,0F --> compare ESI à 0Fh
00401128 JNB SHORT due-cm2.00401139 --> sort de la routine si ESI > ou = 0Fh
0040112A
XOR AL,BYTE PTR DS:[ESI+40211A] --> XOR entre ESI+40211A et le caractère (cf. explication plus bas)
00401130 MOV DWORD PTR DS:[ESI+402160],EAX --> met le résultat dans ESI+402160
00401136 INC ESI --> incrémente ESI = caractère suivant (cf. explication plus bas)
00401137 JMP SHORT due-cm2.00401116 --> boucle au début de la routine
00401139

    Bon donc on commence avec le caractère qui est situé après le premier 01h. Si le caractère est 00h ou 01h (qui servent encore de séparateur) on sort de la routine. On la quitte également si ESI >= 0Fh.

    Avant d'expliquer ce qu'il fait avec le XOR, voyons ce qui correspond à quoi. 40211A est l'adresse où est situé le serial. Donc EBX+40211A correspond au caractère placé en "EBX position" (donc au départ la position juste après le 01h, donc, ici, en 7ème position). Pour ESI+40211A, il faut savoir que juste avant la routine, ESI a été mis à 0. Donc ESI+40211A correspond au 1er caractère. Ainsi pour le XOR AL,BYTE PTR DS:[ESI+40211A] cela signifie qu'il fait une opération XOR entre le Nème caractère après le 01h (donc AL) et le Nème caractère du serial (ESI+40211A). Par exemple lors de la première boucle on aura un XOR entre le 1er caractère et le 7ème. Puis à la deuxième boucle : 2ème caractère XOR 8ème caractère. C'est compliqué à expliquer donc voici pour vous aider la correspondance en couleur : 4E|4E|4E|4E|4E|4F|01|37|38|39|41|42|43|01|44|45|46|47. Les XOR se font entre les valeurs de même couleur.

    Le résultat de calcul est mis à l'adresse : ESI+402160. Si vous voulez voir ce que cela donne vous pouvez regarder dans la fenêtre en bas à l'adresse 402160 (+ESI mais comme celui-ci a varié de 0 à 6 les résultats commencent en 402160+0 donc bien 402160 et finissent en 402166) ce que cela donne (ici "yvw│││").  Nous verrons par la suite à quoi cela sert.

    On continue notre pas à pas (pour éviter de faire en pas à pas les 6 boucles de la routine vous pouvez poser un BP juste à la sortie de la routine en 00401139 et appuyez sur Run pour s'y rendre immédiatement). Par la suite on trouve un INC EBX (pour passer au caractère suivant, donc caractère suivant le deuxième 01h) et une mise à 0 de ESI. Et ensuite encore une routine qui est strictement identique à la deuxième rencontrée à une différence près : CMP ESI,1B2 et non 1D5. Il faut donc que la somme des caractères après le deuxième 01h soit égale à 1B2h. On a 4 caractères après le deuxième séparateur. On fait donc 1B2/4 = 6C et il reste 2. Donc on aura trois fois 6C et une fois 6C+2 soit 6E : 4E|4E|4E|4E|4E|4F|01|37|38|39|41|42|43|01|6C|6C|6C|6E. On fait donc les modifications dans l'éditeur hexa (pensez à réinitialiser OllyDbg et noter à quelle ligne on est). On teste en lançant le .exe. Et là : BINGO ! Ça marche ! Mais il y a un petit détail qui ne va pas ! Notre nom ne ressemble à rien ! :-( On va donc voir comment il trouve le nom. On va donc breaker là où en était (00401155) et continuer à analyser le code. Une fois le programme breaké, on va regarder un peu en dessous pour avoir une vue globale de la suite. Et on remarque :

004011F4 > \68 60214000 PUSH due-cm2.00402160 ; /Text = "yvw│││"
004011F9 . 6A 01 PUSH 1 ; |ControlID = 1
004011FB . FF75 08 PUSH DWORD PTR SS:[EBP+8] ; |hWnd
004011FE . E8 5E010000 CALL <JMP.&USER32.SetDlgItemTextA> ; \SetDlgItemTextA

   
Le texte qu'il va afficher : yvw│││ ressemble étrangement à ce qu'on avait aperçu en 402160, là où on avait le résultat du XOR. Cette routine servait donc à trouver le nom. Maintenant il n'y a plus qu'à calculer pour arriver à afficher notre nom. On va essayer d'afficher "Deamon" (le nom fera forcément 6 caractères donc 6 lettres au maximum). On va donc décomposer mon nom "Deamon" en valeur hexa (pour cela soit vous prenez une table ascii qui est disponible dans HexDecCharEditor (l'icône du carré jaune :-) ) ou soit vous tapez dans la partie ascii de l'éditeur et sa correspondance en hexa s'affiche).

    D|e|a|m|o|n donne donc 44|65|61|6D|6F|6E. Mais pour trouver les lettres le crackme calcule en faisant un XOR entre les caractères après le premier 01h et ceux avant. On a : 4E|4E|4E|4E|4E|4F|01|X1|X2|X3|X4|X5|X6|01|6C|6C|6C|6E. Pour avoir 44 en pour la première lettre ("D") il faut que 4E XOR X1 = 44 (la variable X1 correspondant au nombre recherché pour la première lettre, X2 pour la seconde...) donc X1 = 4E XOR 44 = 0A. Faites le calcul du XOR avec la calculatrice de windows. Donc voici ce que ça donne :

X1 = 4E XOR 44 = 0A
X2 = 4E XOR 65 = 2B
X3 = 4E XOR 61 = 2F
X4 = 4E XOR 6D = 23
X5 = 4E XOR 6F = 21
X6 = 4F XOR 6E = 21

    Ce qui nous donne : 4E|4E|4E|4E|4E|4F|01|0A|2B|2F|23|21|21|01|6C|6C|6C|6E

    Si votre nom est plus court ou plus long vous pouvez déplacer les séparateurs et augmenter le nombre total de caractères. Mais le nom ne dépassera pas les 15 caractères à cause du CMP ESI,0F. Et veillez à mettre le même nombre de lettres avant et après le 1er séparateur. Mais respectez la valeur des sommes des caractères du début et de la fin.

    Attention : Si par exemple vous souhaitiez afficher un "N" dans votre nom, cela n'aurait pas marché car "N" = 4E et comme 4E XOR 4E = 00h cela aurait donc posé un problème (c'est la même chose pour 00h ou 01h). Il aurait donc fallu changer le 4E du départ par exemple en mettant 40 (ce qui fait 4E-40=E) et donc rajouter Eh à un autre pour que la somme soit toujours égal à 1D5h. Exemple : 40|5C|4E|4E|4E|4F|01|0E|39|2F|23|21|21|01|6C|6C|6C|6E. Ce qui revient au même car 4D+5C=4E+4E. Et ainsi le "N" en première place aurait donné X1 = 40 XOR 4E = 0E. Et donc la deuxième lettre si on garde le "e" (65h) donnerait : X2 = 5C XOR 65 = 39. Bon j'espère avec ça que vous aurez compris.

 

Récapitulatif :

- Nom du keyfile : due-cm2.dat
- 18 caractères minimum
- 2 caractères 01h minimum qui servent de séparateur
- 00h ou un vide pour indiquer la fin du keyfile
- La somme des caractères avant le premier 01h fait 1D5h
- La somme des caractères après le deuxième 01h fait 1B2h
- Les caractères du nom sont donnés par l'opération XOR entre les caractères d'avant et d'après le premier 01h

- Exemple valide avec le nom "Deamon" : 4E|4E|4E|4E|4E|4F|01|0A|2B|2F|23|21|21|01|6C|6C|6C|6E

Et avec ça, ça sera tout ! :-) Le crackme est cracké et avec la manière ! Et en plus vous savez désormais utiliser OllyDbg.

 

 

Deamon, le 02 et 03 mars 2004

Passer au cours suivant >>>
[ Brute-Forcer un crackme ]