Cours n°11

Le Crackme qui se cracke tout seul



Objectif :

1° Faire en sorte que le crakme se patche lui-même.

2° Cracker le crackme pour qu'il nous donne le bon serial.


Niveau :

Avancé. (Non mais bougez pas, restez sur votre siège !)


Ce qu'il vous faut :

- Connaître les bases de l'Assembleur (lire mon cours à ce sujet)

- Savoir utiliser WinDasm (si vous avez compris mes cours précédents c'est bon ! ;-) )

- WinDasm

- Un éditeur hexadécimal

- Le crack-me du cours n°3 et 4 (il aura souffert celui là !)

- StudPE (409 Ko)





Introduction

     Je n'ai pas de définition exacte du Reverse Engineering, donc je ne sais pas si on peut considérer ce cours comme étant un cours de Reverse Engineering. Mais cela n'est pas bien grave. Pour ceux qui ne savent pas ce que c'est, c'est l'art de modifier ou d'ajouter des fonctions d'un programme en rajoutant, modifiant du code de ce dernier. Enfin du moins c'est comme ça que je le définirais.

    Dans ce cours tout ce que je vais vous montrer ne vous servira pas forcément plus tard. Je fais cela juste pour m'amuser, pour utiliser mes connaissances en assembleur et pour vous en faire profiter. Donc je vous montrerai des méthodes qui sont beaucoup plus longues que la simple inversion de saut, mais dont le résultat revient au même.

    Dans ce cours vous apprendrez également à mieux utiliser le débuggeur de WinDasm.
 

 

Le Crack-Me qui se patche lui-même !

    J'ai pris le crackme du cours n°3 et 4. Donc nous l'avons déjà cracké ensemble de manière brève mais efficace. Le crack-me comporte juste une boite de dialogue qui demande un serial. Et si c'est pas le bon, comme d'habitude, nous avons le droit à un beau message "Incorrect try again!!".

    Maintenant que vous avez téléchargé StudPe, cela va (ou plutôt doit) devenir un réflexe désormais : regarder si le programme est compressé ou voir en quel langage il a été programmé. Donc lancez StudPe, allez directement à Options, ne passez pas par la case départ et ne recevez pas 20.000 F. Oups pardon je m'égare quelque peu ! Une fois sur l'onglet "Options", cochez la case "Shell Menu Extension". Cela vous permettra, en faisant un clic droit sur un .exe ou .dll de l'analyser avec StudPe grâce à l'ajout d'un raccourci dans le menu "clic-droit". N'oubliez pas de configurer l'éditeur hexa avec la case "HexEditor" en sélectionnant le .exe de votre éditeur préféré.

    Une fois tout cela fait, ouvrez notre crackme avec et allez à l'onglet "Signature". Là on peut voir qu'il a été programmé avec Visual C++ 6.0. Donc il n'est pas compressé. Bon allez, vous vous amuserez à voir les autres onglets quand j'aurai fini mon cours.

    Bon cela ne nous empêche pas, bien sûr, de désassembler notre cher crackme. On recherche le message d'erreur, on tombe sur :
 

:0040158D FF1500204000 Call dword ptr [00402000]
:00401593 85C0 test eax, eax
:
00401595 7516 jne 004015AD
:00401597 6A40 push 00000040

* Possible StringData Ref from Data Obj ->"
CrackMe"
|
:00401599 6850304000 push 00403050

* Possible StringData Ref from Data Obj ->"
Correct way to go!!"
|
:0040159E 6858304000 push 00403058
:004015A3 8B4DE0 mov ecx, dword ptr [ebp-20]

* Reference To: MFC42.Ordinal:1080, Ord:1080h
|
:004015A6 E853050000 Call 00401AFE
:004015AB EB14 jmp 004015C1

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00401595(C)

|
:004015AD 6A40 push 00000040

* Possible StringData Ref from Data Obj ->"
CrackMe"
|
:004015AF 686C304000 push 0040306C

* Possible StringData Ref from Data Obj ->"
Incorrect try again!!"

 

    Oula que c'est dur à cracker ça ! ;-) Bon il faut donc modifier le JNE en 00401595 et le remplacer par un JE ou deux NOP. Il va falloir que le crackme modifie lui-même ce saut comme un grand :-). Pour modifier par exemple le JNE en JE on mettra donc :

            MOV byte PTR [00401595], 74 : il s'agit donc de mettre 74 (JE) à l'adresse 401595.

    Il va falloir insérer cette instruction à un endroit où il y a de la place (là où c'est plein de 00). La plupart du temps vers la fin du programme on trouve de la place. Ouvrez le avec l'éditeur hexadécimal et allez donc vers la fin du programme. On le mettra par exemple à l'offset 4890. Et pour que le programme passe par ici il va falloir l'ammener par un JMP. Donc il faut trouver une instruction qu'on peut remplacer par le JMP. Et il ne faut pas oublier non plus le JMP après le MOV pour pouvoir revenir de là où on vient. J'espère avoir été assez clair.

    Petit rappel : pour un saut court le premier byte (octet) correspond au type de saut et le deuxième au nombre (en hexa bien sur) de bytes qu'il va sauter. Ex: 7516 : 75 = JNE et saute de 16 bytes.

    Le problème ici c'est que le saut est largement supérieur à FFh (255d) octets. Il prendra environ 5 bytes (1 pour le 74 et 4 pour la taille du saut) ce qui est donc impossible à faire comme cela. Si le saut avez été inférieur à FF on aurait très bien pu faire comme ceci : exemple :

 

    Remplacer par :



    Mais là il va falloir remplacer une instruction qui ne sert pas (ou plutôt qui sert mais à rien ;-) ) d'une taille minimum de 5 bytes et dont le programme passe forcément dessus.

    Donc on regarde au-dessus du test qui ammène de notre message d'erreur car il y passe forcément :



* Reference To: KERNEL32.lstrlenA, Ord:0308h
|
:00401560 FF1504204000 Call dword ptr [00402004]
:00401566 8945F0 mov dword ptr [ebp-10], eax
--> met le nombre de lettres du serial dans ebp-10
:00401569 837DF001 cmp dword ptr [ebp-10], 00000001
--> compare à 1
:
0040156D 7316 jnb 00401585 --> si c'est supérieur saute, si pas de lettre ne saute pas
:0040156F 6A40 push 00000040

* Possible StringData Ref from Data Obj ->"CrackMe"
|
:00401571 682C304000 push 0040302C

* Possible StringData Ref from Data Obj ->"Enter Registration Number"
|
:00401576 6834304000 push 00403034
:0040157B 8B4DE0 mov ecx, dword ptr [ebp-20]

* Reference To: MFC42.Ordinal:1080, Ord:1080h
|
:0040157E E87B050000 Call 00401AFE
:00401583 EB3C jmp 004015C1

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0040156D(C)

|
:00401585 8D4DE4 lea ecx, dword ptr [ebp-1C]
:00401588 51 push ecx
:00401589 8D55F4 lea edx, dword ptr [ebp-0C]
:0040158C 52 push edx

* Reference To: KERNEL32.lstrcmpA, Ord:02FCh
|
:0040158D FF1500204000 Call dword ptr [00402000]
:00401593 85C0 test eax, eax
:00401595 7516 jne 004015AD
--> saute vers bon ou mauvais message
:00401597 6A40 push 00000040

* Possible StringData Ref from Data Obj ->"CrackMe"
|
:00401599 6850304000 push 00403050

* Possible StringData Ref from Data Obj ->"Correct way to go!!"

 

    Là où il teste s'il y a des caractères ou non, il y aurait de la place car ce test ne sert à rien pour nous, car il nous force à taper quelque chose. Et faignant comme on est (je sais pas vous mais moi si :-) ) et bien pouvoir ne rien taper c'est mieux.

    Vous vous demandez peut-être comment je sais que c'est le nombre de caractères du serial. Et bien lancez le débuggeur, mettez un BreakPoint sur la ligne en 00401566, faites "Run", tapez n'importe quoi comme serial et faites Ok. Là il break ! Regardez dans la fenêtre des registres (celle en bas à gauche) et plus particulièrement le cadre "Regs". Vous pouvez voir la valeur de EAX. En mettant "123456" comme serial vous aurez 6.

    Bon et bien pendant qu'on break ici et bien on va installer notre patch ! On clique sur "Patch Mode" de la fenêtre de droite. Et là on va pouvoir taper directement en assembleur nos instructions. Donc on tape : JMP 00404890. C'est là qu'on va placer le patch en lui-même. On fait Entrée, si l'instruction est fausse il l'indique et si c'est juste il la met dans le cadre du dessous. L'avantage de le faire ici c'est de pouvoir voir la correspondance en hexa, qu'on pourra éventuellement recopier dans l'éditeur hexadécimal.

    Une fois l'instruction validée, on fait "Apply Patch", là une fenêtre de confirmation s'affiche. Si vous ne la voyez pas c'est qu'elle est cachée derrière la fenêtre actuelle. On fait "Oui" et avant de fermer la fenêtre du Patch Mode on clique sur "Copy" et on colle ça quelque part pour s'en souvenir. Les modifications effectuées ne se voient pas dans la fenêtre principale de WinDasm mais dans celle de droite.

    Là on va devoir aller à 00404890 donc on fait "F7" - Step Into ou "F8" - Step Over (peu importe). Les deux permettent d'avancer en pas à pas. Mais sachez que le Step Into rentre dans les CALL contrairement au Step Over.

    Une fois le F7 (ou F8) fait, on voit : :00404890 add byte ptr [eax], al (toujours dans la fenêtre de droite). C'est là où le JMP nous a emmené. On va insérer notre patch ici même, donc on clique sur "Patch Mode" et on met :

  • MOV byte PTR [00401595], 74
  • JMP 00401585
     
  •     Pourquoi sauter à 00401585 et pas sauter à l'élastique ? Rappelez-vous ! On avait : :0040156D 7316 jnb 00401585 --> si c'est supérieur saute, si pas de lettre ne saute pas.

        Donc il saute à cette adresse s'il y a des caractères. Donc là on pourra mettre rien du tout et il nous dira quand même que c'est bon. C'est-y pas magique ? Ben en fait non ! Car appliquez ce patch là (n'oubliez pas de copier les instructions) ! Pour le tester ensuite, cliquez sur Run. Et là plantage ! AAARRRRGGG ! Cela vient du fait que la section dans laquelle doit être effectuée le patch ne peut pas être modifiée (comme si elle était en lecture seule).
    Faites quand même les modifications que l'on vient d'effectuer dans l'éditeur hexadécimal. Si vous n'avez pas copié les instructions en hexa vous n'avez plus qu'à recommencer ! Et respectez bien les endroits !!!

        Ouvrez ensuite le crackme modifié avec StudPe. Allez dans "section", double-cliquez sur la ligne de ".text" et cochez "MEM_WRITE". Faites Save et voilà, vous pouvez maintenant le tester : notre crackme est cracké en s'auto-patchant.

        Pour remplacer le JNE par des NOP il aurait fallu inscrire :

    :00401DE0 66C6059515400090 mov byte ptr [00401595], 90
    :00401DE8 66C6059615400090 mov byte ptr [00401596], 90

     

    Petite variante

        On aurait pu mettre un CALL à la place d'un jump. Exemple :

        Voici ce qu'il y a dans le programme original :

    :00401566 8945F0 mov dword ptr [ebp-10], eax --> met le nombre de lettres du serial dans ebp-10
    :00401569 837DF001 cmp dword ptr [ebp-10], 00000001
    --> compare à 1
    :0040156D 7316 jnb 00401585
    --> si c'est supérieur saute, si pas de lettre ne saute pas


        Si on break sur la ligne en 00401566 on peut voir que EBX est tout le temps égal à 1. Donc on pourrait mettre :

    :00401566 39D8 cmp eax, ebx --> Compare notre nombre de lettres à EBX (=1), ce qui revient au même que les 2 lignes MOV et CMP.
    :00401568 E823330000 call 00404890 --> Va à l'endroit de notre patch

        Puis à l'endroit du patch, en 00404890 :

    :00404890 66C6059515400074 mov byte ptr [00401595], 74 --> Remplace le saut 75 en 74
    :00404898 C3 ret --> Retourne juste après le CALL

        Par contre si vous ne mettez rien comme serial il vous demandera d'en mettre un.


     

    Le crackme qui nous donne le bon serial !
     

        Voilà comment le crackme vous donne le bon serial !

    * Possible StringData Ref from Data Obj ->"CrackMe"
    |
    :004015AF 686C304000
    push 0040306C

    * Possible StringData Ref from Data Obj ->"
    Incorrect try again!!"
    |
    :004015B4 6874304000
    push 00403074
    :004015B9 8B4DE0 mov ecx, dword ptr [ebp-20]

     

        Le PUSH envoie en fait le texte situé au dessus que WinDasm nous montre gentiment :-). Pour le vérifier lancez le debuggeur, et tapez le numéro qui suit le PUSH dans la case "User Addr" 1 ou 2 (en dessous des registres) et cliquez en dessous sur UA1 ou UA2 selon le numéro choisi. En mettant 00403074 vous pourrez voir "Incorrect try again". Les autres messages se situent vers le même endroit (dans les 004030xx), on va donc aller dans l'éditeur hexa à l'offset 3074 par exemple. Et là on voit les autres messages dont le serial !! On repère donc son adresse en relevant l'offset du premier caractère ('<') du serial. Ici on a 3020. Donc on va remplacer le PUSH que vous souhaitez par un PUSH 00403020.

        Par exemple celui du "Incorrect..." : 6874304000 push 00403074
        On mettra à la place : 6820304000 push 00403020
     

        Je rappelle la correspondance du PUSH en hexa : PUSH = 68 et ensuite les octets sont inversés, regardez le joli (si si regardez bien ! :-P) exemple en couleur :

            6874304000     push 00403074


        Et donc une fois les modifications effectuées, on teste : on met n'importe quoi, on valide ! Et là un beau message vient nous rappeler le bon serial : <BrD-SoB>. Et n'oubliez pas de mettre les < et > pour le bon serial.



        Voilà c'est fini pour aujourd'hui, j'espère vous avoir appris quelque chose de plus avec ce cours ! Et comme toujours si vous avez un problème ou si vous n'avez pas compris quelque chose 2 adresses sont là pour ça : www.forumcrack.new.fr et deamon.crack (at) netcourrier.com.
     

     

    Deamon, le 24 et 25 juillet 2003


    Passer au cours suivant >>>
    [ Reverse Engineering sur la calculatrice Windows ]