Résolution LaFarges crackme #2
Voici une nouvelle résolution de keygenme.
Le keygenme est téléchargeable : ici
Voici ses caractéristiques:
Difficulty: 1 - Very easy, for newbies
Platform: Windows
Language: Assembler
D'après les votes sur le site : Crackme is quite nice.
Ce keygenme est facile à comprendre. Cependant, les nombreuses manipulations de chaîne (pseudo et une chaîne intégrée au programme) rendent casse-tête le codage du kengen, réalisé ici en C (je ne me suis pas encore mis au rip routine ).
Je le conseille au débutant en reverse et en programmation car il permet de s'exercer et de bien gérer la mémoire des variables (choix des types de variables adaptés aux utilisations faites).
Présentation du keygenme
Présentation classique, une musique pas très agréable vient nous chatouiller les oreilles.
Nous regardons les fonctions (ctrl+n), puis nous cliquons sur user32.MessageBoxA pour faire un "Set breakpoint on every reference"
Nous lançons le programme.
Le programme nous demande un pseudo et un mot de passe. Nous cliquons sur OK.
On breakpoint en 004012e3, si nous continuons, nous avons le message "Nope, that's the right code !".
Juste au dessus, en 004012cd, nous remarquons le placement d'un breakpoint, avec comme message "Yes, that's the right code !". Encore en dessus, nous avons des fonctions lstrcmpA. Nous montons plus haut, un breakpoint est placé en 0040115a avec le message "Username must have at least 4 chars".
Nous voyons le véritable sérial apparaître en 00401288
Nous venons de trouver le début et la fin de notre routine......
Nous voyons juste au dessus:
Ce saut (si supérieur) vérifie que le pseudo entré, fait bien plus de 3 caractère. Nous fesons "ENTRER" sur cette ligne et nous nous retrouvons en 00401163, nous y plaçons un breakpoint. Nous lançons le programme, on break, puis on avance avec la touche f8 jusqu'à avoir le message d'erreur.
Nous observons de nombreuses boucles
Boucle 1 : 0040117a à 00401197
Boucle 2 : 004011a3 à 004011c7
Boucle 3 : 004011d1 à 004011f0
Boucle 4 : 004011fa à 0040121e
Boucle 5 : 00401236 à 0040124b
Boucle 6 : 004012ea à 0040126a
Boucle 7 : 00401278 à 00401286
Nous allons donc analyser chacune de ces boucles et les coder.
Durant cette boucle, nous voyons une manipulation de la chaîne du pseudo placée en 00406349 ( clic droit dans la fenêtre de dump >>> go to >> 00406349) ainsi que d'une chaîne de caractère placée en 00406328 ( clic droit dans la fenêtre de dump >>> go to >> 00406328)
Voici le code C équivalent à cette boucle:
name est le pseudo
tab est la chaîne de caractère
count2 est le compteur lié à la chaîne qui est remis à 0 quand il est égale à 5
count est un compteur pour le pseudo
Voici l'équivalent en C:
L'équivalent en C
L'équivalent en C
La Boucle 6 est différente des autre boucle, elle permet de calculé le pseudo.
Plus haut, nous voyons que ecx est égale à 0x0a.
et eax est d'après le code suivant:
eax est égale au dword (4 octets) en 00406345
Donc avec le login "maurice", si nous faisons un breakpoint sur 00401253, et qu'ensuite, nous nous rendons en 00406345 (dans la fenêtre de dump), le dword est de:
88 10 07 23
La lecture se faisant à l'envers, il nous faut une variable égale à 0x23071088
Ensuite cette valeur (0x23071088) est divisée par 0x0a, le résultat va en eax, le reste de la division va en edx.
La boucle continue tant que eax n'est pas égale à 0.
Il est nécessaire de mettre la valeur initiale eax dans un unsigned long.
Voici l'équivalent en C:
Voici en bonus la fonction pour inversé 88 10 07 23 en 0x23071088
La dernière boucle est simple elle permet d'inverser la chaine (variable serial dans la boucle 6), le résultat est le serial final.
En pièce jointe, je met mon keygen
Le keygenme est téléchargeable : ici
Voici ses caractéristiques:
Difficulty: 1 - Very easy, for newbies
Platform: Windows
Language: Assembler
D'après les votes sur le site : Crackme is quite nice.
Ce keygenme est facile à comprendre. Cependant, les nombreuses manipulations de chaîne (pseudo et une chaîne intégrée au programme) rendent casse-tête le codage du kengen, réalisé ici en C (je ne me suis pas encore mis au rip routine ).
Je le conseille au débutant en reverse et en programmation car il permet de s'exercer et de bien gérer la mémoire des variables (choix des types de variables adaptés aux utilisations faites).
Présentation du keygenme
Présentation classique, une musique pas très agréable vient nous chatouiller les oreilles.
Correction
Nous regardons les fonctions (ctrl+n), puis nous cliquons sur user32.MessageBoxA pour faire un "Set breakpoint on every reference"
Nous lançons le programme.
Le programme nous demande un pseudo et un mot de passe. Nous cliquons sur OK.
On breakpoint en 004012e3, si nous continuons, nous avons le message "Nope, that's the right code !".
Juste au dessus, en 004012cd, nous remarquons le placement d'un breakpoint, avec comme message "Yes, that's the right code !". Encore en dessus, nous avons des fonctions lstrcmpA. Nous montons plus haut, un breakpoint est placé en 0040115a avec le message "Username must have at least 4 chars".
Nous voyons le véritable sérial apparaître en 00401288
Nous venons de trouver le début et la fin de notre routine......
Nous voyons juste au dessus:
Code :
00401149 JA SHORT crackme.00401163
Ce saut (si supérieur) vérifie que le pseudo entré, fait bien plus de 3 caractère. Nous fesons "ENTRER" sur cette ligne et nous nous retrouvons en 00401163, nous y plaçons un breakpoint. Nous lançons le programme, on break, puis on avance avec la touche f8 jusqu'à avoir le message d'erreur.
Nous observons de nombreuses boucles
Boucle 1 : 0040117a à 00401197
Boucle 2 : 004011a3 à 004011c7
Boucle 3 : 004011d1 à 004011f0
Boucle 4 : 004011fa à 0040121e
Boucle 5 : 00401236 à 0040124b
Boucle 6 : 004012ea à 0040126a
Boucle 7 : 00401278 à 00401286
Nous allons donc analyser chacune de ces boucles et les coder.
Boucle 1
Code :
0040117A > 8A0C16 MOV CL,BYTE PTR DS:[ESI+EDX] ; commencement boucle , un caractère du pseudo est pris à partir de 0040634a (2eme caractère du pseudo)
0040117D . 8AD9 MOV BL,CL ; on met la valeur de la lettre prise (qui est dans ecx) dans ebx
0040117F . 3298 28634000 XOR BL,BYTE PTR DS:[EAX+406328] ; on fait valeur du caractère du pseudo ^ caractère d'une chaine
00401185 . 40 INC EAX ; eax++
00401186 . 83F8 05 CMP EAX,5 ; on compare eax avec 5
00401189 . 881C32 MOV BYTE PTR DS:[EDX+ESI],BL ; on met la valeur calculée (du xor) dans le pseudo
0040118C . 8888 27634000 MOV BYTE PTR DS:[EAX+406327],CL ; on met la valeur du caractère pseudo (non calculée) dans la chaîne
00401192 . 75 02 JNZ SHORT crackme.00401196 ; si eax n'est pas égale à 5 on remet le compteur à 0 pour la chaîne
00401194 . 33C0 XOR EAX,EAX ; on remet eax à 0
00401196 > 46 INC ESI ; esi++
00401197 . 3BF5 CMP ESI,EBP ;
Durant cette boucle, nous voyons une manipulation de la chaîne du pseudo placée en 00406349 ( clic droit dans la fenêtre de dump >>> go to >> 00406349) ainsi que d'une chaîne de caractère placée en 00406328 ( clic droit dans la fenêtre de dump >>> go to >> 00406328)
Fenêtre de dump
Voici le code C équivalent à cette boucle:
Code :
for(count = 0 ; count != longueur_login ; count++)
{
valeur_int = name[count +1]; // MOV CL,BYTE PTR DS:[ESI+EDX]
name[count+1] = (name[count+1] ^ tab[count2]) ; //XOR BL,BYTE PTR DS:[EAX+406328]
tab[count2] = valeur_int; MOV BYTE PTR DS:[EAX+406327],CL
count2++; // INC EAX
count2 = count2 % 5 ; // CMP EAX,5
}
name est le pseudo
tab est la chaîne de caractère
count2 est le compteur lié à la chaîne qui est remis à 0 quand il est égale à 5
count est un compteur pour le pseudo
Boucle 2
Code :
004011A3 > 8A9F 2D634000 MOV BL,BYTE PTR DS:[EDI+40632D] ; commencement de la 2nd boucle / mise dans ebx du 6eme caractère de la chaîne
004011A9 . 8BF5 MOV ESI,EBP ; on met ebp dans esi
004011AB . 2BF1 SUB ESI,ECX ; esi = esi - ecx
004011AD . 4E DEC ESI ; esi = esi - 1
004011AE . 8A0432 MOV AL,BYTE PTR DS:[EDX+ESI] ; on met la lettre pseudo dans eax
004011B1 . 32D8 XOR BL,AL ; caractère chaine ^ caractère pseudo (dans ebx on met le resultat)
004011B3 . 47 INC EDI ; edi = edi + 1
004011B4 . 881C32 MOV BYTE PTR DS:[EDX+ESI],BL ; on prend la valeur de ebx et on le met dans caractère du pseudo
004011B7 . 8887 2C634000 MOV BYTE PTR DS:[EDI+40632C],AL ; on prend la valeur dans eax et on la met dans la chaine
004011BD . 83FF 05 CMP EDI,5 ; quand le compteur est à 5 on revient au debut de la chaine
004011C0 . 75 02 JNZ SHORT crackme.004011C4
004011C2 . 33FF XOR EDI,EDI
004011C4 > 41 INC ECX ; ecx = ecx + 1
004011C5 . 3BCD CMP ECX,EBP ; on compare ecx avec ebp ( ebp = lg du pseudo)
004011C7 .^72 DA JB SHORT crackme.004011A3 ; fin 2nd boucle
Voici l'équivalent en C:
Code :
for (count = 0 ; count != longueur_login -1 ; count++)
{
valeur = tab[5 + count2]; // MOV BL,BYTE PTR DS:[EDI+40632D]
eax = name[longueur_login - (count)- 1]; // MOV AL,BYTE PTR DS:[EDX+ESI]
valeur = valeur ^ eax ; // XOR BL,AL
name[longueur_login - (count+1)] = valeur ; // MOV BYTE PTR DS:[EDX+ESI],BL
tab[5+ count2] = eax ; //MOV BYTE PTR DS:[EDI+40632C],A
count2++; //INC EDI
count2 = count2 % 5 ; //CMP EDI,5
}
Boucle 3
Code :
004011D1 > 8A043A MOV AL,BYTE PTR DS:[EDX+EDI] ; commencement 3eme boucle ; on prend un caractère du pseudo (on commence par le 2eme) et on le met dans eax
004011D4 . 8A8E 32634000 MOV CL,BYTE PTR DS:[ESI+406332] ; on prend le 5eme caractère de chaine et on le met dans ecx
004011DA . 32C8 XOR CL,AL ; ecx = caractère chaine ^ caractère pseudo
004011DC . 46 INC ESI ; esi++
004011DD . 880C3A MOV BYTE PTR DS:[EDX+EDI],CL ; on met le resultat du calcul (ecx) dans le pseudo
004011E0 . 8886 31634000 MOV BYTE PTR DS:[ESI+406331],AL ; on met la valeur du caractere pseudo non calcule (eax) dans la chaine
004011E6 . 83FE 05 CMP ESI,5 ; esi est count2
004011E9 . 75 02 JNZ SHORT crackme.004011ED
004011EB . 33F6 XOR ESI,ESI
004011ED > 47 INC EDI ; edi est count
004011EE . 3BFD CMP EDI,EBP
004011F0 .^72 DF JB SHORT crackme.004011D1 ; fin 3eme boucle
Boucle 4
Code :
004011FA > 8A9F 37634000 MOV BL,BYTE PTR DS:[EDI+406337] ; commencement 4eme boucle on prend le 10eme caractère de la chaine et on le met dans ebx
00401200 . 8BF5 MOV ESI,EBP ; on met la longueur du pseudo dans esi
00401202 . 2BF1 SUB ESI,ECX ; esi = esi - ecx
00401204 . 4E DEC ESI
00401205 . 8A0432 MOV AL,BYTE PTR DS:[EDX+ESI] ; on prend le caractère pseudo en commencant par le dernier et on le met dans eax
00401208 . 32D8 XOR BL,AL ; caractère chaine ^ caractère pseudo
0040120A . 47 INC EDI ; edi++
0040120B . 881C32 MOV BYTE PTR DS:[EDX+ESI],BL ; met le resultat dans le pseudo
0040120E . 8887 36634000 MOV BYTE PTR DS:[EDI+406336],AL ; on met le pseudo (eax) dans la chaine
00401214 . 83FF 05 CMP EDI,5
00401217 . 75 02 JNZ SHORT crackme.0040121B
00401219 . 33FF XOR EDI,EDI
0040121B > 41 INC ECX
0040121C . 3BCD CMP ECX,EBP
0040121E .^72 DA JB SHORT crackme.004011FA ; fin 4eme boucle
L'équivalent en C
Code :
for (count = 0 ; count != longueur_login -1 ; count++)
{
valeur = tab[15 + count2]; // MOV BL,BYTE PTR DS:[EDI+406337]
eax = name[longueur_login - (count)- 1]; // MOV AL,BYTE PTR DS:[EDX+ESI]
valeur = valeur ^ eax ; //XOR BL,AL
name[longueur_login - (count+1)] = valeur ; // MOV BYTE PTR DS:[EDX+ESI],BL
tab[15+ count2] = eax ; //MOV BYTE PTR DS:[EDI+406336],AL
count2++;
count2 = count2 % 5; //CMP EDI,5
}
Boucle 5
Code :
00401236 > 8BC8 MOV ECX,EAX ; 5eme boucle
00401238 . 83E1 03 AND ECX,3 ; ecx & 3
0040123B . 8A1C0F MOV BL,BYTE PTR DS:[EDI+ECX] ; prend un caractère de la chaine et le met dans ebx
0040123E . 8D340F LEA ESI,DWORD PTR DS:[EDI+ECX]
00401241 . 8A0C02 MOV CL,BYTE PTR DS:[EDX+EAX] ; prend le caractere pseudo en commencant par le 2eme et on le met dans ecx
00401244 . 02D9 ADD BL,CL
00401246 . 40 INC EAX ; additionne pseudo + chaine
00401247 . 3BC5 CMP EAX,EBP
00401249 . 881E MOV BYTE PTR DS:[ESI],BL ; met le caractere pseudo dans la chaine
0040124B .^72 E9 JB SHORT crackme.00401236 ; fin 5eme boucle
L'équivalent en C
Code :
or (count=0 ; count !=longueur_login -1 ; count++)
{
count2 = count2 & 3 ; // AND ECX,3
valeur = tab[29 + count2]; //MOV BL,BYTE PTR DS:[EDI+ECX]
tab[29 + count2] = tab[29 + count2] + name[1+count]; // ADD BL,CL
count2++;
}
Boucle 6
La Boucle 6 est différente des autre boucle, elle permet de calculé le pseudo.
Code :
0040125A > 33D2 XOR EDX,EDX ; 6eme boucle
0040125C . F7F1 DIV ECX
0040125E . 80C2 30 ADD DL,30
00401261 . 8893 49654000 MOV BYTE PTR DS:[EBX+406549],DL
00401267 . 43 INC EBX
00401268 . 85C0 TEST EAX,EAX
0040126A .^75 EE JNZ SHORT crackme.0040125A ; fin 6eme boucle
Plus haut, nous voyons que ecx est égale à 0x0a.
et eax est d'après le code suivant:
Code :
00401253 . A1 45634000 MOV EAX,DWORD PTR DS:[406345]
eax est égale au dword (4 octets) en 00406345
Donc avec le login "maurice", si nous faisons un breakpoint sur 00401253, et qu'ensuite, nous nous rendons en 00406345 (dans la fenêtre de dump), le dword est de:
88 10 07 23
La lecture se faisant à l'envers, il nous faut une variable égale à 0x23071088
Ensuite cette valeur (0x23071088) est divisée par 0x0a, le résultat va en eax, le reste de la division va en edx.
La boucle continue tant que eax n'est pas égale à 0.
Il est nécessaire de mettre la valeur initiale eax dans un unsigned long.
Voici l'équivalent en C:
Code :
while(eax !=0)
{
reste = eax % 0x0a ;
eax = eax / 0x0a ;
reste = reste + 0x30 ;
serial[count2] = reste;
count2++;
}
Voici en bonus la fonction pour inversé 88 10 07 23 en 0x23071088
Code :
for(count = 0 ; count !=4 ; count++)
{
eax = eax + (tab[count + 29] << (8 * count2));
count2++;
printf("\neax : %x", eax);
}
La dernière boucle est simple elle permet d'inverser la chaine (variable serial dans la boucle 6), le résultat est le serial final.
En pièce jointe, je met mon keygen