|
CRACKME2
DE CHRISTAL
|
L'objet de cert essai est pour le moins insolite, car nous allons voir comment cracker... une daube !! De l'avis même de son concepteur, Christal, qui déclare modestement qu'il n'est "ni codeur, ni crackeur, ni quoi que ce soit", il n'y a rien à attendre de ce crackme. Et pourtant ! Au programme on retrouve du code polymorphe, des anti-softice, un anti ForgIce, le tout solidement encrypté... Pour une daube, je la trouve bigrement bien ficelée. Enfin, trêve de considérations culinaires, passons au cracking.
| I°) DUMP ET EPURATION DU LISTING |
Commençons par faire un petit tour du crackme, histoire d'avoir un petit aperçu de la bête. On le lance et on a sucessivement deux MessageBox pour nous inviter à nous défaire de notre cher débugger. Une fois la fenêtre principale chargée, on a encore droit à une MessageBox quand on clique sur "About" (tiens c'est curieux le concepteur à protégé son nom, d'habitude c'est la dernière chose qu'il tient à cacher...:-) et une autre lorsque, découragé, on essaie de sortir du programme.
Par pure curiosité on peut aussi tenter que placer un BPX histoire de voir ce que ce crackme a dans le ventre et là HORREUR ! Le code est polymorphe ! A chaque F8 ou F10 tout le code affiché dans la console change sans prévenir ! La seule instruction qui est valable est celle à eip, toutes les autres sont à peu près incompréhensibles et de toutes façons elles changent tout le temps...
Pour finir ce petit tour on lance un analyseur d'exécutable et bien sûr, il est aussi crypté (par "PE-Encrypt" ou "VGCrypt" selon l'analyseur utilisé).
Bon ! Puisque le traçage semble quasi-impossible on va opter pous une approche de type "dead-listing", c'est à dire dumper le programme, et essayer de le desassembler pour travailler à partir du listing.
Le dump ne pose pas de problème, la technique est hyper-classique (si besoin, voir les tut sur les protections par compression/cryptage à christalpage.cjb.net). On commence par éditer lescaractéristiques des sections et à les remplacer par E0000020. Ensuite on charge le crackme dans le Symbol Loader de Softice et on le lance. L'une des premières instructions de PE-Encrypt étant "pushad", on va rechercher un "popad "pour localiser la fin de PE-Crypt et trouver le saut vers l'Entry Point (EP).
Malheureusement, le code de PE-Encrypt lui-même semble crypté car, après avoir atteint le pushad, on trouve bien un popad un peu plus bas, mais les instructions qui suivent n'ont pas l'air très sympathiques (pop eax et jmp 401CCC). Par contre si on continue à tracer, ces intructions se transforment en : popfd, mov ebx [xxx], mov [xxx] ecx, jmp ebx. PE-Encrypt décrypte au fur et à mesure ses propres instructions de manière à compliquer le patch. Le "jmp ebx" est bien le saut vers EP, et on relève la valeur de l'EP, 401019. Ensuite, il suffit de remplacer "jmp ebx" par "jmp eip" et de faire un "full dump" à l'aide de ProcDump. Et pour obtenir un dump exécutable, on finit par éditer l'EP du dump et remplacer la valeur par 401019.
C'est maintenant les desassembleurs qui entrent en scène. Personnellement, j'ai l'habitude d'utiliser WinDasm mais dans notre cas, pas de chance ! Le pauvre WinDasm est complètement déboussolé par le code polymorphe est nous sort un listing immonde. Qu'à cela ne tienne ! Voilà une bonne occasion de découvrir IDA. On lance le desassemblage et une bonne surprise nous attend. Je commence à comprendre pourquoi les +crackers ne jurent que par IDA, car le listing qu'il nous sort est impeccable et nous permet même de comprendre le polymorphisme. En voici un extrait :
loc_401457: ; CODE XREF: .text:00401452j cmp ax, 0BBBh jnz short loc_4014A1 shr eax, 10h or ax, ax jnz short loc_4014A1 jmp short loc_40146A ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ db 0CDh ; Í db 20h ; db 0EAh ; ê ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ loc_40146A: ; CODE XREF: .text:00401465j call sub_401982 test eax, eax jmp short loc_401476 ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ db 0CDh ; Í db 20h ; db 0C7h ; Ç ;ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ loc_401476: ; CODE XREF: .text:00401471j jnz short loc_40148E push 40319Ah |
On voit que le code est truffé de "db 0CD, db 20, db XX", plus un saut pour les éviter ce qui décale tous les opcodes et les rendent illisibles. Simple mais terriblement efficace ! Cependant, le listing est quand même par endroit assez difficile à suivre car plus de la moitié des lignes sont parasitées par les "db", et mieux vaut s'en débarrasser. J'ai opté pour une méthode d'éradication aussi simple que bourrine : le CTRL-X à répétition. Il y a certainemlent une méthode plus fine de faire le boulot (du genre on parcourt l'exe en recherchent "0C20" puis on les élimine avec les deux octets qui précèdent (le jmp short) et celui qui suit (le 3e db); ensuite on incrémente un compteur qui servira à modifier tous les sauts et les calls qui doivent rester), mais bon....
Après une bonne demi-heure à pourchasser les "db" dans les moindres recoins, on va enfin pouvoir passer aux choses sérieuses.
| II°) LOCALISATION DES PROTECTIONS ET PREMIER PATCH (voir aussi IV°) |
L'étape suivante va consister à débusquer les protection anti-SI et à les patcher. Mais comment localiser dans ce listing de 12 pages les quelques lignes des procédures anti-SI ? En fait le problème est double, car à priori on ne sait pas OU sont situées ces routines, ni à QUOI elles ressemblent.
La solution la plus simple serait d'utliser FrogIce. Mais comme je l'ai annoncé en introduction, cet utilitaire reste sans effet car si on le charge puis on lance le crackme, rien ne se passe... pour une fois qu'on aimerait bien voir ce sympathique écran bleu sans lequel Windows ne serait pas vraiment le même, rien ! C'est pas grave, on va se débrouiller autrement.
On va faire l'hypothèse suivante : chaque détection de Softice entraine l'apparition d'une MessageBox. En lançant une recherche sur la chaîne "MessageBoxA" puis en remontant dans le listing, on devrait pouvoir localiser tous les anti-SI.
La recherche sur "MessageBox" donne 4 occurences. Il faut maintenant vérifier que ces MessageBox concernent bien des messages d'erreur : pour cela il faut aller voir la chaine de caractères qui est poussée en argument (avant dernier push)..
Pour trois des quatre appels trouvés on dispose directement des adresses des chaines qui constituent le corps du message : 0040319A (deux fois) et 0040314F.
Mais le quatrième n'a pas pour argument une adresse fixe, mais "ebp+08" (l'argument a été poussé dans la pile au préalable). Par ailleurs cet appels fait partie d'une "subroutine", donc il va falloir remonter au niveau de l'appel de cette routine pour retrouver l'adresse. Le "+08" de "ebp+08" indique que l'adresse que l'on cherche est le dernier argument poussé dans la pile juste avant le call. En effet, à l'appel d'un call le processeur commence par sauver dans la pile l'adresse de l'instruction appelante pour le retour (ret) et cette adresse est au format dword (4octets). Ebp+08 pointe donc sur la fin du dword poussé juste avant.
On lance donc une recherche sur "401913" pour localiser les call sub_401913, et on trouve trois appels, dont deux suivent un push 40319A et un un push 4031BF.
On dispose desormais des adresses des messages, alors pas de temps à perdre, on hex-édite vite fait, et là... pas de bol, on tombe sur des chaînes incompréhensibles : elles sont cryptées.
La solution de rechange consiste à relancer le programme, et à provoquer un break en espérant qu'à ce moment les chaînes soient déjà décryptées. C'est le cas si on pose un bpx MessageBoxA après le chargement de la fenêtre puis qu'on clique sur "Exit". Ensuite avec la commande "d" on consulte le contenu des trois adresses :
Seules les deux premières adresses nous concernent donc, la troisième contient sans doute le message du "About". Maintenant, on va donc reprendre les 5 appels qui nous interessent et en remontant dans le listing, essayer de trouver des ensembles "call+test+saut conditionnel" que l'on connait bien....
Cette nouvelle recherche nous conduit à trouver pas moins de 5 adresses de procédures anti-SI ! Ce sont 40192F, 401982, 4019A7, 401AE4 et 401B1C.
Sur les cinq on en a trois de la forme :
S u b r o u t i n e sub_4019A7 proc near ; CODE XREF: start+81p xor ebx, ebx push 4019E6h push large dword ptr fs:0 mov large fs:0, esp mov esi, 4647h mov edi, 4A4Dh int 3 ; Trap to Debugger pop large dword ptr fs:0 add esp, 4 mov eax, ds:dword_4034C7 retn sub_4019A7 endp |
et deux autres qui ressemblent plutôt à çà :
; S u b r o u t i n e sub_401B1C proc near ; CODE XREF: .text:0040188Fp push 0 push 80h ; '€' push 3 push 0 push 3 push 0C0000000h push 4031F1h call j_CreateFileA cmp eax, 0FFFFFFFFh jz short locret_401B3F <-SI non détecté, eax reste à ff..ff xor eax, eax <- SI détecté, eax mis à 0 locret_401B3F: ; CODE XREF: sub_401B1C+1Fj retn sub_401B1C endp |
On va enfin pouvoir passer au patch. En regardant de plus près les tests qui suivent chacune de ces routines, on constate que le résultat "SI détecté" est toujours symbolisé par eax=0 et "SI non trouvé" par eax !=0. Il faut donc faire en sorte qu'à la fin de chacune de ces routines eax soit non nul.
Pour les trois premières, le résultat du test placé dans eax est copié depuis 4034C7, donc, si on remplace 4034C7 par une adresse qui pointe sur un octet dont on est sûr qu'il est non nul, le tour est joué ! On pourrait trouver des centaines d'octets qui conviennent et pour ma part j'ai choisi 403189 qui pointe sur le début d'une chaine de caractères.
Enfin, pour les deux autres routines on peut par exemple remplacer la séquence "jz+xor" par un "xor eax, eax" suivi de "inc eax", de telle sorte que quel que soit le resultat de CreatefileA, eax vaudra toujours 1.
Voici donc notre patch :
mov dword ptr [401B17], 9040C033 <- remplace les "jz+xor" par des "xor+inc+nop" mov dword ptr [401B3B], 9040C033 mov dword ptr [40199C], 8931 <- remplace les 4034C7 par des 403189 mov dword ptr [401952], 8931 mov dword ptr [4019E1], 8931 jmp 00401019 <- saut vers l'EP |
| III°) COMMENT PATCHER PE-ENCRYPT ? |
Bien, maintenant que nous avons le patch, il reste deux choses à faire : trouver de la place pour le loger puis modifier le "jump vers Entry Point" (EP) en en "jump vers patch". Et c'est là que les choses se compliquent sérieusement...
Pour commmencer, on a vu précédemment que le jmp EP était lui-même décrypté, ce qui réduit à néant tout espoir de modification en dur. Une solution pourrait être de repérer la routine qui décrypte le saut et de la détourner dès qu'elle est finie vers un second patch. Mais cette technique est assez lourde, et de toutes façons, on aura toujours besoin de place pour caser nos patchs, donc passons au deuxième point, la recherche de place libre. Avant, remarquons tout de même que le contenu de ebx a été récupéré en 401D5B.
Pour rechercher la place libre on hex-édite l'exécutable original, pas le dump, bien sûr. On trouve deux plages pleines de 00 suffisamment grandes pour pouvoir faire l'affaire : entre 403200 et 4035F0 et entre 4060B0 et 40611F0. Mais voilà ! Cet espace n'est en réalité pas libre du tout ! Si on place un bpr 4060B0 40611F0, par exemple, Softice breake. On attérit ici :
|
Cette routine de décryptage n'est pas compliquée du tout. Elle repose en fait sur un simple XOR : le caractère à décrypter est chargé dans eax, additionné à ebx puis on réalise un XOR entre eax et ebx. Ensuite, ebx qui sert en fait de clé de cryptage subit diverses modifications (ror, soustraction puis multiplication par deux).
Cette routine est appelée 3 fois et sert à décrypter de vastes portions de PE-Crypt :
Tout çà, c'est bien joli, mais puisqu'il n'y a pas de place libre, où va-t-on bien pouvoir placer notre patch ?? Réfléchissons un peu... On a vu que la routine de décryptage est commandée par un compteur (ecx), donc, si on pouvait modifier cette valeur, disons la diminuer, on écourterait la routine et on se ménagerait un peu de place vraiment libre.
Voyons donc où est stockée la valeur initiale de ecx : au troisième passage dans la routine (on s'intéresse à la zone pseudo libre entre 4060B0 et 4061F0) on a edi+04 = 00401D33. Tiens ! Comme c'est curieux ! Vous vous rappelez de l'adresse de la valeur placée dans ebx juste avant le jmp ebx ? Mais oui c'est tout proche (00401D5B)... C'est comme si PE-Encrypt disposait d'un espace où sont stockées toutes les données dont il a besoin...
Mais vu comme PE-Encrypt se présente, il y a fort à parier que ces données sont elles aussi cryptées. En effet, si on pose un bpr 00401D33 00401D5B et qu'on relance le programme, SI breake à nouveau. Voilà la seconde routine de décryptage, celle dédiée aux "données" de PE-Crypt :
:00401DA8 8DB547264000 lea esi, dword ptr [ebp+00402647] <- début du code à... décoder ! |
Non ! c'est pas possible ! Mais si, regardez bien...:-) Un simple XOR ! La valeur à décrypter, en al, ne subit qu'un XOR par l'octet en ah. Ensuite, ah est incrémenté puis subit une rotation et une addition, mais çà n'a aucune importance puisqu'on connaît la valeur initiale de ah. Qui dit XOR dit INVERSIBILITE donc on est desormais en mesure de décrypter/recrypter ces données à volonté. Le plus drôle c'est que c'est EXACTEMENT la même routine qui permet de coder ET de décoder. Essayer de remplacer le jmp 0040C99 par un jmp 0041DA8 et le programme se lance dans une boucle folle dans laquelle il crypte et décrypte les données à l'infini !
On approche de la fin... En effet, maintenant que l'on sait comment crypter/décrypter les données, on va pouvoir les décrypter à partir du disque dur, les modifier, les ré-encrypter puis les réecrire sur le disque dur. PE-Crypt n'y verra que du feu, il décryptera les données modifiées par nos soin et les utilisera comme si de rien n'était. Et le plus beau, c'est qu'on va faire d'une pierre deux coups : créer une zone franche pour placer notre patch et modifier le jmp EP.
Ceci est la fameuse zone de donnée, une fois décryptée :
|
En 00401D33 on retrouve "0002" (en réalité, c'est un 0200, les octets sont inversés), le compteur qui sert lors du décryptage de la zone allant de 406000 à 406200. Et en 401D5B on a "1910" c'est à dire Virtual Offset de notre EP. On va donc remplacer "0002" par "F000" pour écourter le décryptage et libérer la zone commençant en 406100. Pour la modification du saut vers EP on va donc remplacer le "1910" par "0061".
Voici le code d'un petit programme qui va faire une grosse partie du travail à notre place :
; ////////////////////////////////////////////////////////////////////////
.386
.model flat, stdcall
option casemap :none ; case sensitive
;////////////////////////////////////////////////////////////////////////
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
;////////////////////////////////////////////////////////////////////////
.data
longueur equ 0CAh
txt_erreur db 'ERREUR FATALE !! ;-(',0
txt_OK db 'C EST DANS LA POCHE ! :-))',0
entete db 'SALUT',0
crackme_name db 'chris2.exe',0
;////////////////////////////////////////////////////////////////////////
.data?
Buffer db 200h dup (?)
fhandle dd ?
bRead dd ?
bWritten dd ?
;////////////////////////////////////////////////////////////////////////
.code
debut: ;<- on commence par lire le code crypté à modifier
invoke CreateFile, offset crackme_name, GENERIC_READ, \
FILE_SHARE_READ, 0, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0
mov fhandle, eax
invoke SetFilePointer, fhandle, 1099h, 0, FILE_BEGIN
invoke ReadFile, fhandle, addr Buffer, longueur, addr bRead, 0
xor ebx, ebx
superboucle: ;\
mov ecx, 0CAh ;\
mov edi, offset Buffer ;> initialisation
mov esi, edi ;/
mov ah, 0F8h ;/
boucle:
lodsb ;\
xor al, ah ;\
inc ah ;\
rol ah, 2 ;\ cryptage/décryptage
add ah, 90h ;/
stosb ;/
loop boucle ;/
inc ebx ;/
cmp ebx, 2 ;<- on vient de décrypter (ebx=1) ou de recrypter (ebx=2)?
je ecrit_resultat
modifie: ;<- si on vient de décrypter, on patche le code décrypté...
mov edi, offset Buffer
mov word ptr [edi+0c2h], 6100h ;<- modif du saut : "vers ep" remplacé par "vers patch"
mov word ptr [edi+9ah] , 00F0h ;<- protection du patch
jmp superboucle ;<- et on le recrypte !
ecrit_resultat: ;<- le resultat est recopier dans l'exe
invoke CloseHandle, fhandle
invoke CreateFile, offset crackme_name, GENERIC_WRITE, \
FILE_SHARE_WRITE, 0, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0
mov fhandle, eax
invoke SetFilePointer, fhandle, 1099h, 0, FILE_BEGIN
invoke WriteFile, fhandle, addr Buffer, longueur, addr bWritten,0
cmp eax, FALSE
jz erreur
message_succes:
invoke MessageBoxA, 0, addr txt_OK, addr entete, 10h
jmp fin
erreur:
invoke MessageBoxA, 0, addr txt_erreur, addr entete, 10h
fin:
invoke CloseHandle, fhandle
invoke ExitProcess, 0
end debut
;/////////////////////////////////////////////////////////////////////////
|
Et voilà ! il ne reste plus qu'à écrire, à la main, avec un éditeur héxadécimal notre patch en 406100. Le crackme se lance et se ferme desormais sans aucune de ces horripilantes MessageBox, et on a accès à la fenêtre "About".
| IV°) ADDENTUM : LOCALISATION DES PROTECTIONS, ACTE II |
Comme me l'a très justement fait remarqué Christal, arrivé à ce point, on a réussi à se débarrasser de toutes les MessageBox, mais lorsqu'on clique sur "Generate", rien ne s'affiche dans la boite de dialogue... Autrement dit, on a un générateur de clés qui ne génére rien du tout et c'est plutôt ennuyeux !-(
Cette fois on va partir de l'hypothèse suivante : il doit exister quelque part un test qui décide s'il faut afficher la clé dans la boite de dialogue ou pas. Pour repérer ce test on va lancer une recherche sur "SetWindowText" car c'est en général cette API qui est utilisée pour écrire dans une boite de dialogue. En dessous voici la spécification de cette API :
BOOL SetWindowText(
HWND hwnd, // handle of window or control
LPCTSTR lpsz // address of string
);
|
Pour faire le tri entre les appels qui nous intéressent et les autres il faudra donc regarder le contenu de l'avant dernier argument poussé dans la pile. On repère en tout quatre appels à SetWindowText. On peut immédiatement éliminer les trois premiers car ils ont respectivement pour argument les chaines "Entrez au moins quatre caractères" (deux fois) et "Pas de caractères numériques !". Par contre le quatrième et dernier semble beaucoup plus intéressant :
loc_4017CF: ; CODE XREF: .text:004017BBj push eax push 403168h push 3 push 405266h push 0 push 403180h push 403182h call j_GetPrivateProfileStringA cmp eax, 0 jnz short loc_401831 jmp short loc_401824 loc_401824: ; CODE XREF: .text:0040181Fj pop eax push eax push ds:dword_4038E4 call j_SetWindowTextA loc_401831: ; CODE XREF: .text:004014AAj |
En effet on voit que selon le résultat de GetPrivateProfileStringA le programme évite ou exécute l'appel à SetWindowText : c'est bien la routine que l'on recherchait. Un petit coup d'oeil rapide dans le Win32.hlp nous apprend que GetPrivateProfileStringA sert à retirer des informations dans un .ini, qu'elle requiert 6 arguments et que son dernier argument est un pointeur vers le nom du fichier à consulter. Chez nous ce sixième argument est 403168 et que trouve-t-on à cette adresse ? Suspence.... on trouve "w32dasm8". C'est donc un anti-WinDasm cette fois ! Décidément il y en a pour tous les goûts dans ce crackme !
On va patcher çà simplement en nopant le jnz . Il faut donc rajouter la ligne suivante à notre patch :
mov word ptr [0040181D], 9090
Une fois le nouveau patch sauvegardé, on relance le programme, certain d'en avoir fini une bonne fois pour toute. Mais non ! Ce satané crackme est vraiment coriace !!! Cette fois, lorsqu'on clique sur "Generate" il apparait dans la boite de dialogue "Merci de désactiver votre debugger" !
Force est de constater qu'il doit y avoir encore d'autres anti-Softice planqués dans les recoins, on va donc se remettre en chasse. Si on regarde de plus près l'appel à SetWindowText, on remarque que le texte affiché est pointé par eax. Avant d'attérir dans eax, ce pointeur a été récupéré dans la pile (pop eax),au tout début de l'extrait on le voit même poussé dans la pile. Donc, en 4017CF eax contient soit le pointeur sur serial, soit le pointeur sur "Merci de désactiver..."
On va donc lancé une recherche sur "4017CF" pour repérer à quel endroit le choix du pointeur à mettre dans eax est fait et par la même occasion l'anti-Softice qui l'accompagne.
On obtient 4 nouveaux résultats qui ressemblent à :
loc_4014A6: |
Et ces quatres tests nous conduisent à quatre nouvelles routines anti-SI ! Elles sont situées en 4019F8, 401A18, 401B52 et 401B7B.
Les routines en 4019F8, 401A18 et 401B52 sont construites sur le même principe que celles vues précédemment : on a une séquence cmp+jnz+xor que l'on va patcher en cmp+xor+inc(+nop pour boucher les trous).
La routine en 401B7B est légèrement différente :
sub_401B7B proc near ; CODE XREF: .text:00401746p sidt qword ptr ds:406027h mov edi, ds:406029h mov bx, [edi] mov ax, [edi+8] sub ax, bx sub ax, 10h retn sub_401B7B endp |
Le résultat "Softice détecté" est ici équivalent à eax non nul, donc on peut choisir de patcher en remplaçant le dernier "sub" par un "xor eax, eax".
Finalement le patch est de la forme :
mov dword ptr [401B17], 9040C033 <- remplace les "jz+xor" par des "xor+inc+nop" mov dword ptr [401B3B], 9040C033 mov dword ptr [40199C], 8931 <- remplace les 4034C7 par des 403189 mov dword ptr [401952], 8931 mov dword ptr [4019E1], 8931 mov word ptr [40181D], 9090 <- anti-Windasm mov dword ptr [401AD7], 9040C033 mov dword ptr [401A07], 9040C033 mov dword ptr [401B75], 9040C033 mov dword ptr [401B92], 9090C033 <- remplace le sub par un xor jmp 00401019 <- saut vers l'EP |
Et voilà ! Cette fois le générateur de clés GENERE !! On l'aura mérité ce générateur de clé ! Il y avait là dedans pas moins de 10 protections différentes (9 anti-SI et 1 anti-WinDasm), et encore, on ne s'est pas intéressé à l'anti-Frogice... UNE SACREE DAUBE !
Pour finir, je tiens à remercier tous ces chefs qui prennent la peine de nous mijoter de sympathiques crackme, car ce sont de formidables moyens d'apprendre. Et plus généralement un grand merci à tous ceux qui partagent leurs connaissances.
KeYsER, le 7 Octobre 2000.
(pour toute réclamation, s'adrsesser au service après vente à k.e.y.s.e.r@caramail.com :-)