Niveau | Outils | Auteur |
---|---|---|
Newbie+ | Ollydbg | elooo |
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.
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é :
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 !
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 :
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 :
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 :
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 :
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 :
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 !
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é.
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.
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.