Name: Lucifer48 Code: 3550318047 Name: Lucifer48 Code: 3629595978 Programme : CrAcK me #2 By THE_q/PC PlateForme : Windows 95 Date : 14h, 3 juillet 1998 (France-Italie à 16h30) Protection : Registration Fichier : Crackme2.exe Outils : Soft-ice v2.0 Ou ça? : http://cracking.home.ml.org (un très bon site) Temps passé: Quelques heures Cours : 09 Matos : Bloc-notes en 800*600 Aujourd'hui, on s'attaque à un Crackme. Le but c'est de trouver, le bon code correspondant à mon nom. Je lance le programme, je mets les infos suivantes: Name: Lucifer48 Code: 36153615 ===================== 1. APPROCHE THEORIQUE ===================== Sous soft-ice je mets un breakpoint hmemcpy. Je click sur OK F5, F11 et je trace. J'arrive sans difficuté ici: XXXX:0040130A 3C00 CMP AL,00 XXXX:0040130C 743F JZ 0040134D XXXX:0040130E 90 NOP XXXX:0040130F 90 NOP XXXX:00401310 90 NOP XXXX:00401311 90 NOP XXXX:00401312 E85E000000 CALL 00401375 XXXX:00401317 E8A5000000 CALL 004013C1 XXXX:0040131C 85C0 TEST EAX,EAX XXXX:0040131E 742D JZ 0040134D <------ Test important XXXX:00401320 90 NOP Le call 00401375 sert à encoder le nom entré, il prend les 8 premiers caractères, additionne le premier avec le dernier... effectue un xor avec le nom PhroZenQ... Allez y faire un tour si ça vous intéresse. C'est de l'assembleur à comprendre (ni + ni -). Que fait en gros le crackme: - Encode (sorte de checksum) notre Nom (sur 10 octets) et notre Code (sur 4 octets =32 bits) - Le CALL 004013C1 effectue une serie de manipulation sur notre nom et ressort true ou false (EAX=1 ou EAX=0) Explorons plutôt ce CALL 004013C1, voiçi la routine entière: XXXX:004013C1 MOV EAX,[004021D2] ;Pointe sur notre code (encodé) et le met dans EAX XXXX:004013C6 MOV CL,[004021A9] ;Pointe sur le 1er octet du nom encodé, le met dans CL XXXX:004013CC ROL EAX,CL ;Rotation du registre EAX de CL bits vers la gauche XXXX:004013CE MOV ECX,[004021AE] ;Charge EAX avec les 4 derniers octets de mon nom encodé XXXX:004013D4 XOR EAX,ECX ;ou exclusif... XXXX:004013D6 MOV EBX,[004021AA] ;Chage EBX avec les 2,3,4,5 èmes octet de mon nom encodé XXXX:004013DC MOV CL,AL ;pas besoin d'explications XXXX:004013DE ROR EBX,CL ;Rotation du registre EBX de CL bits vers la droite XXXX:004013E0 XOR EAX,EBX ;<-------- ou exclusif très important XXXX:004013E2 MOV EAX,00000000 ;c'est à dire XOR EAX,EAX XXXX:004013E7 JNZ 004013EF ;si on saute: mauvais code (dans ce cas EAX=0) XXXX:004013E9 NOP XXXX:004013EA NOP XXXX:004013EB NOP XXXX:004013EC NOP XXXX:004013ED INC AL ;Si EAX=1, alors bon code XXXX:004013EF RET ;fin du call Vous l'avez compris, notre but c'est que, à la fin des manipulations (ROL,XOR,ROR) on aboutisse à EAX=EBX pour que l'instruction XOR EAX,EBX mette à 0 EAX et par le même occasion le flag ZF=1 et donc pas de saut et donc c'est un bon code. ================= 2. PHASE PRATIQUE ================= Calcul du bon serial: Avec les données que j'ai entrée, voilà mes données encodés en mémoire: Code: 0F A9 27 02 Nom: D0 8F BA A0 DA 82 A6 9E 38 MOV EAX,[004021D2] donne comme résultat EAX= 0227A90F MOV CL,[004021A9] donne comme résultat CL= D0 MOV ECX,[004021AE] donne comme résultat ECX= 389EA682 MOV EBX,[004021AA] donne comme résultat EBX= DAA0BA8F Rappel: x_d est une valeur décimale et x_h est une valeur héxadécimale (voir cours 08) Soit X notre bon serial encodé (sur 32bits), que l'on recherche Soit Y les 8 premiers bits (= 1 octet) de X à l'issue de l'instruction XOR EAX,ECX Donc pour avoir le bon code il faut que: ( ROL X,D0_h ) XOR (389EA682) = ROR DAA0BA8F,Y Trouver une relation entre X et Y va être difficile car Y dépend directement du résultat de ( ROL X,D0_h ) XOR (389EA682) Culture générale: A XOR B <=> (A OR B) AND ( NOT (A AND B) ) <=> (A OR B) AND ( (NOT A) OR (NOT B) ) <=> A (+) B Il faut donc prendre une valeur pour Y. Résumons nous autrement: -Soit Y le premier octet (le + à droite) de ( ROL X,D0_h ) XOR (389EA682) -Soit Z le premier octet (le + à droite) de ( ROL X,D0_h ) Il faut que Z XOR 82 = Le premier octet de ROR DAA0BA8F,Y = Y (à partir de XXXX:004013D6) Finalement Y = Le premier octet (le + à droite) de ROR DAA0BA8F,Y ------------------------------------------------------------ La voilà notre condition. * Supposons que Y=01 alors Z XOR 82 = 47 (premier octet de ROR DAA0BA8F,01) mais Z XOR 82 = 01 (condition) Ca ne va donc pas (47<>01) * On va pas essayer toutes les valeurs au hasard. C'est le moment de passer la main à l'amie fidèle du cracker: la calculatrice. J'improvise un petit programme (que voilà): << 32 STWS #0h #DAA0BA8F DO RR SWAP 1 + SWAP DUP #FFh AND UNTIL 3 PICK == END >> Concis mais efficace... je l'exécute et il me ressort: #1F et #B541751F (Remarque: #D5 et #5D47ED5 est aussi une solution possible) En effet: ROR DAA0BA8F,1F = B541751F -- -- On voit que notre condition est respectée. Finalement ROL X,D0_h XOR (389EA682) = B541751F <=> ROL X,10_h XOR (389EA682) = B541751F <=> (389EA682) XOR (B541751F) = ROL X,10_h Remarque: D0_h = 32 * 6_h + 10_h, soit une rotation de 16 bits (= XCHG AL,AH par ex) On trouve que ROL X,10_h = 8DDFD39D <=> ROR 8DDFD39D,10_h = X <=> X = D39D8DDF (enfin!, ceci est donc l'encodage en mémoire de mon nom) =================================== 3. COMMENT EST ENCODE NOTRE SERIAL? =================================== Notre serial encodé est mis dans EAX après l'exécution en XXXX:00401300 E821010000 CALL 00401426 Ce call est en fait CALL [User32!GetDlgItemInt] (merci wdasm, bon d'accord ma version de soft-ice est un peu trop préhistorique...,je n'avais pas modifié winice.dat) qui renvoit dans EAX la valeur d'un serial convertie en héxadécimale. Ici: EAX vaut 0227A90F, or 227A90F_h = 36153615_d (ça me dis qqch...) Deplus D39D8DDF_h = 3550318047_d On recommence donc la manip: Name: Lucifer48 Code: 3550318047 Ca marche! C00L!