ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» º KeygenMe hccc #5 º ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ Outils: WDASM et Softice 4.0 C'est parti on lance le prog et: Boom Soft-ice detected,le prog se lance mais on peu pas appuyer sur le bouton de verif..! :-o On a deux choix: -Utilisez FrogIce pour cacher la detection (Merci MR Frogsprint). -Patcher... Le premier choix ne necessitant pas d'explication! (Enfin on sait jamais, on bouge la souris jusqu'a l'icone de frogice,on clique deux fois, Et...ahh oui faut pas oubliez de copiez le fichier correspondant a la bonne version de votre vinedoz et puis enable et puis vous pouvez lancez le prog sans probleme.) Pour le deuxieme: On desassemble,on cherche dans les String Data reference, On trouve "Soft-Ice detected :-(" On double clique et on se retrouve a: 0040134f push 004060f0 00401354 moc ecx, dword ptr [ebp+08] on remonte un peu pour trouver le jump conditionnel qui nous amene ici, et on trouve * Reference by a (U)conditional... |:0040131f(C) 00401326 push 000003ea ... on recherche donc a l'addresse 0040131f et on trouve: 0040131b cmp ax,F386 <--- Soft ice detecter 0040131f je 00401326 <--- si oui Vilain Garcon 00401321 jmp 004013f2 <--- sinon Systeme clean,lance l'application normal. (on pourrai faire la même chose avec soft-ice,on pose un bpx messageboxa pour pieger le message "Soft-ice detected",on lance le prog,ca break en 00401358 on remonte un peu pour trouver le jz 00401326 (a l'adresse 0040131f) on pose un breakpoint a l'adresse correspondante,on relance le prog,et on fait un 'r fl z' , c'est plus propre que de patcher directement un memoire en changeant le jz en jnz;r fl z sert a remplacer le resultat du cmp ax,F386 en son inverse) Passons maintenant a la recherche du serial perdu: Tatalata tatala Tatalata tatalatata Tatalata tatalaaaaaa tatalala lalala lalala (musique d'Indiana Jones..) On met un Nom et un Faux Serial,on valide et une jolie boite de message: Tu t'es plante essaie encore ;). On vas donc poser un breakpoint sur l'API que l'on suppose être utilise ici j'essaie: bpx messageboxa car je suppose que le prog utilise cette Api pour afficher la boite, (au pire on pourra poser un bpx hmemcpy qui est l'Api utilise pour gerer les saisies de caractère...) on remonte un peu pour trouver le premier call precedent, et on le trouve en :00401244 mov ecx, dword ptr [00406A98] <--- deuxieme partie du code :0040124A push ecx <--- placer dans la pile :0040124B mov edx, dword ptr [00406B00] <--- premiere partie du code :00401251 push edx <--- placer dans la pile * Possible StringData Ref from Data Obj ->"%d-%d-69" | :00401252 push 00406138 <--- placer dans la pile le format du code :00401257 push 00406A9C <--- placer dans la pile l'endroit ou sera stocke le code final :0040125C call 00401470 <--- mettre au format la premiere partie,la <--ICI deuxieme partie Explication: On met dans la pile un nombre que l'on a calcule precedemment,puis un autre, on specifie le format souhaite(ici '%d'(un chiffre); '-' (un tiret);%d(pareil);'-'(un autre tiret); '69' le chiffre 69). Donc le code sera Chiffre1-Chiffre2-69; en suite on specifie l'endroit ou sera stocke le code(ici 00406a9c) et on appelle la fonction Calcul_la_chaine_final=call 00401470: Code=Calcul_la_chaine_final(Variable_pour_stocker_le_code,format,Chiffre1,Chiffre2) Donc apres le call,le bon Serial se trouve en 00406a9c Donc pour ma part: Nom: Farad77 d 00406a9c=6707-1809-69 On teste le code,c'est bon!Maintenant passons au choses encore plus serieuse l'elaboration du Keygen: ---------------------------------- ***********_*** __* _************* |/|_ \/| _ |_ |\ | |\|_ / |__||_ | \| ********************************** ---------------------------------- On sait donc que La premiere partie du code(Chiffre1) se trouve en 00406b00,et que la deuxieme se trouve en 00406a98. Donc dans soft-ice lorsque que l'on break en 00401252(push 00406138) on remonte pour pouvoir trouver la routine de calcul.Malheureusement,on arrive à un cul de sac!!on peux plus remonter. Pas prob,Bob:On pose un bpx hmemcpy,on valide et ca break. On le disable(bd N°du bpx hmemcpy);on trace avec F12 pour sortir de l'API(9 fois) Et on arrive en: 004010a7; (Pour plus de simplicite je noterai [00406XXX]:00406XXX... voici le listing detaille,decoupe,explique ligne par ligne < :00401069 C705946A400000000000 mov dword ptr [00406A94], 00000000 <---00406a94=0 :00401073 C705006B400000000000 mov dword ptr [00406B00], 00000000 :0040107D C705986A400000000000 mov dword ptr [00406A98], 00000000 :00401087 C705506A400000000000 mov dword ptr [00406A50], 00000000 > (Cette partie correspond a l'intialisation des variables) < :00401091 6A32 push 00000032 :00401093 685C6A4000 push 00406A5C <---Variable de stockage du nom :00401098 68E8030000 push 000003E8 :0040109D 8B4508 mov eax, dword ptr [ebp+08] :004010A0 50 push eax * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :004010A1 FF15C8504000 Call dword ptr [004050C8] <----prend le texte qui est dans la zone de texte "Nom" et le place en 00406a5c > (Je crois pas qu'il y ai besoin de plus d'explication) < :004010A7 6A32 push 00000032 :004010A9 68046B4000 push 00406B04 <-- Variable de stockage du serial :004010AE 68E9030000 push 000003E9 :004010B3 8B4D08 mov ecx, dword ptr [ebp+08] :004010B6 51 push ecx * Reference To: USER32.GetDlgItemTextA, Ord:0104h | :004010B7 FF15C8504000 Call dword ptr [004050C8] <-- prend le texte qui est dans la zone de texte "Serial" et le place en 00406b04 > < :004010BD 0FBE155C6A4000 movsx edx, byte ptr [00406A5C] <---1er caractère du nom dans edx :004010C4 85D2 test edx, edx <-- test si nul(si edx est nul ca veut dire qu'il n'ya pas de premier caractère donc pas de nom) :004010C6 751B jne 004010E3 <-- si edx different de 0 saute vers la prochaine etape sinon on passe a la routine d'arret pour cause de "pas de nom" :004010C8 6A00 push 00000000 <---- 0 dans la pile pour l'appel de la messagebox (voir apres) * Possible StringData Ref from Data Obj ->"KeyGeNMe" | :004010CA 68B4614000 push 004061B4 <-- Nom de la boite de message * Possible StringData Ref from Data Obj ->"Vous devez entrer un nom" | :004010CF 6898614000 push 00406198 <-- Texte de la boite de message :004010D4 8B4508 mov eax, dword ptr [ebp+08] :004010D7 50 push eax <-- Type de bouton(ici 1944=Ok only) * Reference To: USER32.MessageBoxA, Ord:01BEh | :004010D8 FF15B8504000 Call dword ptr [004050B8] <-- Messagebox: "Vous devez rentrer un nom" :004010DE E92F020000 jmp 00401312 <-- Saute vers la fin de la grosse routine de calcul,test serial * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004010C6(C) <--Si edx different de 0 alors y a un nom | :004010E3 0FBE0D046B4000 movsx ecx, byte ptr [00406B04] <-- 1er caractère du serial dans ecx :004010EA 85C9 test ecx, ecx <-- Comme tout a l'heure pour explication cf=la haut :004010EC 751B jne 00401109 :004010EE 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"KeyGeNMe" | :004010F0 68B4614000 push 004061B4 * Possible StringData Ref from Data Obj ->"Vous devez entrer un serial" | :004010F5 687C614000 push 0040617C :004010FA 8B5508 mov edx, dword ptr [ebp+08] :004010FD 52 push edx * Reference To: USER32.MessageBoxA, Ord:01BEh | :004010FE FF15B8504000 Call dword ptr [004050B8] :00401104 E909020000 jmp 00401312 > (Cette partie est la routine d'arret en cas d'insuffisance d'information pour le calcul ou le traitement du mot de passe) < * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004010EC(C) <-- c'est bon y a un nom et un serial d'entre | :00401109 BF5C6A4000 mov edi, 00406A5C <-- edi=Nom :0040110E 83C9FF or ecx, FFFFFFFF <-- ecx=FFFFFFFF ,on le met à la valeur maximum pour scanner le Nom(au cas ou celui-ci serait un Long) :00401111 33C0 xor eax, eax <-On cherche un 0 :00401113 F2 repnz <--on repete jusqu'a ce que ce ne soit pas 0 :00401114 AE scasb <-- scas (scan string) cherche dans une chaine de caractère une valeur (ici cette operation sert à calculer la longueur du Nom). En effet,voici la demarche: Edi=...."Farad77"....=0000000046617261373700000000 repnz signifie repeter une operation sur une chaine de caractère tant que il n'y a pas de zero. l'operation ici est scasb c'est à dire scanner une chaine de caractère avec pour accumulateur al (car b=byte;donc scasw-->ax(w=word) et scasd-->eax(d=dword)). Tout ceci peut sembler un peu flou;met il suffit de comprendre que cela sert a compter le Nombre de caractère du Nom :00401115 F7D1 not ecx <-- on convertit ecx en une valeur positive :00401117 83C1FF add ecx, FFFFFFFF <-- on lui ajoute -1 car on a commence à 0 :0040111A 890D586A4000 mov dword ptr [00406A58], ecx <--- on met ca dans 00406a58 :00401120 833D586A400004 cmp dword ptr [00406A58], 00000004 <--- on teste pour savoir si la longueur superieur a 4 si oui on saute sinon on ne saute pas et on arrive à la routine:"Le nom doit comprendre au moins 4 caractères" > (Cette routine testait donc si le nombre de caractère du nom etait suffisant pour le calcul) < :00401127 7D1B jge 00401144 :00401129 6A00 push 00000000 * Possible StringData Ref from Data Obj ->"KeyGeNMe" | :0040112B 68B4614000 push 004061B4 * Possible StringData Ref from Data Obj ->"Le nom doit comprendre au moins " ->"4 caract" | :00401130 684C614000 push 0040614C :00401135 8B4508 mov eax, dword ptr [ebp+08] :00401138 50 push eax * Reference To: USER32.MessageBoxA, Ord:01BEh | :00401139 FF15B8504000 Call dword ptr [004050B8] :0040113F E9CE010000 jmp 00401312 > < * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401127(C) <--- le Nom comprend suffisament de caractères(au minimum 4) | * Possible StringData Ref from Data Obj ->"KeygenMe" | :00401144 68E4604000 push 004060E4 <---"KeyGenMe" Dans la pile :00401149 685C6A4000 push 00406A5C <---Nom Dans la pile * Possible StringData Ref from Data Obj ->"Hccc" | :0040114E 68DC604000 push 004060DC <--- "Hccc" dans la pile * Possible StringData Ref from Data Obj ->"%s%s%s" | :00401153 6844614000 push 00406144 <--- Format de sortie :00401158 68386B4000 push 00406B38 <--- Variable de stockage :0040115D E80E030000 call 00401470 <--- Calcul_la_chaine_final voir "<--ICI" (plus haut) :00401162 83C414 add esp, 00000014 :00401165 C705506A400000000000 mov dword ptr [00406A50], 00000000 <--- 00406a50=0 :0040116F EB0F jmp 00401180 > (Cette partie sert à transformer le nom entre en "Hccc'Nom'KeygenMe") (la routine qui suit est la premiere routine de calcul,celle qui va calculer la premiere partie du code:Chiffre1) < * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004011E5(U) | :00401171 8B0D506A4000 mov ecx, dword ptr [00406A50] <-- ecx=00406a50 }cette partie sert a augmenter le :00401177 83C101 add ecx, 00000001 <-- incremente ecx } pointeur pour acceder au prochain :0040117A 890D506A4000 mov dword ptr [00406A50], ecx <-- 00406a50=ecx } caractère du nom. * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040116F(U) | :00401180 8B15506A4000 mov edx, dword ptr [00406A50] <---edx=00406a50,où 00406a50 est la variable iteration,qui va permettre de compter les caractères(calcul avec le 1er,2eme,...) :00401186 0FBE82386B4000 movsx eax, byte ptr [edx+00406B38] <--- eax=ième caractère du nom ou i=edx(en ASCII) :0040118D 85C0 test eax, eax <--- si il n'ya plus de caractère :0040118F 7456 je 004011E7 <--- on passe a la deuxième phase :00401191 8B0D506A4000 mov ecx, dword ptr [00406A50] <--- ecx=pointeur :00401197 0FBE91386B4000 movsx edx, byte ptr [ecx+00406B38] <--- edx=ième caractère du nom ou i=ecx(en ASCII) :0040119E 8915946A4000 mov dword ptr [00406A94], edx <--- 00406a94=edx=ième caractère :004011A4 A1946A4000 mov eax, dword ptr [00406A94] <--- eax=ième caractère :004011A9 83C009 add eax, 00000009 <--- eax=eax+9 :004011AC A3946A4000 mov dword ptr [00406A94], eax <--- 00406a94=eax :004011B1 8B0D946A4000 mov ecx, dword ptr [00406A94] <--- ecx=00406a94 :004011B7 6BC90E imul ecx, 0000000E <--- ecx=ecx*14 :004011BA 890D946A4000 mov dword ptr [00406A94], ecx <--- 00406a94=ecx :004011C0 A1946A4000 mov eax, dword ptr [00406A94] <--- eax=00406a94 :004011C5 99 cdq <--- convertit eax de 32 en 64 bits mais ici comme il n'y a que les bits de 0 à 8 et de 8 à 16 qui sont rempli,ceux de 16 à 64 =0 donc edx=0 sauf si eax>126(en decimal) donc ce cas edx=1 :004011C6 83E203 and edx, 00000003 <--- ici comme edx=0 ou 1 en sortie ce sera ou 0 ou 3 :004011C9 03C2 add eax, edx <--- eax=eax+edx :004011CB C1F802 sar eax, 02 <--- sar=shift arithmetic right,c'est à dire un deplacement de 2 bit vers la droite,ce qui revient à faire une division signe par puissance de deux. sar eax,n est a peut pres egal à eax=int(eax/2^n),où int est une fonction qui renvoie le quotient de la division. :004011CE A3946A4000 mov dword ptr [00406A94], eax <--- 00406a94=eax :004011D3 8B15006B4000 mov edx, dword ptr [00406B00] <--- edx=00406b00(Valeur precente de Chiffre1 calcule) :004011D9 0315946A4000 add edx, dword ptr [00406A94] <--- edx=edx+00406a94(on lui ajoute eax) :004011DF 8915006B4000 mov dword ptr [00406B00], edx <--- et on le remet dans 00406b00(Chiffre1) :004011E5 EB8A jmp 00401171 <--- repart vers le debut de la routine. Voici donc la premiere partie du serial calcule,si avec soft-ice on se place en 00401e7 en qu'on fait: d 00406b00 et ?sur les 4 premieres valeurs hexa (ne pas oublier qu'il sont a l'envers);on obtient le code: Exemple: d 00406b00 --->> 0F040000 ?0000040F on obtient 1039.... (Maintenant passons au calcul de Chiffre2 et ce sera fini..!!) < * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:0040118F(C) <---Premiere partie calcule,au suivant!! | :004011E7 C705506A400000000000 mov dword ptr [00406A50], 00000000 <---00406a50=0,initialisation de la variable d'iteration :004011F1 EB0D jmp 00401200 * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:00401242(U) | :004011F3 A1506A4000 mov eax, dword ptr [00406A50] :004011F8 83C001 add eax, 00000001 :004011FB A3506A4000 mov dword ptr [00406A50], eax * Referenced by a (U)nconditional or (C)onditional Jump at Address: |:004011F1(U) | :00401200 8B0D506A4000 mov ecx, dword ptr [00406A50] <--- ecx=pointeur :00401206 0FBE91386B4000 movsx edx, byte ptr [ecx+00406B38] <--- edx=ième caractère ou i=ecx (en ASCII) :0040120D 85D2 test edx, edx <--- si plus de caractère alors c'est fini :0040120F 7433 je 00401244 <--- on peut passer à l'assemblage des 2 parties :00401211 A1506A4000 mov eax, dword ptr [00406A50] <--- eax=pointeur :00401216 0FBE88386B4000 movsx ecx, byte ptr [eax+00406B38] <--- ecx=eaxème caractère (enASCII) :0040121D 890D946A4000 mov dword ptr [00406A94], ecx <--- variable d'ajout=ecx :00401223 8B15946A4000 mov edx, dword ptr [00406A94] <--- edx=variable d'ajout :00401229 83F209 xor edx, 00000009 <--- edx=edx xor 9 Une petite parenthèse sur le Xor: Le Xor (Le sherif,le sherif de l'espace..); est une operation mathematique et un operateur,il correspond au ou exclusif voici sas table de verite: A|B|S 0|0|0 0|1|1 1|0|1 mais ce qu'il le differencie de l'operateur or c'est que: 1 or 1 = 1 1 xor 1 = 0 et en fait pour l'operation mathematique,il effectue le xor à chaque bit. ce qui a pour effet de donner un autre chiffre. La fonction Xor a l'avantage d'être involutive c'est à dire si A=B xor C alors C=B xor A...(mais bon....ca nous sert pas a grand chose pour le cas suivant). :0040122C 8915946A4000 mov dword ptr [00406A94], edx <--variable d'ajout=nouvelle valeur de edx :00401232 A1986A4000 mov eax, dword ptr [00406A98] <--eax=00406a98(Chiffre2) :00401237 0305946A4000 add eax, dword ptr [00406A94] <--eax=eax+variable d'ajout :0040123D A3986A4000 mov dword ptr [00406A98], eax <--Chiffre2=eax :00401242 EBAF jmp 004011F3 <-- continuer tant que le Nom n'est pas terminer > Et voilà on a tout decortiquer!Maintenant il suffit juste de choisir un language,et de reecrire l'algorythme qui se trouve apres les "<---". Comme je suis un paresseux je choisis le Visual Basic(Ok!c'est nul!Mais moi j'aime bien,c'est facile à utiliser même si c'est pas bien rapide et que les programmes sont de tailles exagere!Je trouve que ca se tient pour faire des Keygen...parlons francais,des generateurs de clefs...). Voila le code source en visual basic 6: Dim PremPart As String Dim DeuxPart As String Dim V00406a50 As Long Dim V00406a58 As Long Dim V00406a94 As Long Dim V00406a98 As Long Dim V00406b00 As Long Dim i As Integer Private Sub TxtNom_Change() PremPart = "" DeuxPart = "" V00406e58 = 0 V00406a50 = 0 'pointeur V00406a94 = 0 V00406b00 = 0 '<***Chiffre1 V00406a98 = 0 '<***Chiffre2 inteax = 0 i = 0 eax = 0 ebx = 0 ecx = 0 edx = 0 txt = TxtNom.Text If Len(txt) < 4 Then Label1.Caption = "Le nom doit comporte au moins 4 caractères" Else Nom = "Hccc" + txt + "KeygenMe" ecx = Len(Nom) 1 i = i + 1 If i > Len(Nom) Then GoTo fin End If edx = V00406a50 eax = Asc(Mid(Nom, i, 1)) If eax > 126 Then 'convertir les valeurs en asccii superieur a 126 en chiffre eax = eax - 256 negatifs inteax = -1 Else inteax = 0 End If ecx = V00406a50 edx = eax V00406a94 = edx eax = V00406a94 If i = 8 Then h = 0 End If eax = eax + 9 V00406a94 = eax ecx = V00406a94 ecx = ecx * 14 V00406a94 = ecx eax = V00406a94 edx = inteax edx = edx And 3 eax = eax + edx eax = Int(eax / 4) V00406a94 = eax edx = V00406b00 edx = edx + eax V00406b00 = edx ecx = V00406a50 ecx = ecx + 1 edx = ecx GoTo 1 fin: PremPart = Str$(V00406b00) V00406a50 = 0 i = 0 2 i = i + 1 If i > Len(Nom) Then GoTo fin2 End If ecx = V00406a50 edx = Asc(Mid(Nom, i, 1)) If edx > 126 Then 'convertir les valeurs en asccii superieur a 126 en chiffre edx = edx - 256 negatifs Else End If eax = V00406a50 ecx = edx V00406a94 = ecx edx = V00406a94 edx = edx Xor 9 V00406a94 = edx eax = V00406a98 eax = eax + V00406a94 V00406a98 = eax eax = V00406a50 eax = eax + 1 V00406a50 = eax GoTo 2 fin2: DeuxPart = Str$(V00406a98) Label1.Caption = PremPart + "-" + DeuxPart + "-69" End If End Sub Farad77