Cracking : Comprendre la pile

To the non-french speaker, note that you can translate the articles using the Google Trad widget situated at the bottom of all pages.


Dans l’article précédent j’ai laissé pas mal de mystères quant à ce type d’instructions :

mystere.png

Je vous avais juste dit qu’on chargeait ce qui est contenu à l’adresse RBP-0x1c dans le registre RDX. Aujourd’hui je vais donc vous expliquer d’où vient cette notation et surtout à quoi elle sert.

code

 

Le programme est simple : on demande à l’utilisateur d’entrer le code qui permet de déverrouiller le coffre fort via un scanf. Puis on envoie la valeur entrée à la fonction vérification qui compare le code entré au code du coffre fort. Si les deux sont égaux alors on retourne la valeur 0 sinon on retourne la valeur 1 (on simule un booléen). Suivant la valeur de la réponse, on affiche un message d’échec ou de réussite.

Du coup pas de mystères, la solution de ce crackme est codée en dur ce qui veut dire qu’avec un logiciel d’analyse tel que IDA (que vous trouverez dans la section Downloads en version Free pour Linux) ou radare2 vous le trouverez très facilement. Cependant le but de cet article ne vas pas être de trouver la solution du crackme mais de comprendre comment fonctionne la pile qui est un élément fondamental en assembleur.

 

I/ Un peu de théorie

La pile est une structure de type LIFO (Last In First Out). Quand on empile sur la pile on empile par le bas et plus on empile plus les adresses deviennent petites :

pile.png

La pile n’a techniquement pas de taille maximale en revanche sa taille varie en fonction du contenu du programme. Pour toujours savoir où elle commence et où elle se termine nous disposons de deux registres : EBP et ESP.

EBP est le registre qui contient l’adresse mémoire du début de la pile (l’adresse la plus haute de la pile) tandis que ESP contient l’adresse mémoire de la fin de la pile (l’adresse la plus basse). La première conséquence de cette architecture est que à chaque fois qu’on va placer un élément sur la pile (via l’instruction push), l’ESP sera modifié :

push

Deuxième conséquence de cette architecture, toutes les valeurs placées sur la pile seront contenues entre l’adresse stockée dans le registre EBP et l’adresse stockée dans ESP.

Comme je vous l’ai dit dans l’article d’introduction à l’assembleur, à chaque fois que le programme appelle une fonction, une stack frame est créée. Cette stack frame est un espace dédié à la fonction appelée. Elle et elle seule y aura accès. Si dans notre programme nous avons deux fonctions (un main et une fonction fonc()) alors on aura deux stack frame dissociées l’une de l’autre :

push

Maintenant la question a laquelle il va être intéressant de répondre est : « comment est-ce que l’on passe d’une stack frame à l’autre  » ? Cela peut paraître simple et pourtant il y a plusieurs mécanismes utilisés. Pour comprendre tout ça,  je vous propose d’analyser le crackme de A à Z.

Cette deuxième partie va être longue et dense en informations mais elle aura au moins le mérite de répondre à toutes les questions que vous pourrez vous poser. Relisez la plusieurs fois tout en exécutant le programme via un déboguer et n’hésitez pas à me contacter si vous avez des questions 🙂 !

II/ Analyse du crackme

Le programme commence par l’affichage du coffre fort :

coffre

L’affichage du coffre fort ainsi que de la phrase « Entrez le code : » se fait via cet ensemble d’instructions qui est répété plusieurs fois :

chargementstring.png

Le plus important ici c’est l’instruction :

lea eax, [ebx-0x188c]

qui charge une chaîne de caractères contenue à l’adresse mémoire EBX-0x188c dans le registre EAX. La première chaîne de caractères chargée correspond au premier printf() :

dessuscoffrefort.png

Puis le registre EAX est pushé sur la pile et le programme fait appel à la fonction puts qui va afficher la chaîne de caractère.

Lorsqu’il s’agit d’afficher du texte, le compilateur utilisera deux fonctions : puts et printf. Pourquoi l’une et pas l’autre ? Eh bien puts est plus rapide à exécuter que printf(). Sachez aussi que la fonction puts ajoute par défaut un « \n » (retour à la ligne) à la fin de la chaîne de caractères. Enfin, puts ne peut pas formater une string. Autrement dit vous ne pourrez pas faire ce genre de manipulations avec puts :

puts("Hello %s", name);

Mais vous pourrez avec printf(). Que vous utilisiez printf() ou puts() dans votre code C ne changera rien puisque de toute manière le compilateur fera les modifications à votre place.

Une fois l’affichage fait, on demande à l’utilisateur d’entré un code qui sera capturé par la fonction scanf :

scanf.png

Je ne vais pas détailler le fonctionnement de la fonction scanf pour plusieurs raisons. La première c’est qu’elle est longue. La seconde c’est que je ne m’y suis jamais vraiment intéressé donc je ne la connais pas ahah ! En revanche ce qui est intéressant ici c’est de voir que le résultat de la fonction est déplacé dans le registre EAX qui est lui même pushé sur la pile juste avant l’appel de la fonction verification() :

avantappel.png

Plus précisément, on peut affirmer que le résultat de la fonction scanf est stocké à l’adresse EBP-0x10.

Vu que le contenu du registre EAX a été pushé sur la pile, on devrait le retrouver sur le dessus de cette dernière :

avantappel.png

On voit bien que le denier élément empilé est l’input de l’utilisateur soit 0x1e240 (123456). Ensuite on arrive à l’appel de la fonction verification qui se fait via cette instruction :

call verification

L’instruction call est un condensé de ces deux instructions :

push eip
jump verification

Dans un premier temps on va push sur la pile le contenu du registre EIP. Pour rappel, le registre EIP contient l’adresse mémoire de la prochaine instruction à exécuter. Autrement dit on push sur la pile l’adresse mémoire de l’instruction qui suit l’appel de la fonction verification (soit l’adresse 0x56555696) :

etatstack.png

Puis on jump sur la fonction verification dont le contenu est le suivant :

verification.png

A force d’analyser des fonctions en assembleur vous avez dû vous rendre compte que dans chaque fonctions il y a deux ensembles d’opérations qui se répètent au début de la fonction :

push ebp
mov ebp, esp

et à la fin de la fonction:

pop ebp
ret

Ces instructions sont appelées respectivement le prologue et l’épilogue d’une fonction et permettent de passer d’une stack frame à une autre sans altérer l’état de la pile.

Étant donné que l’on vient d’entrer dans la fonction verification, la première instruction qui sera exécutée est la première instruction du prologue de la fonction soit :

push ebp

Pourquoi fait-on cela ? L’idée derrière tout ça c’est de sauvegarder la valeur d’EBP pour justement ne pas écraser les données déjà présentes dans la pile.

Du coup si on regarde la valeur d’EBP juste avant l’appel de la fonction verification on trouve ça :

ebp

Et si on regarde le contenu de la pile une fois qu’on est dans la fonction verification on obtient ça :

stack

De haut en bas on retrouve bien en premier la valeur sauvegardée d’EBP, l’adresse de la prochaine instruction à exécuter (la sauvegarde d’EIP) et enfin le paramètre envoyé à la fonction !

On passe à la seconde instruction du prologue :

mov ebp, esp

Cette instruction va permettre de faire pointer le registre ESP sur le registre EBP, c’est grâce à cette instruction que va être créé la stack frame de la fonction verification. Voici un schéma qui résume l’état actuel de la pile :

ebpesp

Les prochaines instructions constituent le corps de la fonction verification :

ebpesp

Je passe volontairement la fonction get_pc_thunk pour la bonne et simple raison que je ne sais pas ce qu’elle fait. J’ai fait des recherches pourtant mais…. Wallou ahah !

Le plus important dans cette fonction c’est la ligne de comparaison entre le code envoyé par l’utilisateur qui est présent sur la pile à l’adresse EBP+8 et la valeur hexadécimale 0x27009 qui se traduit en décimal par : 159753. Suivant le résultat de la comparaison, on mov dans le registre EAX la valeur 0 ou 1 (c’est la simulation de booléen dont je vous ai parlé plus tôt) et on arrive à l’épilogue de la fonction qui est :

pop ebp
ret

L’instruction pop permet de récupérer les quatre octets présents sur le dessus de la pile et de les placer dans le registre indiqué, ici EBP. Or les quatre derniers octets de la pile à ce moment de l’exécution du programme correspondent à la sauvegarde d’EBP que nous avons fait plus tôt :

popebp

En faisant ça, on va  indiquer au programme que l’on veut retourner dans la stack frame du main. Oui je vous l’accorde, c’est mal dit mais l’idée est là. Pour finir le programme exécute la dernière instruction de l’épilogue :

ret

Cette instruction est en fait l’équivalent de celle ci :

pop eip

Vous l’avez compris, ici on va tout simplement faire popé les quatre octets présents sur le dessus de la pile. Et miraculeusement (ou pas), les quatre derniers octets correspondent à la sauvegarde d’EIP. En faisant ça on va tout simplement indiquer au programme que la prochaine instrution à exécuter est l’instruction qui suit l’appel de la fonction verification dans le main :

backtomain.png

Soit l’instruction :

add esp, 0x10

Voilà ! Si vous avez compris cet article vous avez compris le fonctionnement de la pile et vous pourrez cracker tout et n’importe quoi ! Bon en vrai pas tout parce que parfois vous trouverez des protections mais je vous montrerai -un de ces jours 😉 – comment bypass une protection !

Si vous avez des questions et/ou si vous avez repérés des coquilles je vous invite à me contacter via ma page Facebook.

3 commentaires

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Google

Vous commentez à l'aide de votre compte Google. Déconnexion /  Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s