Mars - Crackme3 : Solution par elooo

Valid XHTML 1.1! Valid CSS!

NiveauOutilsAuteur
Newbie+Ollydbgelooo

Sommaire

Introduction

Tout d'abord, pour ceux qui voudraient mettre ce tutorial en pratique, le crackme est dispo ICI.

Ensuite afin que tout le monde puisse en profiter, on va corriger une petite négligence de coding, qui fait que le crackme crashe sur un windows différent de XP.

Image 1

Il va donc falloir xorer ebx avant de l'utiliser comme indexeur, par contre on manque de place, il nous faudrait de la place pour 2 opcodes, mais on a juste un nop avant. On pourrait rediriger plus bas, mais j'ai vu un truc plus propre :p
En effet, à peine au-dessus en voit que eax est xoré :

Image 2

Or les valeurs de retour d'une fonction d'API sont toujours renvoyées dans eax, donc eax va de toutes manières être écrasé. Quel intérêt de le xorer avant étant donné qu'il n'est pas non plus pris en paramètre ??
On va donc xorer ebx à la place :)
Remplacez donc le

0040119B   .  33C0          XOR EAX,EAX

par un

0040119B      33DB          XOR EBX,EBX

Et vous sauvez les modifications :)
Maintenant passons au boulot proprement dit !

A la chasse aux anti-debugging

Mars en proposant son crackme a trop parlé : "remarque2: si vous pensez avoir trouvé une solution valide, verifiez la sans le debugeur"
Ce qui permet d'affirmer avec certitude qu'il y a des détections de debugging qui vont effectuer des modifications si un debugger est détecté. Donc on va être prudent et on va tout tracer :>

On charge le prog dans olly et à peine plus bas on voit :

Image 3

DialogBoxParamA va chercher DlgProc en 004030A0, donc on fait Click-droit -> Go To -> 004030A0, on pose un bp (F2) et on fait F9 afin de breaker au début de DlgProc. Refaites F2 pour enlever votre bp, on ne sait jamais... je vous rappelle qu'on cherche des tricks anti-debugging justement :D

Le premier trick anti-debug est ici :

Image 4

En ce qui me concerne j'avais patché mon olly en remplaçant toutes les occurences de OLLYDBG ou Ollydbg par Ellydbg.
Le crackme compare donc (chez moi) LLY à lly, par conséquent j'échappe à la détection sans faire aucune modif.
Sinon il suffit de nopper le saut.

En ce qui concerne le deuxième trick, c'est un peu plus subtil pour les débutants :

Image 5

Pour mieux comprendre faisons la comparaison avec la fonction d'API IsDebuggerPresent de Kernel32.dll

kernel32!IsDebuggerPresent:
77E72740 64:A1 18000000 MOV EAX,DWORD PTR FS:[18] ; pointe sur le TIB du thread
77E72746 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30] ; pointe sur le PEB 
77E72749 0FB640 02 MOVZX EAX,BYTE PTR DS:[EAX+2] ; champ BeingDebugged du PEB
77E7274D C3 RETN

Le registre FS pointe sur le TEB (Thread Environnement Block) et pour accéder au code il suffit d'employer une adresse linéaire (FS:[18] Ceci dit FS:[0] pointe vers la même zone mémoire que FS:[18] mais utiliser une adresse linéaire devient utile dans un souci de portabilité). Le pointeur lu en FS:[18] permet d'accéder à d'autres valeurs du TEB.
Il y a un TIB (Thread Information Block) pour chaque thread lancé sous Win 32. FS:[18] permet de récupérer l'adresse du TIB du thread actif, [TIB+30] récupère l'adresse du PEB (Program Environment Block) et [PEB+2] pointe sur le flag de debug.
Si on revient au deuxième trick anti-debugging présent dans le crackme de mars, on remarque vite que c'est l'équivalent de IsDebuggerPresent (FS:[30] -> on pointe sur l'adresse du PEB du thread actif, on l'incrémente de deux et on récupère la valeur du flag BeingDebugged).
Si le programme n'est pas débuggé, le flag est à 0. Donc si vous n'utilisez pas le plugin qui va bien pour Ollydbg, si vous avez débugger sous un autre outil, assurez-vous que AL est à zéro sinon vous vous dirigez droit vers le mur :)

Une fois ces deux tricks correctement passés vous devez voir "LOLO" dans la fenêtre de dump à la place de MARS.

Mais attention, un troisième piège se cache dans le code, voyons voir ça :p
On pose un bp (F2) en 004011CE, puis on fait F9, on entre un nom et un serial bidon et on clique sur "Vérifier".
On breake. On trace doucement avec F8 et comme c'est bizarre, on arrive vers une liste d'opcode qu'ollydbg n'interprète pas comme du code. Voyons voir :

Image 6

En fait, juste avant, ces opcodes vont être xorées afin de récupérer quelque chose qui ressemble à du code. Une fois ce code passé, les opcodes vont être re-xorées afin de les re-crypter (ou camoufler, comme vous préférez).
Une fois ce petit bout décrypté on a :

Image 7

En fait il va chercher un bp dans toute la fonction qui va récupérer le nom et le serial. Et si il en trouve, eh bien il va utiliser LOLO pour l'algo de vérif de serial et non LOLA.
Mais c'est facilement contournable, un inversement de saut pendant que la routine est décryptée et hop on n'en parlera plus, ou sinon vous évitez de posez des bp là où il va checker :)

Passons à l'algo maintenant !

Comprendre l'algo de vérification du serial et le reverser

Image 8

Tout d'abord, qu'il s'agisse du nom ou du serial, on va incrémenter le compteur qui va se charger de pointer vers le bon byte jusqu'à 0Eh (soit 14, autrement dit de 0 à 14, donc 15 caractères). Si le nom ou le serial ne comporte pas 15 caractères, ce n'est pas un problème puisque qu'ensuite on aura des 0 dans le buffer.
Par contre le résultat de l'addition est toujours stocké sur 2 bytes (AL ou DL), ce qui sous-entend que AL, en fonction du nom entré variera de 00 à 0FFh, et DL variera de 00 à 0FFh en fonction du serial entré.

Image 9

On additionne EAX et EDX, or on a constaté auparavant que AL variera de 00 à 0FFh et idem pour pour DL, donc EAX, après l'addition avec EDX aura une valeur comprise en 00000000 et (000000FFh + 000000FFh), c'est-à-dire entre 0 et 1FEh.
Donc après l'addition avec 414C4F4Ch et EAX, on aura EAX compris entre 414C4F4Ch et 414C514Ah, et c'est sur ses nouvelles valeurs de EAX que sera calculée la racine carrée.

Si on calcule la racine carrée de 414C4F4Ch, on obtient 814Bh, et si on calcule celle de 414C514Ah, on a également 814Bh, on peut donc considérer cette valeur comme une constante dorénavant, puisqu'on sait que quel que soit le nom et le serial rentré, après le calcul de la racine carrée par le FPU on aura toujours 814Bh dans ECX.

Image 10

Quand j'ai vu cette boucle avec un compteur de 814Bh,j'ai eu mal au crane,mais je me suis dit que c'était cool car déjà on savait que 814Bh était fixe. Je me suis pas cassée la tête j'ai reversé le crackme au niveau de ses fonctionnalités pour qu'il me donne toutes les bonnes valeurs d'ESI qui me feraient aller à GoodBoy.
Amusez-vous à debugger crackme3m_reverse.exe, vous verrez quelles modifs j'ai faites.
En gros j'ai transformé le crackme en bruteforcer : il part de 414C514Ah et décrémente jusqu'à 414C4F4Ch afin de choper tous les ESI corrects.
On obtient alors :

414C5147h
414C5101h
414C50FBh
414C50D7h
414C50ABh
414C507Bh
414C506Fh
414C5065h
414C5057h
414C5053h
414C500Bh
414C4FF7h
414C4FDBh
414C4FBDh
414C4FBBh
414C4FB1h
414C4FA9h
414C4FA3h
414C4F9Dh
414C4F8Dh
414C4F79h
414C4F75h
414C4F5Dh
414C4F5Bh
414C4F51h
414C4F4Fh

Soit 26 ESI potentiellement corrects. Si on veut continuer à reverser l'algo, cette fois, il faut soustraire ALOL ( 414C4F4Ch) à toutes ces valeurs :

000001FBh
000001B5h
000001AFh
0000018Bh
0000015Fh
0000012Fh
00000123h
00000119h
0000010Bh
00000107h
000000BFh
000000ABh
0000008Fh
00000071h
0000006Fh
00000065h
0000005Dh
00000057h
00000051h
00000041h
0000002Dh
00000029h
00000011h
0000000Fh
00000005h
00000003h

Et maintenant pour récupérer un serial valide pour un nom, c'est très très très simple, puisque les valeurs juste au-dessus qu'on a obtenu correspondent à EAX après l'addition de EAX à EDX :

0040120F   > /0281 40304000 ADD AL,BYTE PTR DS:[ECX+403040]
00401215   . |0291 50304000 ADD DL,BYTE PTR DS:[ECX+403050]
0040121B   . |41            INC ECX
0040121C   . |83F9 0E       CMP ECX,0E
0040121F   .^\75 EE         JNZ SHORT crackme3.0040120F
00401221   .  03C2          ADD EAX,EDX                    ;  eax = eax + edx  <---- LA !!

On se souvient qu'EAX n'a que AL d'occupé, et que AL contient l'addition des 15 caractères du nom mais uniquement stockés sur 2 bytes, donc quand on dépasse 2 bytes, on ne prend pas en compte ce qui serait normalement contenu dans AH.

Exemple avec elooo:
AL = 0
AL = 0 + 65h = 65h
AL = 65h + 6Ch = 0D1h
AL = 0D1h + 6Fh = 40h 
(normalement ça fait 140h mais le 1 est ignoré car "dépasse" la capacité de stockage d'AL)
AL = 40h + 6Fh = 0AFh
AL = 0AFh + 6Fh = 1Eh (normalement 11Eh)

A l'issue on a donc AL = 1Eh et comme EAX est xoré avant on obtient EAX = 0000001Eh

A partir de là, ayant constaté que mars ne demandait qu'un caractère au minimum pour le serial, je ne me suis pas cassée la tête pour le keygen, d'autant plus qu'il existe plusieurs serials de un caractère pour chaque nom :
Il suffisait après l'addition des caractères du nom dans AL de soustraire cette valeur obtenue aux 26 autres valeurs présentes ci-dessus, et en ce qui me concerne je n'ai conservé que les serials de un caractère compris entre 21h (inclus) et 7Eh (inclus), et je stocke tout ça dans un fichier.

Mon keygen est dispo ICI.


Cordialement,
elooo.