Cours
de Cracking par pifoman |
Mon objectif : programmer un keygen pour Start Clean 1.2
-> Le programme à craquer
:
Start
Clean v1.2
-> Un désassembleur
:
W32dasm
8.93
-> Un compilateur C++
:
Dev C++
Dans ce cours nous allons apprendre à fabriquer un keygen pour Start Clean 1.2.Pour cela on va procéder par étapes.
-> Analyse de l'enchaînement des étapes
dans la vérification du serial par Start Clean
-> Localisation de la routine de calcul
du serial valide dans Start Clean
-> Etude détaillée de la routine de
calcul
-> Programmation d'un algorithme en C++
qui reproduit le calcul du serial
Avant de commencer la leçon petit rappel pour ceux qui ne connaissent pas la signification de ce terme : un keygen (abbréviation en anglais de key generator) est un générateur de clés valides pour déverrouiller un logiciel en version d'évaluation. Il permet moyennant la donnée d'un nom de calculer un numéro de série correspondant puis d'enregister un logiciel et de l'utiliser dans sa version complète. Un numéro de série en général est une suite de chiffres séparés par des - par exemple 1598-15026-2421-469 (c'est le numéro de série (on utilise aussi le terme serial) associé au nom pifoman dans Start Clean). Cette opération de keygenning n'est bien sûre possible que si bien entendu le logiciel le logiciel à keygenner possède une boîte d'enregistrement. Dans le cas de Start clean c'est le cas puisque si vous cliquez sur le bouton Register vous voyez apparaître un 2 ième écran où vous avez la possibilité de vous enregistrer.
Cette première étape permet de réfléchir à la manière dont est conçu le mécanisme de vérification. Je ne m'étendrais pas sur le sujet puisque le cours traite déja le sujet en détail. Ce que l'on peut dire en revanche pour résumer c'est que
-> L' utilisateur saisi le couple
name / code
dans la boîte d'enregistrement du dessus
-> Le programme récupère le nom et le code entrés grâce à
l'API User32.GetDlgItemTextA
-> Le programme calcule le vrai code à partir nom
entré
-> Le programme compare avec l'API
Kernel32.lstrcmpA le code calculé au
code entré
-> Si ce n'est pas la même -> le
programme n'est pas enregistré.
-> Si c'est le même le programme est
enregistré (version complète).
Pour localiser la routine de calcul du serial dans Start Clean il faut procéder par étapes :
-> On désassemble
Start Clean dans
W32dasm avec la commande
Open File To Disassemble ->
STARTCLN.EXE
-> On charge le programme
Start Clean en mémoire par la commande
CTRL L de
w32dasm
-> On démarre dans le débugger de
w32dasm le programme
Start Clean avec
F9 puis on saisi le couple name / code
suivant pifoman /
123456 dans la boîte d'enregistrement
sans faire OK.
-> On clique sur le bouton Imp
Fn (Imported Functions) dans w32dasm pour voir
les fonctions API
importées dans
Start Clean.
-> Dans cette liste on répère l'API
Kernel32.lstrcmpA (qui est utilisée
pour la comparaison des chaîne de caractères).
-> On
clique sur l'API
Kernel32.lstrcmpA qu'on a vient de repérer.
-> On arrive au total à
5 endroits dans le code (en continuant de cliquer sur l'API
Kernel32.lstrcmpA). Les voici.
:00401131
FF1520924000
Call
dword ptr [00409220] :004011E3 FF1520924000 Call dword ptr [00409220] :0040154B 8B2D20924000 mov ebp, dword ptr [00409220] :00401A32 8B2D20924000 mov ebp, dword ptr [00409220] :00401F1F FF1520924000 Call dword ptr [00409220] |
-> Ce que l'on va faire maintenant c'est mettre un bp (breakpoint; en français un point d'arrêt) sur chacune des 5 lignes trouvées comme ça dès que le programme va faire appel à l'API Kernel32.lstrcmpA le débugger de w32dasm va s'arrêter sur la ligne où l'on a mis le bp. Il suffit pour mettre un bp de sélectionner la ligne de code dans w32dasm puis de faire F2.
-> On clique sur le bouton OK de la boîte d'enregistrement de Start Clean et on tombe à cet endroit
:004011E3 FF1520924000 Call dword ptr [00409220]
Si on fait F9 dans w32dasm , Start Clean nous affiche ensuite le message d'erreur.
Conclusion : Start Clean ne fait appel qu'une seule fois à la fonction lstrcmpA de l'API Kernel32. C'est à cet endroit à l'adresse 004011E3 que Start Clean effectue une comparaison de chaîne (entre notre code 123456 et le code calculé).
Ecran 0
Pour rappel (cf milieu de ce cours) en 004011DD le registre eax contient notre serial 123456 et en 004011DE l'adresse 0040630 contient le vrai serial calculé par Start Clean 1598-15026-2421-469 (pour le nom pifoman).Le vrai serial est calculé juste avant dans le call 00401280 surligné en bleu dans la photo du dessus. On va donc entrer dans ce call (sélectionnez la ligne d'adresse 004011D1 puis flèche doite du clavier).
C'est le point le plus difficile du cours car il fait appel à l'interprétation de l'assembleur. L'assembleur est un langage assez hermétique pour celui qui n'y est pas initié. Je vais essayer quand même de vous expliquer en détail chaque ligne de code et de me placer à un niveau débutant en assembleur pour que vous puissiez comprendre ce qui est écrit dans le code du call 00401280 à l'adresse 004011D1.Il va vous falloir pour cela de la concentration et une attention continue jusqu'à la fin. Sachez en effet qu'un keygen est beaucoup plus difficile à faire qu'un crack car il ne fait pas qu'annuler la protection d'enregistrement mais il reproduit l'algorithme de calcul. Ok c'est parti.
a/ Avant de commencer petit rappel d'asm (asm = assembleur)
-> Passage d'arguments à une fonction en asm.
On utilise la zone de pile du programme (le stack).Par exemple si vous voulez passer les arguments suivants arg1,arg2,arg3 dans cet ordre à la fonction f (c'est à dire appeler f(arg1,arg2,arg3)) on écrira en assembleur
push adresse_arg3
push
adresse_arg2
push adresse_arg1
call
adresse_fonction_f
-> Affectation d'un valeur à un registre
:004012BB 8B9C241C010000
mov ebx, dword ptr [esp+0000011C] -> mettre
dans ebx le contenu de l'adresse esp+0000011C
:004012C5 8BC3
mov
eax, ebx -> mettre l'adresse du registre ebx dans le registre
eax
b/ Note importante
Dans les écrans suivants (écran 1 à écran 10) quand je dis qu'un registre ou une adresse contient telle valeur je m'appuie sur le résultat donné par le débugger w32dasm au cours de la session de debugging de Start Clean. Pour lancer une session de debugging dans w32dasm il faut suivre les étapes suivantes (en référence à l'écran 0 plus haut) :
-> Désassembler Start
Clean dans w32dasm par Open
File To disassemble -> STARTCLN.EXE
-> Dans w32dasm
CTRL L suivi de F9 puis
on entre le couple name/code pifoman / 123456
dans la boîte d'enregistrement de Start Clean (que
l'on vient de lancer avec w32dasm) sans faire OK.
-> On
sélectionne dans w32dasm la ligne de code 004011D1 E8AA000000
call 00401280 et on met un bp
dessus avec F2.
-> On
fait OK dans Start Clean
-> w32dasm
break sur 004011D1 (vous entendez un bing).
-> On
fait F7 pour descendre dans ce call
00401280
-> Ensuite avec des F8
on exécute le programme ligne de code par ligne de code et dès qu'une adresse
ou un registre nous intéresse (ie on veut examiner ce qu'il y a dedans) on fait
comme ça par exemple si on est sur la ligne de code d'adresse 004012C2 et
qu'on veut voir le contenu de l'adresse contenue dans le registre ebx
et calculée en 004012BB
:004012BB 8B9C241C010000
mov
ebx, dword ptr [esp+0000011C]
:004012C2
83C408 add
esp, 00000008
J'explique au cas où certains n'auraient pas compris. Quand le débugger arrive sur l'adresse 004012C2 on voit dans la zone regs (regs pour registers => registres en français) que le registre ebx vaut 00406130 (il est écrit en capitale => ça veut dire qu'il vient d'être mis à jour). Tout ce qui commence par 00 suivi d'un chiffre non nul comme 4 par exemple cela signifie que c'est une adresse dans le programme en cours de debugging (ici STARTCLN.EXE). Donc 00406130 c'est une adresse.Nous l'adresse 00406130 ça nous intéresse pas c'est ce qu'elle contient qui nous intéresse.Pour le savoir on clique sur la case sous User Addr 2 et on tape l'adresse dont on veut voir le contenu à savoir 00406130.On clique sur le bouton UA2 à côté à droite pour constater qu'en 00406130 commence une chaîne qui contient le nom entré dans la boîte d'enregistrement de Start CLean à savoir pifoman. En 00406130 on a la lettre p de pifoman dont le code hexadécimal est 70 (on lit les codes hexadécimaux en partant de la droite) ensuite à l'adresse 00406131 (00406130 + 00000001) on a la lettre i de pifoman dont le code hexadécimal est 69 et caetera pour le reste des lettres de la chaîne pifoman.
c/ Rôle de 2 API utilisées dans le programme
Dans le corps de la routine de calcul du serial de Start Clean vous aller voir qu'il y a des appels à l'API USER32.wsprintfA et à l'API KERNEL32.lstrcatA. La première convertit notre nombre calculé (ebp) en chaîne, et la deuxième permet de concaténer deux chaînes, c'est à dire les coller ensemble (la deuxième derrière la première).
-> wsprintfA(buffer, "%d-", ebp); // où buffer est un élément de type char va recevoir la valeur du registre ebp converti en entier suivi d'un -.Le d dans %d signifie decimal.
-> lstrcatA(serial, buffer); //où serial est un char * qui va recevoir la valeur du serial actuel suivi du contenu de la variable buffer c'est à dire en prenant pour symbole de concaténation de chaîne le signe + on a serial = serial + buffer.
d/ Analyse de la routine de calcul
- Tous les écrans suivants s'enchaînent les un aux autres de haut en bas.
-
Pour les calculs prenez la calculatrice de windows (démarrer->exécuter->calc)
-> affichage -> scientifique -> bouton hex coché.
Ecran 1
|
Quand on break avec w32dasm sur le bp posé en 004011D1 (là où il y a notre call 00401280) et qu'on F7 on arrive à l'adresse 00401280 (c'est la ligne surlignée en bleu). Dans cette partie la chose intéressante à remarquer c'est l'initialisation du registre ebp à la valeur hexadécimale 0000006A (instruction mov ebp, 0000006A). C'est cette variable ebp qui contiendra le serial qui sera progressivement calculé dans la routine de calcul du serial pour Start Clean. |
---------------------------------------------
Ecran 2
|
L'appel à la fonction wsprintfA en 004012B5 ici ne donne rien d'intéressant. Ce qui par contre est intéressant c'est le code asm à l'adresse 004012BB où l'on met dans ebx le contenu du registre esp augmenté de 0000011C (instruction mov ebx, dword ptr [esp+0000011C]). En effet ebx vaut [esp+0000011C] = 00406130 et à cette adresse (en 00406130) on a la chaîne pifoman qui va être utilisée pour générer le vrai serial. Ensuite on met en 004012C5 la valeur de ebx dans eax soit eax = 00406130.
|
En 004012CD on compare le byte (octet en français) à l'adresse 00406130 (car [ebx] = contenu de ebx = 00406130) avec le byte 00. Comme en 00406130 on a 70 (code hexadécimal pour le p dans la chaîne pifoman) qui est différent du byte 00 le saut en 004012D0 (qui saute si les 2 termes de la comparaison sont précédente sont égaux) ne va pas s'exécuter. On va donc exécuter l'API charNextA qui va balayer tous les lettre du nom pifoman NB : vous vous demander certainement pourquoi la comparaison se fait à 00. Eh bien c'est simple 00 c'est le caractère NUL qui termine toute chaîne de texte et qui va dire au programme qu'on a atteint la fin de chaîne. Pour ceux qui programme en C le 00 ça correspond au '\0' qu'on ajoute pour terminer une chaîne. |
---------------------------------------------
Ecran 3
|
Boucle de calcul de la première partie du serial. |
En 004012D2 on met le byte à l'adresse 00406130 (00406130 = contenu de eax = [eax]) dans ecx soit ecx = 00000070 (70 c'est le code hexadécimal pour la lettre p de pifoman). Ensuite on empile eax (qui vaut 00406130) avec l'instruction push eax En 004012D6 on effectue le calcul de la première partie du serial. On prend ebp que l'on avait initialisé à 6A à l'adresse 004012A2 et on lui ajoute 2 * ecx. On met ensuite le résulat du calcul dans ebp (lea a1,a2 met a2 dans a1).Le calcul se fait en hexadécimal sur 4 octets (c'est pour ça que c'est écrit dword : 1 byte = 1 octet, 1 word = 2 bytes; 1 dword (1 double word) = 4 bytes) soit ebp = 0000006A + 2 * 00000070 = 0000014A (prenez la calculatrice de windows avec démarrer->exécuter->calc en prenant l'affichage scientifique et en cochant le bouton hex et tapez 6A + 2* 70). En 004012DA on appelle l'API User32.charNextA en passant en paramètre à cette fonction la variable eax (c'est le push eax en 004012D5). Le registre edi vaut à ce moment 77D172EC. 77D172EC c'est l'adresse calculée en 004012C7de la fonction charNextA dans C:\windows\system32\user32.dll. En effet si vous désassemblez user32.dll dans w32dasm et que vous faites SHIFT F12 -> 77D172EC vous arrivez sur le code de la fonction charNextA (Exported fn(): CharNextA - Ord:002Bh...). Vous comprenez maintenant un peu mieux comment fonctionne la communication entre le programme et le système ... Arrivé en 004012DC notre API User32.charNextA a mis à jour eax qui est passé de 00406130 à 00406131.C'est normal car User32.charNextA a pour fonction de passer au caractère suivant dans une chaîne (ici c'est la chaîne pifoman dont il s'agit). 00406131 contient le i de pifoman. En 004012DC que se passe-t-il ? Le programme va comparer le byte contenu à l'adresse de eax (l'adresse de eax vaut 00406131) à 00 et comme ce byte vaut 69 (c'est le code hexadécimal de la lettre i de pifoman) le jne (jump if not equal to zero) de la ligne de code suivante va provoquer un saut vers 004012D2. En conclusion entre les adresses 004012D2 et 004012DF nous avons une boucle qui lit tous les caractères du nom tapé dans la boîte d'enregistrement de Start Clean et qui effectue un calcul sur chacune des lettres du nom. Le résultat du calcul est mis dans le registre ebp comme ceci : Algorithme ebp
= ebp + (BYTE)name[i] *2
// pour i variant de 0 à longueur du name -1. Paramètres Ici les
variables de l'algorithme ont les valeurs suivantes : Soit ebp =
0000006A + 00000070
* 2 = 0000014A
// 70
code hexadécimal de la lettre p dans
pifoman (1 ière boucle) Sortie de boucle : ebp = 0000063E |
---------------------------------------------
Ecran 4
|
Dans cette partie on va invoquer l'API user32.wsprintfA qui va prendre dans cet ordre les paramètres eax , 00406274, ebp et convertir en nombre décimal ebp en lui ajoutant un - et en mettant le résultat obtenu dans eax. L'adresse 00406274 contient la chaîne %d-. En 004012EC on a simplement écrit wsprintfA(eax,"%d-",ebp) qui traduit ce qu'on vient de dire soit eax = 1598-. En 004012F2 on réinitialise eax qui passe de 00406137 à 00406130. En 004012FB on concatène esi qui vaut 00000000 à eax et on met le résulat dans esi. On a simplement écrit lstrcatA(esi,eax). En 00401301 on réinitialise eax à 00406130. En 00401303 comme le byte contenu à l'adresse de ebx vaut 70 (code hexadécimal de la lettre p de pifoman) et que c'est différent de 00 le je (jump if equal to zero) ne s'exécute pas en 00401306. |
---------------------------------------------
Ecran 5
|
Boucle de calcul de la deuxième partie du serial. |
Algorithme ebp = ebp
+ (BYTE)name[i] *18 //
pour i variant de 0 à longueur du name -1. Sortie de boucle : ebp = 00003AB2 |
---------------------------------------------
Ecran 6
|
En 00401325 on appelle wsprintfA(eax,"%d-",ebp) qui converti ebp = 00003AB2 (calculé dans la boucle de calcul précédente) en décimal et le met dans eax. Soit eax = 15026- En 00401334 on appelle lstrcatA(esi,eax) qui ajoute eax à esi = 1598- soit esi = 1598-15026- En 0040133C comme byte ptr [ebx] = 70 != 00 le je (jump if equal to zero) en 0040133F ne provoque pas de saut vers 00401359. |
---------------------------------------------
Ecran 7
|
Boucle de calcul de la troisième partie du serial. |
Algorithme ebp = (BYTE)name[L-1]
*22 + 1 //
avec L = longueur du name -1 Sortie de boucle : ebp = 00000975 |
---------------------------------------------
Ecran 8
|
En 00401364 on appelle wsprintfA(eax,"%d-",ebp) qui converti ebp = 00000975 (calculé dans la boucle de calcul précédente) en décimal et le met dans eax. Soit eax = 2421- En 00401373 on appelle lstrcatA(esi,eax) qui ajoute eax à esi = 1598-15026- soit esi = 1598-15026-2421- En 0040137B comme byte ptr [ebx] = 70 != 00 le je (jump if equal to zero) en 0040137E ne provoque pas de saut vers 00401392. |
---------------------------------------------
Ecran 9
|
Boucle de calcul de la quatrième partie du serial. |
Algorithme ebp = (BYTE)name[L-1]
* 4 + 1D //
avec L = longueur du name -1 Sortie de boucle : ebp = 000001D5 |
---------------------------------------------
Ecran 10
|
En 0040139D on appelle wsprintfA(eax,"%d-",ebp) qui converti ebp = 000001D5 (calculé dans la boucle de calcul précédente) en décimal et le met dans eax. Soit eax = 469 En 004013AC on appelle lstrcatA(esi,eax) qui ajoute eax à esi = 1598-15026-2421- soit esi = 1598-15026-2421-469 En 004013BC on quitte la boucle de calcul pour retourner à l'adresse qui suit le call 00401280 soit l'adresse 004011D6. |
Voila j'espère que vous avez tenu jusque là ! On est enfin arrivé à la fin de la routine de calcul ! C'était long (pour moi aussi) mais nous avons calculé notre serial pour le name pifoman qui est de la forme X-Y-Z-T où X = 1598, Y = 15026, Z = 2421, T = 469. On peut donc maintenant élaborer un algorithme pour calculer le bon serial en C++.
a/ Description de la marche à suivre
Voici les étapes à suivre pour générer le bon serial connaissant le nom
-> 1/ On demande un nom que l'on
met dans la variable name.
-> 2/
On initialise une variable ebp à 6A.
-> 3/
1 ière boucle : ebp = ebp
+ (BYTE)name[i] *2 // i variant de 0 à la longueur
de name -1.
-> 4/ On ajoute un -
derrière ebp
-> 5/
2 ième boucle : ebp = ebp
+ (BYTE)name[i] *18 // i variant de 0 à la longueur
de name -1.
-> 6/
On ajoute un - derrière ebp et on ajoute la chaîne
calulée en 5/ à celle calculée en 4/
-> 7/
3 ième boucle : ebp = (BYTE)name[L-1]
*22 + 1 // L = longueur de name -1.
->
8/ On ajoute un - derrière ebp et on ajoute
la chaîne calulée en 7/ à celle calculée en 6/
-> 9/
3 ième boucle : ebp = (BYTE)name[L-1]
*4 + 1D // L =
longueur de name -1.
->
10/ On ajoute un - derrière ebp et on ajoute
la chaîne calulée en 9/ à celle calculée en 8/
Cet algorithme fonctionne aussi quand le name est une chaîne vide auquel cas le serial est 106-106-106-106 (les 106 viennent de la conversion de 6A en décimal avec la fonction wsprintfA(eax, "%d-", ebp)). On obtient cette chaîne car les étapes 3/ 5/ 7/ 9/ sont évitées à cause de l'instruction cmp byte ptr [eax], 00 aux adresses 004012CD, 00401303, 0040133C, 0040137B car à ce moment là byte ptr [eax] = 00 et le saut conditionnel qui suit (je = jump if equal to zero dans l'intruction précédente) s'exécute.
b/ Interface graphique du keygen
c/ Code C++ du keygen
Le code suivant entre Début du code et Fin du code est à copier dans dev C++.Pour le compiler il faut procéder ainsi
-> Installez dev C++ sur votre machine
-> Lancez
devcpp.exe
-> File
-> New Project -> Application
console -> OK -> Project
1 -> Enregistrer
-> Insérez
le code entre Début du code et Fin du code dans la page devant vous en effaçant
le code que dev c++ a mis.
-> CTRL
F10 (execute -> compile
and run)
Remarque :
-> Les commentaires sont en vert et précédé de //
-> L'espace de travail (code source ,exécutable) est ici
--------------------------------------------------------------------------------- Début du code ---------------------------------------------------------------------------
#include <windows.h> //Accès aux API #include <stdio.h> //Minimum vital #include <stdlib.h> //Pour avoir system //Variables globales //Serial est un tableau de 100 cases vides de type chaîne char serial[100]=""; void afficher_banniere() { printf("\n\n\n"); printf(" ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²\n"); printf(" ² ²\n"); printf(" ² Keygen pour Start Clean 1.2 ²\n"); printf(" ² ²\n"); printf(" ² Par pifoman le 10/01/2005 ²\n"); printf(" ² ²\n"); printf(" ²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²²\n\n\n\n"); } char * get_serial(char * name) { //Déclaration et initialisation des variables int EBP=0x6A; char *buffer; serial[0]='\0'; //On calcule la longueur de la chaîne name avec la fonction strlen //En C un tableau est une chaine et vice-versa long longueur=strlen(name); //1 ière partie du serial à calculer (cf 1ière boucle)
for(int i=0;i<longueur;i++) EBP+=(BYTE)name[i]*2; //On ajoute un - derrière le morceau de serial calculé EBP et on le met dans serial //%d converti EBP en nombre décimal wsprintfA(buffer, "%d-", EBP); lstrcatA(serial, buffer); //2 ième partie du serial à calculer (cf 2ième boucle) //On converti chaque caractère du name en entier,on les multiplie par 18,on les somme //entre eux puis on ajoute la valeur de EBP calulée dans le 1ère boucle for(int i=0;i<longueur;i++) EBP+=(BYTE)name[i]*18; //On ajoute un - derrière le morceau de serial EBP calculé dans la 2ième boucle //et on le colle derrière la variable serial calculée dans la 1ère boucle //%s converti serial en string et %d converti EBP en nombre décimal wsprintfA(buffer, "%d-", EBP); lstrcatA(serial, buffer); //3 ième partie du serial à calculer (cf 3ième boucle) //On prend la dernière lettre de la variable name. //On la converti en byte et on la multiplie par 22 puis on lui ajoute 1 //On ne fait pas appel au serial calulé dans les boucles précédentes //Si name est vide sa longueur est donc 0.Il faut donc vérifier que la longueur est > à 1 if(longueur>0) EBP=(BYTE)name[longueur-1]*22+1; wsprintfA(buffer, "%d-", EBP); lstrcatA(serial, buffer); //4 ième partie du serial à calculer (cf 4ième boucle) //On prend la dernière lettre de la variable name. //On la converti en byte ,on la multiplie par 4 puis on lui ajoute 1D //On ne fait pas appel au serial calulé dans les boucles précédentes //Si name est vide sa longueur est donc 0.Il faut donc vérifier que la longueur est > à 1 if(longueur>0) EBP = (BYTE)name[longueur-1] * 4 + 0x1D; wsprintfA(buffer, "%d", EBP); lstrcatA(serial, buffer); return serial; } //Point d'entrée du programme int main(int argc, char *argv[]) { long a; char *name; char continuer='o'; char C; while(continuer=='o') { int j=0; //On vide l'écran system("cls"); afficher_banniere(); printf(" Votre nom : "); //On met chaque lettre du nom dans le tableau name //jusqu'à ce qu'on tape sur la touche entrée symbolisée par '\n' while((C=getchar())!= '\n') name[j++]=C; //On termine la chaîne saisie par le caratère de fin de chaîne '\0' //Si on ne l'ajoute pas la lecture de la chaîne saisie continuera au dela de la //longueur de la chaine et la fonction get_serial rendra un serial erroné. name[j++]='\0'; //On affiche "Votre serial :" suivi du serial calculé avec la fonction get_serial //%s converti en string le résultat de get_serial(name) printf("\n Votre serial : "); printf("%s\n\n",get_serial(name)); //On demande à l'utilisateur s'il veut continuer o pour oui n pour non printf("\n Recommencer [o/n] ?"); continuer=getchar(); //On vide le tampon d'entrée du clavier rewind(stdin); } return 0; }
--------------------------------------------------------------------------------- Fin du code ---------------------------------------------------------------------------
Voila vous avez entre les main un keygen fonctionnel.Il ne fonctionne cependant après des tests poussés pas avec les caractères dont le code ascii est supérieur à 127 (ascii étendu) par exemple les caractères £µù§àoôöûüéèêëîï. Cela vient du compilateur C++ qui ne reconnais pas les caractères accentués (ISO-LATIN-1) qui ne font pas partie de l'aphabet anglo-saxon. Le compilateur est en effet incapable de convertir correctement les caractères spéciaux en codes hexadécimaux corrects (vous aurez un serial mais il ne sera pas valide).
Par exemple vous pouvez tester ces combinaisons name /code correctes (les
caractères du name ont un code ascii <127)
-> &"#[('15
%; 1120-10246-1299-265
-> &*/-+$<>?./!
1230-11346-727-161
Pour vous éviter de chercher sur le net une table ascii compatible avec le keygen je vous encode la table ascii étendue (256 caractères -> elle inclue les caractères spéciaux) en javascript.Sur chacune de ses lignes vous avez le code décimal qui correspond à chaque caractère.
Ce tutorial est assez long à expliquer car il fait appel à beaucoup de mécanismes dans le langage d'assemblage puis le langage C qui ne sont pas forcément simples à comprendre du premier coup par exemple la conversion d'un caractère en code hexadécimal. Néanmoins la procédure de génération du serial reste assez simple à comprendre et à retranscrire en C++ pour celui qui a quelques notions suffisantes en assembleur et qui a la volonté d'apprendre.
Nous avons vu aussi dans la leçon comment trouver la procédure de calcul d'un serial puis nous avons appris à interpréter le code dans cette procédure en constatant le rôle joué par les API wsprintfA, lstrcatA, CharNextA dans la construction pas à pas du serial