Génération " Code "
Godezip version 1.99
By Christal
Heureusement qu'il existe les boites d'enregistrement. Elles permettent d'offrir quelques moments particulièrement ludiques, et j'en rends grâce à leurs auteurs.
GodeZip est un utilitaire de compression / décompression équipé d'un écran d'accueil shareware avec une option enregistrement par une boite de dialogue à 2 champs. Pour une fois, ce n'est pas un nom et un numéro de série qui doivent être entrés, mais deux sérials. Sur le coup, j'ai pensé que GodeZip risquait d'opposer plus de résistance que d'habitude…
![]()
Dans un cas comme celui ci, il y a plusieurs entrées possibles, les plus courantes sont :- Tracer au départ du programme jusqu'à trouver le call qui affichera l'écran shareware.
- Trouver ce call en utilisant Task/Hwnd/Bmsg, après l'affichage de l'écran en question.
- Etudier les string data références de Wdasm à la recherche de " programme non enregistré ", " code invalide ", " thank you… "
- Trouver la routine de contrôle du code en posant un bpx Hmemcpy
- Chercher l'écho des bons sérials en utilisant S 0 l FFFFFFFF " sérial entré "
- Etc…
A chacun sa technique, suivant ses goûts personnels. En général, j'aime bien tester les réactions du programme à une attaque directe (live approach), en faisant :- remplissage des champs de la boite d'enregistrement
(1er champ :123456789 2ème champ : Christal)
- Ctrl-D pour basculer dans SoftIce
- Bpx Hmencpy (ou GetDlgItelTextA…) à entrer sur la ligne de commande du débuggeur
- F5 pour revenir à GodeZip
- Validation de la boite d'enregistrement ( clic sur [OK] )
- Pof ! break dans Softice
- F12 pour quitter les API Windows, et revenir aux codes assembleurs de GodeZip.
- Grand sourire, Tout est là !
+ORC et Fravia, deux hauts maîtres crackeurs, conseillaient aux développeurs de désassembler leurs schémas de protection pour avoir une idée de ce à quoi ils pourraient ressembler, afin de s'assurer, au minimum, de leur discrétion. Dans le cas de GodeZip, il est clair que son auteur n'en a rien fait. Il aurait pu s'apercevoir que sa procédure contrôle_du_code avait la discrétion d'un naturiste au milieu d'un colloque de vieilles dames prenant le thé !
Avant de vous la présenter en détail, en voici un résumé :Call getdlgItem TextA saisie des caractères entrés dans un champ Contrôle du code Test eax, edx eax = 1er champ, edx = 2nd champ Je " code invalide " Mov [004271D0],00000001 adresse mémoire = 1 si code 0k
Vous êtes en face d'un des schémas de protection les plus courant. Après le contrôle du code, le programme ira en " code invalide " si la comparaison eax, edx génère un résultat nul, et sinon l'adresse mémoire [004271D0] sera mise à 1 pour valider un bon sérial.
Pour une fois, ca n'est pas directement le bon_code (eax) comparé avec le code_entré (edx), mais le résultat d'un savant calcul : les caractères du 2eme champ sont saisis dans un ordre précis et comparés un à un à ceux du 1er champ après une petite opération. A chaque fois que la comparaison est correcte, eax et edx, par un jeu de conditions logiques, se voient attribuer une valeur (0 ou 1).
Dans un cas comme celui ci, la recherche des " bons codes " ou de leur écho, n'aurait rien donné.
Regardez le listing (un peu long) de cette procédure, et vous allez tout comprendre :* Reference To: USER32.GetDlgItemTextA, Ord:00EAh | :00401CDA E889460200 Call 00426368 --> saisi du 1er champ :00401CDF 6A09 push 00000009 :00401CE1 68DD724200 push 004272DD * Possible Reference to Dialog: DialogID_4650, CONTROL_ID:4654, "" | :00401CE6 6854460000 push 00004654 :00401CEB 53 push ebx * Reference To: USER32.GetDlgItemTextA, Ord:00EAh | :00401CEC E877460200 Call 00426368 --> saisi du 2eme champ * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401C7C(C) | :00401CF1 803DDD72420044 cmp byte ptr [004272DD], 44 :00401CF8 0F94C0 sete al :00401CFB 0FB6D0 movzx edx, al :00401CFE 803DD472420047 cmp byte ptr [004272D4], 47Aussitôt après la saisie des 2 champs, le contrôle de la validité des codes commencent.
Le deuxième sérial est mis en mémoire en 00427DD et suivant. Comme j'ai entré Christal dans le deuxième champ, j'obtiens :
00427DD
00427DE
00427DF
00427E0
00427E1
00427E2
00427E3
00427E4C
h
r
i
s
t
a
lEn 00401CF1, 1er cap à passer : comparaison du contenu de 00427DD avec 44h (soit la lettre D, comme vous pourrez le voir en faisant " ? 44 " sur la ligne de commande de SoftIce). Donc le premier caractère du 2nd sérial doit obligatoirement commencer par D
Le premier sérial, lui, est mis en 004272D4, de la même façon, et le 1er caractère de ce code doit être un G (47h)
:00401D05 0F94C0 sete al :00401D08 25FF000000 and eax, 000000FF :00401D0D 21C2 and edx, eaxsete al, add eax, 000000FF, and edx, eax vont servir à chaque fois à déterminer la valeur finale qu'edx et eax contiendront.
:00401D0F 0FBE0DE3724200 movsx ecx, byte ptr [004272E3] :00401D16 0FBE05D5724200 movsx eax, byte ptr [004272D5] :00401D1D 83C01E add eax, 0000001Echargement du 7eme caractère (a) du 2nd sérial dans edx, et du 2nd caractère du 1er sérial dans eax. Ajout à celui-ci de 1Eh, soit 30 en décimal.
En (plus) clair, le 7eme caractère/ 2nd sérial est-il égal au 2nd caractère/ 1er sérial +30, et ainsi de suite:00401D20 39C1 cmp ecx, eax
:00401D22 0F94C0 sete al :00401D25 25FF000000 and eax, 000000FF :00401D2A 21C2 and edx, eax :00401D2C 0FBE0DE2724200 movsx ecx, byte ptr [004272E2] :00401D33 0FBE05D6724200 movsx eax, byte ptr [004272D6] :00401D3A 83C017 add eax, 00000017 :00401D3D 39C1 cmp ecx, eax :00401D3F 0F94C0 sete al :00401D42 25FF000000 and eax, 000000FF :00401D47 21C2 and edx, eax :00401D49 0FBE0DDF724200 movsx ecx, byte ptr [004272DF] :00401D50 0FBE05D7724200 movsx eax, byte ptr [004272D7] :00401D57 83C01F add eax, 0000001F :00401D5A 39C1 cmp ecx, eax :00401D5C 0F94C0 sete al :00401D5F 25FF000000 and eax, 000000FF :00401D64 21C2 and edx, eax :00401D66 0FBE0DE4724200 movsx ecx, byte ptr [004272E4] :00401D6D 0FBE05D8724200 movsx eax, byte ptr [004272D8] :00401D74 83C011 add eax, 00000011 :00401D77 39C1 cmp ecx, eax :00401D79 0F94C0 sete al :00401D7C 25FF000000 and eax, 000000FF :00401D81 21C2 and edx, eax :00401D83 0FBE0DE0724200 movsx ecx, byte ptr [004272E0] :00401D8A 0FBE05D9724200 movsx eax, byte ptr [004272D9] :00401D91 83C013 add eax, 00000013 :00401D94 39C1 cmp ecx, eax :00401D96 0F94C0 sete al :00401D99 25FF000000 and eax, 000000FF :00401D9E 21C2 and edx, eax :00401DA0 0FBE0DDE724200 movsx ecx, byte ptr [004272DE] :00401DA7 0FBE05DA724200 movsx eax, byte ptr [004272DA] :00401DAE 83C015 add eax, 00000015 :00401DB1 39C1 cmp ecx, eax :00401DB3 0F94C0 sete al :00401DB6 25FF000000 and eax, 000000FF :00401DBB 21C2 and edx, eax :00401DBD 0FBE0DE1724200 movsx ecx, byte ptr [004272E1] :00401DC4 0FBE05DB724200 movsx eax, byte ptr [004272DB] :00401DCB 83C01D add eax, 0000001D :00401DCE 39C1 cmp ecx, eax :00401DD0 0F94C0 sete al :00401DD3 25FF000000 and eax, 000000FF :00401DD8 85D0 test eax, edx :00401DDA 7434 je 00401E10 * Possible Reference to String Resource ID=00001: "Godezip" | :00401DDC C705D071420001000000 mov dword ptr [004271D0], 00000001 * Possible StringData Ref from Data Obj ->"Shareware Version" | :00401DE6 BFD4714200 mov edi, 004271D4 * Possible StringData Ref from Code Obj ->"Registered Version" | :00401DEB BE5A194000 mov esi, 0040195A :00401DF0 FC cld :00401DF1 B909000000 mov ecx, 00000009 :00401DF6 F3 repz :00401DF7 A5 movsd :00401DF8 A4 movsb :00401DF9 68D4724200 push 004272D4
Vous admettrez qu'en terme de discrétion, il est difficile de faire pire. C'est un exemple de comparaison byte à byte. Il est presque dommage que son auteur ait été trahis aussi facilement…
Avec une calculette et une table de correspondance ASCII il est désormais facile de calculer autant de bons sérials que vous le souhaiterez, et de les vérifier à chaque fois dans la boite d'enregistrement puisque celle-ci reste en permanence accessible.
Par jeu, j'ai voulu aller plus loin, et poser les bases d'un générateur de codes, sachant que :- le contenu de 1er champ doit être numérique pour avoir une correspondance ASCII alphanumérique, et éviter les symboles du genre ù ¨ ~> ¤ …
- le 1er caractère du 1er champ est un G
- le 1er caractère du2nd champ est un D
- les sérials doivent être d'au moins 8 caractères de long
- le calcul se fait sur la valeur ASCII des caractères entrés
- la table de correspondance est la suivante
1er champ hexa
deci
2 eme champ 2eme caractère (1E)
+
30
=
7eme caractère 3eme caractère (17)
+
23
=
6eme caractère 4eme caractère (1F)
+
31
=
3eme caractère 5eme caractère (11)
+
17
=
8eme caractère 6eme caractère (13)
+
19
=
4eme caractère 7eme caractère (15)
+
21
=
2eme caractère 8eme caractère (1D)
+
29
=
5eme caractère Comme j'avais entré en 1er sérial 123456789, j'obtiens donc :
2 --> valeur ASCII : 50d + 30d = 80d soit P pour le 7eme caractère / 2nd champ
3 --> valeur ASCII : 51d + 23d = 74d soit J pour le 6eme caractère / 2nd champ
etc…
Rien de plus facile, à partir de maintenant de générer d'autres codes " personnalisés ", par exemple, G0000000 donne DEOCMGNA ( 0 --> 48d + 30d = 78 d soit N pour le 7eme caract / 2eme champ).
Il ne rest plus qu'à écrire un petit programme qui :
- saisira le code à entrer dans le 1er champ
- vérifiera sa " compatibilité " avec le jeu de caractères acceptables
- utilisera la table de correspondance pour générer un sérial pour le 2nd champ
- affichera ce code
Si l'un d'entre vous se sent inspiré, je lui propose d'écrire ce programme, et de venir compléter cet essai par son propre texte…
L'approche dynamique, et le passage par une feuille de papier, a suffit pour trouver le code. Une approche par le Dead Listing (Wdasm) aurait donné un résultat identique, en partant de la string data références " registered version ". Reste maintenant que vous êtes enregistré avec un code bizarre. A la veille de la fête des mères, j'aurais voulu voir comme numéro d'enregistrement " à maman ", une sorte d'hommage, un reste de complexe Oedipien…
Or le sérial d'enregistrement qui apparaîtra est celui du 1er champ, qui, pour une question de correspondance ASCII, doit être de type numérique. Problème facile à résoudre :
Cette fois ci, c'est le jump_if_egal en 00401DDA qu'il va falloir inverser pour aller en mov [004271D0],00000001, l'adresse mémoire mise à 1 pour un sérial valide.
Donc au break en 00401DDA, il faudra, par exemple, taper " a " pour passer en mode assemblage, puis jne 00401E10. touche [Echap] pour quitter le mode assembleur, et F5 pour retourner à GodeZip. Le sérial est accepté, et apparaît à l'écran comme souhaité. Fermeture de l'application.
A la réouverture, l'écran shareware propose à nouveau de s'enregistrer. Pas bien ça !
Réfléchissons :- [004271D0] ne sert pas à valider le statut de l'utilisateur
- les breakpoints précédents n'ayant pas été effacés, la procédure de contrôle des codes n'a pas été sollicitée, sinon SoftIce aurait réagit.La seule explication, c'est qu'au lancement de GodeZip, celui ci re-contrôle les sérials entrés, et mémorisés dans godezip. Ini :
taux_compression=6
association=1
Install=0
licencenum=G2345678
licencekey=DLSIUJPF
ZX=25
ZY=30
ontop=0Si ceux ci ne sont pas sharewarement corrects, l'écran d'accueil réapparaît. Bien! reste à savoir où a lieu ce deuxième contrôle.
Pour avoir une copie complète de la routine de contrôle des sérials, j'avais utilisé Wdasm pour désassembler GodeZip, puis importé le listing obtenu (godezip.alf) dans un éditeur de texte, mais je n'avais pas jugé bon, alors, de questionner les string data références. Si je l'avais fait, j'aurais connu d'avance la réponse à ma question.
En utilisant la fonction " find " de l'éditeur de texte, j'ai trouvé un miroir de la procédure de contrôle des sérials en recherchant une occurrence sur movsx ecx, byte ptr [004272E1], et comme vous allez pouvoir vous en rendre compte, les lignes qui suivent sont la copie conforme de celles que nous connaissons déjà.:00401C08 0FBE05DA724200 movsx eax, byte ptr [004272DA] :00401C0F 83C015 add eax, 00000015 :00401C12 39C1 cmp ecx, eax :00401C14 0F94C0 sete al :00401C17 25FF000000 and eax, 000000FF :00401C1C 21C2 and edx, eax :00401C1E 0FBE0DE1724200 movsx ecx, byte ptr [004272E1] :00401C25 0FBE05DB724200 movsx eax, byte ptr [004272DB] :00401C2C 83C01D add eax, 0000001D :00401C2F 39C1 cmp ecx, eax :00401C31 0F94C0 sete al :00401C34 25FF000000 and eax, 000000FF :00401C39 85D0 test eax, edx :00401C3B 742C je 00401C69 * Possible Reference to String Resource ID=00001: "Godezip" | :00401C3D C705D071420001000000 mov dword ptr [004271D0], 00000001 * Possible StringData Ref from Data Obj ->"Shareware Version" | :00401C47 BFD4714200 mov edi, 004271D4 * Possible StringData Ref from Code Obj ->"Registered Version" | :00401C4C BE5A194000 mov esi, 0040195A :00401C51 FC cld :00401C52 B909000000 mov ecx, 00000009 :00401C57 F3 repzEt notamment le même jump_if_egal qui envoit vers le mov [004271D0],1.
Que reste-il à faire ? Inverser ce nouveau je, de la même façon que le précédent, et GodeZip démarrera sans écran d'accueil shareware. Dans la boite About, je retrouve le sérial que je voulais…
Je suis content…
A bientôt.
Christal@lemel.fr