Voici un article sur la résolution des « crackme », destiné à guider les débutants en reverse engineering (cet article est proposé sur le site Begin.re).
Yow,
Tout d’abord, les outils utilisés. Contrairement au site Begin.re, je ne vais pas utiliser IDA pro qui est payant, mais bien radar2 avec Cutter qui est lui, gratuit et https://www.begin.re/téléchargeable ici. Je ne vais pas non plus utiliser ollydbg pour l’analyse dynamique , mais bien www.immunityinc.com. Ces deux outils installés, nous avons tout ce qu’il faut. En effet, les crackme ici ne sont pas d’un niveau expert et ne sont pas non plus des malwares en puissance, donc pas besoin de VM, PE Bear, …
Enfin, ici je ne vais pas traiter en détail les méthodes ASM ou autres conventions. Cette solution est destinée aux personnes ayant commencé l’apprentissage de l’ASM x86 et du reverse engineering qui sont bloquées à la résolution des exercices de Begin.re .
Commençons :
Exer0_Password :
Une fois téléchargé via le site, exécutons le programme via une invite de commande. Le programme nous demande un mot de passe. Quel est-il ? et bien c’est le but de l’article, donc c’est partis !
Ouvrons l’exécutable avec Cutter (choisissez l’analyse automatique : aaa). Le programme étant maintenant désassemblé, cherchons la méthode « main » dans la fenêtre de gauche :
Dans la fenêtre du milieu se trouve le code relatif à cette méthode. Analysons le :
Il ne faudra pas longtemps avant de voir que le mot de passe attendu est « cr4ckm3« .
Comment le sait-on ? Regardons ce qu’il ce passe dans cette méthode. A l’adresse 0x004010b6 on a un push d’une chaine de caractères formant « Enter password: », rappelez-vous, lorsque l’on exécute le programme, c’est cette phrase qui s’affiche. Ensuite à 0x004010bb on fait un call vers une méthode de la crt windows traitant les entrés/sorties (stdio), on en déduit donc que c’est a ce moment la que la chaine de caractère est affichée à l’écran.
0x004010c7 on push un paramètre, encore une chaine de caractères « %s » qui en langage C est utilisé pour dire au programme que l’on attend un paramètre de type string (chaine de caractère) et à la ligne juste après, un autre appel de méthode à la lib stdio. En clair, ici on attend que l’utilisateur entre une chaine de caractères (notre mot de passe).
Si l’utilisateur entre un mot, que fait-on ensuite ? Directement on passe en argument d’une autre méthode le résultat de l’appel de la méthode précédente, donc notre mot de passe et encore une chaine de caractères « cr4ckm3 ». Et ici, ces deux argument sont envoyé à une méthode qui porte le nom de « strcmp » (string compare). Cette méthode est une méthode utilisée en C pour comparer deux chaines de caractères ensembles. Si c’est deux chaines sont identiques, le résultat est 0 (plus d’informations ici).
Maintenant je présume que vous commencez à connaître la chanson, on regarde la suite et à l’adresse 0x004010e8 on teste le résultat renvoyé par strcmp. Pour rappel « test » effectue un et (AND) logique. Donc ici, si, le résultat est différent de zéro on passe à l’adresse 0x00401109 sinon on continue. Et si l’on continue, on voit que l’on affiche (push + call) une chaine de caractères qui est : « Correct!\n ». On en conclu donc que notre mot de passe doit être égal à « cr4ckm3 ».
Voilà, le 1er exercice est fini. La suite ?
Exer1_GoodLuck :
Comme pour le programme précédent, téléchargeons le et exécutons le :
Il ne se passe rien, essayons avec un paramètre :
Toujours rien. Bien, voyons le code. Chargeons le programme avec Cutter. Voici la fonction « main » :
Que peut-on y trouver ? Et bien tout d’abord, à l’adresse 0x00401043 on trouve que l’on compare [arg_ch] à deux. Pour information [arg_ch] est l’argument de la
fonction main qui contient le nombre de paramètres (argc) passés au programme. Ici, on sort du main si le nombre d’arguments n’est pas deux. Le programme attend bien un argument.
Alors pourquoi lors de la seconde tentative celui-ci ne faisait rien non plus ? Voyons la suite.
De l’adresse 0x001049 à 0x0040104f, on appelle une méthode de nom « atoi » en lui donnant le paramètre de la ligne de commande. Atoi est une fonction qui permet de convertir une chaine de caractères en entier (int). Et à l’adresse suivante, si le résultat de la fonction n’est pas correct, on sort du programme. Dans notre cas, convertir « test » en entier renvoie d’office une erreur, d’où le pourquoi nous sortions du programme directement. Si l’on test avec un nombre :
Tout se passe bien, sauf que ce n’est pas le nombre attendu. pour trouver le nombre attendu, il faut aller un peu plus loin dans le code, juste après la conversion (adresses 0x0040105c à 0x0040105f). On met dans le registre eax le résultat de l’expression eax + eax x 4. Ensuite on compare la valeur de eax à la valeur hexadécimale 0x181a (6170 en décimale). Ici la solution réside dans la différence entre l’instruction mov et lea. Mov déplace une valeur située à une adresse, lea déplace simplement une valeur. Ici la valeur résultant de eax + eax x 4, soit 5 fois eax. Et si l’on regarde attentivement eax est le résultat de la conversion en entier.
Donc pour résumer, le nombre recherché est quelque chose x 5 qui est égale à 6170. Si l’on fait 6170/5 = 1234. Testons :
Exer2_Juila :
Ce crackme est un peut plus complexe, mais rien de très méchant vous allez voir. Commençons comme d’habitude et exécutons ce programme :
Ok, encore une fois c’est une application qui attend un mot de passe en paramètre. Regardons dans Cutter le code :
On voit qu’il y a déjà un peu plus à manger que dans les programmes précédents. Encore une fois le programme teste si l’on a bien deux arguments en paramètre, si oui, on va à l’adresse 0x00401063 sinon on affiche « please provide the password » et l’on sort.
Que ce passe-t-il à l’adresse 0x00401063 ? Et bien en gros il y a un appel à une méthode « strlen » qui prend en paramètre notre argument (arg_ch).
Strlen renvoie la taille d’une chaine de caractères et en voyant le malloc en dessous on comprend que l’on va allouer de la mémoire d’une taille équivalente à notre argument + 1 (add eax, 1 à l’adresse 0x00401080). On ajoute un car en C/C++ ce qui définit la fin d’une chaine de caractères est la valeur 0 que l’on place à la fin et strlen n’en tient pas compte.
On voit que le pointeur vers la mémoire allouée est placé dans une variable locale (local_4h) que pour plus de visibilité nous pouvons renommer (Shift + N). Ici je la renomme en « p_malloc » .
La suite, à l’adresse 0x004010b2 on appel une méthode nommée strcpy et si l’on regarde les paramètres, on trouve arg_ch (arg_ch + 4) qui est notre chaine de caractères passée lors du lancement du programme et le pointeur p_malloc.
Strcpy copie une chaine de caractères dans une autre, ici le programme copie notre chaine de caractères dans l’espace mémoire alloué précédemment. Si cela ce passe mal, on affiche « input copying to array failed » et on appelle « free » pour libérer l’espace.
Si tout se passe correctement, on se retrouve à l’adresse 0x004010e9. A cet endroit sans rentrer dans les détails on constate que l’on rentre dans une boucle qui itère sur p_malloc. Boucle dans laquelle un appel à une fonction mystérieuse (fcn.00401160) est fait. Cette fonction prend en paramètre un caractère de la chaine à l’adresse p_malloc à chaque tour.
Après avoir bouclé sur toute la chaine, on compare celle-ci à la valeur « VIMwXliFiwx« . Si c’est la même chose, on affiche « brava!!! » et c’est gagné, sinon on affiche « Incorrect Password ».
Ok, donc cela veut dire que la fonction fcn.00401160 applique un traitement à notre chaine de départ. Voyons le code de cette fonction :
Alors, cela donne que notre caractère est testé pour voir s’il est contenu entre 0x61 et 0x7a ou entre 0x41 et 0x5a. Comme nous l’indique ici Cutter, 0x61 = 97 = le code la lettre ‘a’, 0x7a = 122 = ‘z’, 0x41 = 65 = ‘A’ et 0x5a = 90 = ‘Z’. En résumé, on regarde si notre caractère est bien une lettre. Si c’est le cas on ajoute à la valeur numérique de notre lettre la valeur située à [0x403008]. C’est ici que l’analyse dynamique avec immunity debugger entre en jeu.
Ouvrons notre programme dans le debugger et retrouvons l’expression de l’adresse 0x00401179.
Ajoutons maintenant un « watcher » dans la fenêtre « watch expressions ». Nous allons ajouter « DS:[1103008] » pour voir la valeur qui se cache à cette adresse.
000000004 ! Donc cela veut dire que cette fonction ajoute 4 à la valeur numérique de notre caractère. En cryptographie cela s’appelle un code de césar. Donc pour trouver notre mot de passe, nous devons faire l’inverse avec VIMwXliFiwx.
Le résultat est : « REIsTheBest » 🙂
Retrouvez aussi mes articles sur mon blog DCK's blog.