Compétences requises : posséder quelques bases en cracking et en ASM.
La lecture du premier opus (FlashGet 1.40 : cracking) est par ailleurs vivement recommandée.
Matériel requis : FlashGet 1.40, un debugger et un désassembleur (optionnel).
Nous allons ici étudier de plus près les rouages internes de FlashGet afin d'obtenir un serial valide.
Ceci suppose que vous sachiez comment localiser la procédure de vérification des données d'enregistrement, de cette manière nous pourrons rentrer directement dans le vif du sujet. Si ce n'est pas le cas, lisez le premier tutoriel.
On commence par démarrer FlashGet avec notre debugger, puis on pose un breakpoint au début de la routine de validation, c'est à dire à l'adresse 41C550.
En traçant un peu, on arrive à ce niveau :
0041C5F2 MOV EAX,DWORD PTR DS:[EDX-8] ; eax = la longueur de l'email
0041C5F5 TEST EAX,EAX ; teste si celle-ci est nulle
0041C5F7 JE flashget.0041C739 ; si oui, saute vers bad boy
0041C5FD MOV EAX,DWORD PTR DS:[EBX]
0041C5FF MOV ECX,DWORD PTR DS:[EAX-8] ; ecx = la longueur du serial
0041C602 TEST ECX,ECX ; teste si celle-ci est nulle
0041C604 JE flashget.0041C739 ; si oui, saute vers bad boy
0041C60A MOV ECX,ESI
0041C60C CALL flashget.0049A227
0041C611 MOV ECX,ESI
0041C613 CALL flashget.0049A1DB
0041C618 MOV ECX,DWORD PTR DS:[ESI]
0041C61A CMP DWORD PTR DS:[ECX-8],5 ; compare la longueur de l'email à 5
0041C61E JLE flashget.0041C739 ; si inférieure ou égale, saute vers bad boy
0041C624 PUSH flashget.004F1488
0041C629 MOV ECX,ESI
0041C62B CALL flashget.00499E42 ; teste la presence du '@' dans l'email
0041C630 TEST EAX,EAX
0041C632 JL flashget.0041C739 ; si ce n'est pas le cas, saute vers bad boy
0041C638 PUSH flashget.004F1484
0041C63D MOV ECX,ESI
0041C63F CALL flashget.00499E42 ; teste la presence d'un '.' dans l'email
0041C644 TEST EAX,EAX
0041C646 JL flashget.0041C739 ; si ce n'est pas le cas, saute vers bad boy
Ce sont des tests basiques sur l'email et le serial, certes de second ordre, mais dont il faut tenir compte. En effet, si le serial est valide mais l'email ne l'est pas, on passe quand même en mode non enregistré.
C'est un peu plus loin que l'on va vraiment passer aux choses sérieuses...
Quelques lignes en dessous du code précédemment étudié, on trouve cela :
0041C65C MOV EAX,DWORD PTR DS:[EDX-8] ; eax = la longueur du serial
0041C65F CMP EAX,2C ; compare cette longueur a 2Ch (44)
0041C662 JNZ flashget.0041C739 ; si différente, saute vers bad boy
0041C668 PUSH flashget.004F147C
0041C66D MOV ECX,EBX
0041C66F CALL flashget.00499E42 ; teste si le serial commence par "fgc-"
0041C674 TEST EAX,EAX
0041C676 JNZ SHORT flashget.0041C67E ; si ce n'est pas le cas, saute vers la deuxième vérification
0041C678 MOV DWORD PTR SS:[ESP+10],EDI
0041C67C JMP SHORT flashget.0041C696
0041C67E PUSH flashget.004F1474 ; teste si le serial commence par "fgf-"
0041C683 MOV ECX,EBX
0041C685 CALL flashget.00499E42
0041C68A TEST EAX,EAX
0041C68C JNZ flashget.0041C739 ; si ce n'est pas le cas, saute vers bad boy
Je crois que c'est assez explicite. Le serial doit faire 44 caractères, et il doit commencer par fgc- ou fgf-.
On entre ensuite dans une boucle, ou plutôt un switch :
0041C69F MOV EBP,EAX ; [ebp] = le serial
0041C6A1 XOR ESI,ESI
0041C6A3 ADD EBP,4 ; déplace le pointeur de 4 caractères
0041C6A6 XOR EDI,EDI
0041C6A8 / MOV EAX,DWORD PTR SS:[EBP] ; eax = les 4 caractères suivants
0041C6AB | MOV ECX,EDI
0041C6AD | ADD EBP,4
0041C6B0 | SUB ECX,0 ; début du switch
0041C6B3 | MOV DWORD PTR SS:[ESP+1C],EAX
0041C6B7 | JE SHORT flashget.0041C6D5 ; si premier tour de boucle, saute en case 0
Ceci mérite quelques explications : en fait, le programme prend le serial, enlève les 4 premiers caractères et met les 4 suivants dans le registre eax. Si on est au premier tour de boucle (car il y a deux itérations) et si notre serial se présente ainsi : fgf-0123456789..., la valeur de eax sera 33323130 (0123). Au deuxième tour, ce sera 37363534 (4567).
On saute après en case 0, c'est à dire l'une des possibilités du switch.
Petite présicion avant de commencer, ce que j'appelle les 4 premiers caractères, c'est en fait le premier dword situé après le préfixe fgc- ou fgf- (soit 0123 dans notre exemple). N'oubliez pas aussi que l'on travaille avec les valeurs héxadécimales de chaque caractère (0 = 30h, 1 = 31h, etc...).
Voyons maintenant de quoi cela ressort :
0041C6D5 | MOV CL,BYTE PTR SS:[ESP+1E] ; cl = char3 (3ème caractère)
0041C6D9 | MOV DL,AH ; dl = char2 (2ème...)
0041C6DB | XOR ECX,EDX ; ecx = char3 ^ (xor) char2
0041C6DD | MOVSX EDX,BYTE PTR SS:[ESP+1F] ; edx = char4
0041C6E2 | AND ECX,7F
0041C6E5 | IMUL ECX,EDX ; ecx = (char3 ^ char2) * char4
0041C6E8 | MOVSX EAX,AL ; eax = char1
0041C6EB | ADD ECX,EAX ; ecx = (char3 ^ char2) * char4 + char1
0041C6ED | MOV ESI,ECX ; esi = ecx
...
0041C703 | MOVSX ECX,BYTE PTR DS:[EDI+4EF640] ; ecx = 6Bh
0041C70A | MOV EAX,ESI ; eax = (char3 ^ char2) * char4 + char1
0041C70C | XOR EDX,EDX
0041C70E | DIV ECX ; eax = ((char3 ^ char2) * char4 + char1) / 6Bh
...
0041C721 | TEST EDX,EDX ; teste si le reste est égal a 0
0041C723 | JNZ SHORT flashget.0041C730 ; si ce n'est pas le cas, saute vers bad boy
Plusieurs calculs sont effectués avec les caractères respectivement nommés char1, char2, char3 et char4 pour l'occasion. Notez aussi que j'ai cumulé les opérations afin que l'on voie la progression au fur et à mesure.
Bref, nous remarquons que le reste de la division ((char3 ^ char2) * char4 + char1) / 6Bh doit être 0. Autrement dit, la condition ((char3 ^ char2) * char4 + char1) % (modulo) 6Bh == 0 doit être vraie pour ne pas sauter vers bad boy (pour ceux qui n'ont pas compris pourquoi j'ai modifié le calcul, sachez que l'opération modulo retourne le reste d'une division...).
En faisant quelques tests avec une calculatrice ou en appliquant une méthode de brute force, on obient rapidement des résultats.
Par exemple,
2tqa,
go2z,
y75d,
ylbj
sont valides.
Il y a en tout plusieurs milliers de possibilités (17116 exactement), rien qu'en prenant des caractères alphanumériques. On étudiera ceci de plus près dans la deuxième partie de ce tutoriel si vous le voulez bien... Ce qui compte pour le moment est que nous puissions continuer avec un serial qui tienne la route (au hasard, fgc-go2zxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).
Nous entrons donc notre superbe serial, et... nous passons au deuxième tour de boucle. Une nouvelle vérification nous attend, concernant les 4 caractères suivants.
Maintenant que nous en avons l'habitude, attaquons-nous au code sans plus tarder :
0041C6BC | MOVSX ESI,BYTE PTR SS:[ESP+1E] ; esi = char7
0041C6C1 | MOVSX EDX,BYTE PTR SS:[ESP+1F] ; edx = char8
0041C6C6 | MOVSX ECX,AH ; ecx = char6
0041C6C9 | AND ESI,ECX ; esi = char 7 & (and) char6
0041C6CB | IMUL ESI,EDX ; esi = (char7 & char6) * char8
0041C6CE | MOVSX EAX,AL ; eax = char5
0041C6D1 | ADD ESI,EAX ; esi = (char7 & char6) * char8 + char5
0041C6D3 | JMP SHORT flashget.0041C6EF
...
0041C703 | MOVSX ECX,BYTE PTR DS:[EDI+4EF640] ; ecx = 65h
0041C70A | MOV EAX,ESI ; eax = (char7 & char6) * char8 + char5
0041C70C | XOR EDX,EDX
0041C70E | DIV ECX ; eax = ((char7 & char6) * char8 + char5) / 65h
...
0041C71A | CMP EDX,8 ; compare le reste à 8
0041C71D | JNZ SHORT flashget.0041C730 ; si différent, saute vers bad boy
Même type de shéma, la condition ((char7 & char6) * char8 + char5) % 65h == 8 doit être vraie. Pour vous éviter de chercher, je vais vous donner quelques valeurs :
0889,
0899,
0989,
0998.
Juste une petite information, ces 4 combinaisons sont les seules qui sont valides si l'on se limite à des caractères numériques.
À ce stade, voilà à quoi pourrait ressembler notre serial : fgc-go2z0889xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Testez-le donc (n'oubliez pas d'entrer une adresse email correcte) et redémarrez FlashGet... Yeah ! On est enregistrés !
Lorsque j'ai keygenné FlashGet pour la première fois, je me suis arrêté ici. Logique, me direz-vous.
Mais au bout de quelques jours d'utilisation, j'ai remarqué que le programme repassait en mode non enregistré. Naturellement, j'ai regardé dans la base de registre et j'ai vu que le serial avait été banni...
Un online check !, me suis-je alors dit. Et sans persévérer davantage, je me suis contenté du crack classique (celui que nous avons vu dans le premier tutoriel), qui marche parfaitement.
Jusqu'au jour où j'apprends (merci Nitrogen) qu'il existe une deuxième procédure de validation, cachée, et intervenant au bout de 58 minutes d'éxécution du programme. Essayez ! 58 minutes pour vivre... et votre serial est blacklisté !
J'ai donc cherché cette procédure et je l'ai trouvée. Je ne vous dis pas comment car ce serait trop long à expliquer, mais je peux vous donner un petit indice : les deux fonctions de validation utilisent une même constante pour faire leurs calculs (le nom de l'auteur). Après, il faut se débrouiller avec son désassembleur...
Je vous donne directement l'adresse, ce sera plus simple : 4235E0. La routine est appelée en 42A674, suite à un test sur le nombre de secondes écoulées depuis le démarrage de FlashGet. Au lieu d'attendre une heure pour que le programme breake, vous pouvez vous amuser à renverser le saut pour rentrer directement dans la procédure...
Cette dernière étant similaire à celle que nous avons étudiée, je vous laisse faire le travail ! Allez, par sympathie et parce que nous en aurons besoin pour la suite, je vous donne la condition qui doit être vraie : ((char15 | char14) * char16 + char13) % 69h == 0.
Voilà ! À vous de trouver des serials valides ;-). En voici un qui fonctionne : fgc-60150889xxxx3820xxxxxxxxxxxxxxxxxxxxxxxx. Mais sachez qu'il y en a plein d'autres !
Nous avons donc vu comment obtenir un good serial en comprenant le code et non en changeant bêtement des je par des jne ou en faisant autre chose de la sorte... C'est certes plus difficile, mais c'est aussi plus intéressant, n'est-ce pas ? Mais je ne le cache pas, comparé à d'autres programmes, FlashGet est une cible relativement facile... Enfin peu importe, j'espère que vous avez apprécié cette première partie de tutoriel et que vous n'avez pas trouvé ceci trop compliqué !
Peut-être vous posez-vous à présent une question : sur quoi va porter la deuxième partie ? Eh bien, comme je vous l'ai laissé sous-entendre tout à l'heure, nous allons voir comment créer un keygen à partir de ce que nous avons étudié ici. Ce sera donc surtout une partie coding, avis aux amateurs !
Jusqu'à la prochaine fois, un bon exercice serait d'essayer de faire un algorithme générant les différentes parties du serial, car nous n'en avons pas parlé pour le moment. Bonne chance !
Remerciements : Nitrogen (TSRh), The Analyst, Lise_Grim ainsi que la ShmeitCorp pour leurs tutoriaux.
Je salue par ailleurs les membres de la ICIteam, et tous les gens fréquentant le CrFF !