How To Keygen Total Recorder v3.2 (petite approche de la cryptographie)

 

Est-il réellement necessaire d'écrire de nouveau tuts ? Je ne penses pas, étant donné le nombre impressionant qui existe déjà et qui raconte tous la même chose. Cependant dans son nouveau dossier, Christal aborde la cryptographie et ceci est assez innovant (notament par la manière dont il a décidé de le traiter). C'est pour cela que j'ai décidé de rédiger ce dernier tutorial qui je l'espère montrera à certaines personnes comment la crypto a envahi le monde du keygening ;-). Je dedie donc ce tut à tous mes potes crackers que j'ai connu et qui m'ont fait découvert le cracking (TeeJi entre autre), et spécialement à Christal qui n'a jamais perdu cette passion pour réussir à venir à bout d'une protection ;-).

Première approche de Total Recorder

On va tout d'abord commencer par désassembler ce petit prog sous IDA (ici le 4.04). Ensuite on rentre un nom et un serial bidon et le classique bpx hmemcpy sous sice, puis ok puis quelques F12 pour remonter le code (je passe vite car je ne suis pas là pour vous apprendre les bases du keygening il y a bien assez de tuts) et on arrive en 4276B6. On remarquera au passage dans l'aide une copie d'écran avec la forme d'un serial à rentrer (TR30.62QM.T6AX.BMRY.BNL6) qu'on utilisera comme serial de test :

004276B6 call sub_4587AF
004276BB lea eax, [esi+60h]
004276BE lea ecx, [esp+24h+var_18]
004276C2 push eax
004276C3 call sub_455203
004276C8 lea ecx, [esp+24h+var_18]
004276CC call sub_4534A6
004276D1 lea ecx, [esp+24h+var_18]
004276D5 call sub_45345A
004276DA lea ecx, [esi+5Ch]
004276DD push ecx
004276DE lea ecx, [esp+28h+var_1C]
004276E2 call sub_455203
004276E7 lea ecx, [esp+24h+var_1C]
004276EB call sub_4534A6
004276F0 lea ecx, [esp+24h+var_1C]
004276F4 call sub_45345A
004276F9 mov edx, [esp+24h+var_18]
004276FD cmp [edx-8], ebx ; compare la taille du nom à 0
00427700 jnz short loc_427711
00427702 push ebx
00427703 push 45h
00427705 push 1
00427707 push 0EF1Fh
0042770C jmp loc_4277C4
00427711 ; ---------------------------------------------------------------------------
00427711
00427711 loc_427711: ; CODE XREF: sub_427680+80j
00427711 mov eax, [esp+24h+var_1C]
00427715 cmp dword ptr [eax-8], 18h ; compare la taille du serial à 18h
00427719 jnz loc_4277BA
0042771F push ecx
00427720 lea edx, [esp+28h+var_1C]
00427724 mov ecx, esp
00427726 mov [esp+28h+var_14], esp
0042772A push edx
0042772B call sub_454E8B
00427730 push ecx
00427731 lea eax, [esp+2Ch+var_18]
00427735 mov ecx, esp
00427737 mov [esp+2Ch+var_10], esp
0042773B push eax
0042773C mov byte ptr [esp+30h+var_4], 2
00427741 call sub_454E8B
00427746 mov byte ptr [esp+2Ch+var_4], 1
0042774B call sub_438460
00427750 add esp, 8
00427753 cmp eax, ebx
00427755 jz short loc_4277BA ; saute si mauvais serial

J'ai détaillé ce que l'on appercoit en tracant en petit peu, et notamment le fait que le serial soit de 24 (18h) caractères, soit autant que dans le serial donné en exemple. On s'apercoit également que le call sub_438460 est déterminant pour verifier si le serial est correct ou non. On va donc regarder ce qu'il y a. On y trouve 3 appels de fonctions, en regardant les 2 derniers sous ida, on s'apercoit qu'ils servent à gérer les erreurs, on va donc regarder le premier (call sub_435550) de plus près :

0043559D push ecx
0043559E push edx
0043559F push eax
004355A0 push offset aTr30 ; "TR30"
004355A5 push offset a00c311839b88e5 ; "00c311839b88e5f9345c7a"
004355AA push offset a00147bd8cb6e9c ; "00147bd8cb6e9c0429c8a7"
004355AF push offset a00b96ee50831 ; "00b96ee50831"
004355B4 push offset a00ce6b643be352 ; "00ce6b643be35209b435a1"
.......
00435640 call sub_4351A0
00435645 add esp, 4Ch
00435648 retn
00435648 sub_435550 endp

Je n'ai laissé que la partie interressante de cette fonction. Cependant on n'en sait pas plus sur le serial pour le moment. On va donc aller dans cette fonction en 4351A0 qui est la seule ici (on remarquera au passage les 5 strings passées en paramètres à cette fonction et nottament les chiffres). Dans cette fonction (4351A0) il y en a une multitude d'autres ainsi qu'un assez long code. Cependant si on rentre ds certaines d'entre elles on trouve des messages du style :bigMod: modulus is zero., ou bigDivide: divisor is zero. Et là on voit qu'il fait des calculs sur des big numbers. Le plus simple (au lieu de tout tracer) serait de trouver la lib qu'il utilise. En faisant une petite recherche sur ces strings sous google, j'ai découvert qu'il s'agissait de la CryptoLib.

C'est ici que IDA va être d'un grand recourt, on va utiliser cette librarie et créer un fichier de reconnaissance de lib pour ida, comme cela il va reconnaitre les fonctions utilisées et ce sera plus simple de comprendre le code. On recupère d'abord cette lib (ici CryptoLib v1.1) sur le net. On a les sources en C, on les compile donc en librairie static avec Visual C++ 6.0 (ou 5.0) car le programme utilise VC++ (ida nous l'apprend au début du disasm). Ensuite on va utiliser FLAIR (un programme fournit avec la version complète d'ida, ou alors il faut se débrouiller pour le récupérer). On fait les manip suivantes :

PCF cryptolib.lib cryptolib.pat

sigmake cryptolib.pat cryptolib.sig

Et on a maintenant un fichier .sig qui va permettre à ida de reconnaitre les fonctions de cette librairie. On met les .sig dans le rep d'ida concu à cette effet, puis on le charge pour qu'ida reconnaisse les fonctions (File->Load File->Flirt Signature File), et voilà notre fonction (4351A0) revu et corrigé :

004351A0 sub_4351A0 proc near ; CODE XREF: sub_435550+F0p
004351A0
004351A0 var_4C = dword ptr -4Ch
004351A0 var_48 = dword ptr -48h
004351A0 var_44 = dword ptr -44h
004351A0 var_40 = word ptr -40h
004351A0 var_38 = dword ptr -38h
004351A0 var_34 = byte ptr -34h
004351A0 var_33 = byte ptr -33h
004351A0 var_1C = dword ptr -1Ch
004351A0 var_18 = dword ptr -18h
004351A0 var_14 = dword ptr -14h
004351A0 var_10 = dword ptr -10h
004351A0 arg_0 = dword ptr 4
004351A0 arg_4 = dword ptr 8
004351A0 arg_8 = dword ptr 0Ch
004351A0 arg_C = dword ptr 10h
004351A0 arg_14 = dword ptr 18h
004351A0 arg_18 = dword ptr 1Ch
004351A0 arg_1C = dword ptr 20h
004351A0 arg_20 = dword ptr 24h
004351A0
004351A0 sub esp, 4Ch
004351A3 push ebx
004351A4 push ebp
004351A5 push esi
004351A6 push edi
004351A7 push 18h
004351A9 call _clib_malloc
004351AE mov esi, eax
004351B0 mov eax, [esp+60h+arg_0]
004351B4 push eax
004351B5 call _atobig <-- convertis les 4 hex strings passé en param en nombre
004351BA mov ecx, [esp+64h+arg_4]
004351BE mov ebp, eax
004351C0 push ecx
004351C1 call _atobig
004351C6 mov edx, [esp+68h+arg_8]
004351CA mov ebx, eax
004351CC push edx
004351CD mov [esp+6Ch+var_4C], ebx
004351D1 call _atobig
004351D6 mov [esp+6Ch+arg_4], eax
004351DA mov eax, [esp+6Ch+arg_C]
004351DE push eax
004351DF call _atobig
004351E4 mov [esp+70h+arg_8], eax
004351E8 mov eax, [esp+70h+arg_18]
004351EF add esp, 14h
004351F2 cmp byte ptr [eax], 20h
004351F5 jnz short loc_435200
004351F7
004351F7 loc_4351F7: ; CODE XREF: sub_4351A0+5E&#25;j
004351F7 mov cl, [eax+1]
004351FA inc eax
004351FB cmp cl, 20h
004351FE jz short loc_4351F7
00435200
00435200 loc_435200: ; CODE XREF: sub_4351A0+55&#24;j
00435200 mov ecx, [eax+5]
00435203 mov edx, [eax+0Ah]
00435206 mov [esp+5Ch+var_1C], ecx
0043520A mov ecx, [eax+0Fh]
0043520D mov [esp+5Ch+var_18], edx
00435211 mov edx, [eax+14h]
00435214 lea eax, [esp+5Ch+var_1C]
00435218 mov [esp+5Ch+var_14], ecx
0043521C push eax
0043521D lea ecx, [esp+60h+var_38]
00435221 push 10h
00435223 push ecx
00435224 push 5
00435226 mov [esp+6Ch+var_10], edx
0043522A call sub_434A00 <-- après quelques modifications notre serial (sans les . et le 4 premier char est passé en param à cette fonction). On constate qu'il en ressort un grand buffer buf.
0043522F push 8
00435231 call _clib_malloc
00435236 push 0
00435238 mov edi, eax
0043523A call _itobig
0043523F push eax
00435240 push ebp
00435241 mov [esi], eax
00435243 call _bigCopy
00435248 push 0
0043524A call _itobig
0043524F push eax
00435250 push ebx
00435251 mov [esi+4], eax
00435254 call _bigCopy
00435259 push 0
0043525B call _itobig
00435260 mov edx, [esp+8Ch+arg_4]
00435267 push eax
00435268 push edx
00435269 mov [esi+8], eax
0043526C call _bigCopy
00435271 push 0
00435273 call _itobig
00435278 mov [esi+0Ch], eax
0043527B push eax
0043527C mov eax, [esp+9Ch+arg_8]
00435283 push eax
00435284 call _bigCopy
00435289 mov ecx, [esi+4]
0043528C mov eax, [esi]
0043528E add esp, 44h
00435291 mov edx, [ecx+4]
00435294 mov ecx, [esi+8]
00435297 shl edx, 3
0043529A push edx
0043529B push eax
0043529C push ecx
0043529D call _g16_bigpow
004352A2 mov edx, [esi+4]
004352A5 mov ecx, [esi]
004352A7 mov [esi+10h], eax
004352AA mov eax, [edx+4]
004352AD mov edx, [esi+0Ch]
004352B0 shl eax, 3
004352B3 push eax
004352B4 push ecx
004352B5 push edx
004352B6 call _g16_bigpow
004352BB push 0
004352BD mov [esi+14h], eax
004352C0 call _itobig
004352C5 push 0
004352C7 mov [edi], eax
004352C9 call _itobig
004352CE mov [edi+4], eax
004352D1 mov eax, [edi]
004352D3 push eax
004352D4 lea ecx, [esp+80h+var_38]
004352D8 push 5
004352DA push ecx
004352DB call _bufToBig <-- convertit la première partie de notre buffer buf en big
004352E0 mov edx, [edi+4]
004352E3 lea eax, [esp+88h+var_33]
004352E7 push edx
004352E8 push 5
004352EA push eax
004352EB call _bufToBig <-- puis la deuxième partie
004352F0 push 0
004352F2 call _itobig
004352F7 mov ecx, [esp+98h+arg_14]
004352FE lea edx, [esp+98h+var_48]
00435302 push ecx
00435303 push edx
00435304 mov ebx, eax
00435306 call loc_434AC0
0043530B add esp, 44h
0043530E lea eax, [esp+5Ch+var_48]
00435312 push 1
00435314 push ebx
00435315 push 10h
00435317 push eax
00435318 call _bigMessageDigest <-- un message digest sur notre nom, qui en ressort un digest sur 160bits
0043531D mov ecx, [esp+6Ch+arg_20]
00435324 push ecx
00435325 push esi
00435326 push edi
00435327 push ebx
00435328 call sub_4353F0 <-- call très important
0043532D push esi
0043532E mov [esp+80h+arg_0], eax
00435335 call _freeEGPrivateKey
0043533A push edi
0043533B call _freeRSAPublicKey
00435340 push ebx
00435341 call _freeBignum
00435346 push ebp
00435347 call _freeBignum
0043534C mov edx, [esp+8Ch+var_4C]
00435350 push edx
00435351 call _freeBignum
00435356 mov eax, [esp+90h+arg_4]
0043535D push eax
0043535E call _freeBignum
00435363 mov ecx, [esp+94h+arg_8]
0043536A push ecx
0043536B call _freeBignum
00435370 mov esi, [esp+98h+arg_1C]
00435377 xor edx, edx
00435379 mov [esp+98h+var_48], edx
0043537D add esp, 3Ch
00435380 mov [esp+5Ch+var_44], edx
00435384 mov ecx, 5
00435389 mov [esp+5Ch+var_40], dx
0043538E lea edi, [esp+5Ch+var_48]
00435392 xor edx, edx
00435394 mov eax, esi
00435396 repe cmpsw
00435399 jz short loc_4353DE
0043539B mov dl, [esp+5Ch+var_34]
0043539F mov ebx, [esp+5Ch+var_38]
004353A3
004353A3 loc_4353A3: ; CODE XREF: sub_4351A0+23C&#25;j
004353A3 mov ecx, 5
004353A8 mov edi, eax
004353AA lea esi, [esp+5Ch+var_38]
004353AE xor ebp, ebp
004353B0 repe cmpsw
004353B3 jnz short loc_4353C9
004353B5 mov ecx, [esp+5Ch+arg_20]
004353BC mov [esp+5Ch+arg_0], ebp
004353C0 test ecx, ecx
004353C2 jz short loc_4353C9
004353C4 mov [ecx], ebx
004353C6 mov [ecx+4], dl
004353C9
004353C9 loc_4353C9: ; CODE XREF: sub_4351A0+213&#24;j
004353C9 ; sub_4351A0+222&#24;j
004353C9 add eax, 0Ah
004353CC mov ecx, 5
004353D1 lea edi, [esp+5Ch+var_48]
004353D5 mov esi, eax
004353D7 xor ebp, ebp
004353D9 repe cmpsw
004353DC jnz short loc_4353A3
004353DE
004353DE loc_4353DE: ; CODE XREF: sub_4351A0+1F9&#24;j
004353DE mov eax, [esp+5Ch+arg_0]
004353E2 pop edi
004353E3 pop esi
004353E4 pop ebp
004353E5 pop ebx
004353E6 add esp, 4Ch
004353E9 retn
004353E9 sub_4351A0 endp
004353E9

Le premier call (43a440) convertit notre serial (sans les . et les 4 premiers char) en un buffer. On remarque que les valeurs vont de 00 à FF (256 cas), et que dans ce call il utilise la chaine "23456789ABCDEFGHJKLMNPQRSTUVWXYZ" (32 char différents) pour effectuer cette conversion. Si on analyse de plus près cette fonction il s'agit en fait d'un conversion de Base : Base32 vers Base256. Donc on devrait encrypter un buffer vers une Base32 avec cette string dans notre Keygen. Pour être sur qu'il s'agit bien d'une conversion Base32->hex buffer, la seule facon est bien entendu de tester en encodant un buffer en base32, en le mettant en serial et en verifiant si cette fonction decrypte bien en nous donnant le même buffer qu'au départ ;-).

Ensuite si on analyse le call _bigMessageDigest avec le param 1 qui lui est passé, on constate qu'il appel la fonction _bigShsDigest, c'est donc la fonction de hashage à sens unique SHA-1 qui est appelé avec en paramètre notre nom (en fait notre nom est modifié avant). Il va en résulter un buffer digest de 160bits. Ensuite on voit qu'il convertit notre buffer resultant de la conversion Base32 -> Base256 (hex number) en 2 big number différents (la moitié du buffer est utilisé pour chaque big qu'il créé).

Maintenant il reste cela :

00435324 push ecx
00435325 push esi
00435326 push edi
00435327 push ebx
00435328 call sub_4353F0

Avec les deux nouveaux big créé + le digest passé en paramètres (on le trouve en tracant sous sice ou en étudiant cahque paramètres de chaque fonction sous IDA). Si on trace ce call on voit cela :

0435470 call _bigMultiply
00435475 add esp, 40h
00435478 push esi
00435479 push edi
0043547A push esi
0043547B call _bigMod
00435480 mov ecx, [ebx+8]
00435483 push 0
00435485 mov [esp+38h+arg_0], ecx
00435489 call _itobig
0043548E mov edx, [esp+38h+var_14]
00435492 mov ecx, [esp+38h+var_C]
00435496 mov ebx, eax
00435498 mov eax, [esp+38h+var_10]
0043549C push ebx
0043549D push edx
0043549E push esi
0043549F push ebp
004354A0 push eax
004354A1 push ecx
004354A2 call _double_brickell_bigpow
004354A7 push ebx
004354A8 push edi
004354A9 push ebx
004354AA call _bigMod

En analysant le code source de Cryptolib, on constate que cette fonction n'est autre que DSAVerify de la lib (IDA ne la pas reconnu à cause du différence au début du code). On est donc en présence du système DSA. Avant d'aller plus loin je vais vous le présenter.

DSA (Digital Signature Algorithm)

Je vais essayer de présenter le système de signature DSA. Pour de plus ample informations lire Cryptographie appliquée de Bruce Schneier qui est incontournable. Supposons qu'une personne A veuille signer un message m, pour montrer qu'elle en est bien l'auteur à l'aide du DSA. Pour cela elle doit d'abord créer des clefs publiques et privées qui vont servir pour la signature.

Clef Publique :

p nombre premier de 512 à 1024 bits
q facteur premier de p-1de 160 bits
g = h ^ ((p-1)/q) mod p, où h est inferieur à p-1 et (((p-1)/q) mod p) > 1
y = g ^ x mod p

Clef Privée :

x < q (160bits)
A diffuse ses clefs publiques mais garde sa clef privée. Ensuite pour signer son message m, il doit d'abord le hasher (avec SHA-1 comme standard pour le dsa) puis il fait :
k nombre aléatoire < q
r = (g ^ k mod p) mod q
s = (k ^ (-1)) * (H(m) + x*r) mod q

Le couple (r,s) est la signature du message m. Pour que la personne B puisse verfier que A a bien signer le message m, il doit faire les opérations suivantes :

w = s ^ (-1) mod q
u1 = (H(m) * w) mod q
u2 = r*w mod q
v = ((g ^u1 * y ^u2) mod p) mod q

Si v = r alors la signature est correcte et A est bien l'auteur de m.

Supposons maintenant qu' un utilisateur C veuille se faire passer pour A et signer le message M. Plusieurs possibilité se présente à lui (bien entendu dans la mesure où ceci est réalisable), il peut soit trouver la clef privé de A, soit forger une signature qui verifiera l'équation (je ne présenterais pas ici les techniques). Comment C peut t' il trouver x ? et bien il lui suffit de résoudre y = g^x mod p : c' est le porblème des logarithmes discrets (ou DLP). Il existe plusieurs algortihmes pour les résoudres (pollard rho, index calculus algorithm ...) mais il est bien evident qu'avec des nombres de 512bits ou 1024bits ceci est impossible.

Retour à Total Recorder

J'espère que cette petite explication sur le DSA vous a permis d'y voir plus clair ? non ? alors je vais tout expliquer. Si on ananlyse bien chque paramètre de la fonction DSAVerify (on etudie avec les codes sources de la lib CryptoLib) on constate ceci :

Il verifie la signature du message m qui est ici notre nom (avec qq modifs). Il le hash donc avec sha-1 pour la verification de la signature. Ensuite on constate que les 2 valeurs de la signature (r,s) sont en fait les deux différentes parties du serial decrypté en base256 :

s = partie 1 de Base32Decrypt(serial)
r = partie 2de Base32Decrypt(serial)

Ensuite il nous reste à determiner la clef publique qu'il utilise pour la verif : et bien ce sont les 4 hex string qui sont passé en paramètres. En tracant sous sice et en analysant le code source de DSAVerify, on parvient à identifier les différentes valeurs (on aurait pu le faire sans le code source, mais comme on l'a autant en profiter) :

p = 00ce6b643be35209b435a1
q = 00b96ee50831
g = 00147bd8cb6e9c0429c8a7
y = 00c311839b88e5f9345c7a

Maintenant comment peut - on créer un serial valide pour notre nom ? il faut donc hasher notre nom (il faut faire qq modifs avant qui sont également ds le prog), ensuite il faut le signer avec la clef privé x 'que l'on n'a pas), convertir s et r (signature) en un buffer où ils sont accolés, le convertir en Base32, puis créer notre serial (rajouté les 4 premiers char TR30 et les . , TR30 pour total recorder 3.0 on le constate avec le serial donné en exemple ds l'aide ;-).

Donc pour pouvoir créer un serial pour notre nom il va falloir obtenir la clef privée X. Pour cela il nous faut résoudre le DLP, or ici comme la taille de p n'est que de 80bits c'est tout à fait faisable. Pour ce faire on va utiliser la librairie C Miracl qui permet de faire des calculs sur les big numbers. On a avec cette librairie un fichier index.c qui permet de résoudre les dlps. Or comme il faut utiliser un autre fichier avec cette source (qui contient les facteurs premiers de p-1) on va utiliser la version modifier par tHE EGOiSTE (http://egoiste.cjb.net, également incluse dans les sources dlp.c), où l'on n'a plus qu'à mettre p, q, g, y et les facteurs premiers de p-1 pour résoudre le DLP (pour obtenir les facteurs premiers de p-1 on utilise factor.exe qui est fournit avec la lib miracl).

Après quelques instants de calcul on obtient la clef privée :

x = 4935E89076E03CA1CA60

On est maintenant presque en mesure de faire le keygen. Je vous ai dit qu'il modifiait le nom avant de le hasher. Il se trouve là (juste avant le hash) :

352EB call _bufToBig
004352F0 push 0
004352F2 call _itobig
004352F7 mov ecx, [esp+98h+arg_14]
004352FE lea edx, [esp+98h+var_48]
00435302 push ecx
00435303 push edx
00435304 mov ebx, eax
00435306 call loc_434AC0 <-- modif du nom (il est passé en param)
0043530B add esp, 44h
0043530E lea eax, [esp+5Ch+var_48]
00435312 push 1
00435314 push ebx
00435315 push 10h
00435317 push eax
00435318 call _bigMessageDigest

On va donc analyser cette routine et on trouve cela :

3514F loc_43514F: ; CODE XREF: sub_434E2F+327&#25;j
0043514F mov [eax+edx], al <-- Créé un buffer avec les valeur de 0 à Fh (donc 16 bytes)
00435152 inc eax
00435153 cmp eax, 10h
00435156 jl short loc_43514F
00435158 push esi
00435159 mov esi, [esp+4+arg_104]
00435160 xor eax, eax
00435162 cmp byte ptr [esi], 0
00435165 jz short loc_435192 <-- Xor les valeurs de ce buffer avec la valeur à la case correspondant du nom (sauf si espace)
00435167 push ebx
00435168
00435168 loc_435168: ; CODE XREF: sub_434E2F+360&#25;j
00435168 mov cl, [esi]
0043516A cmp cl, 20h
0043516D jz short loc_435189
0043516F mov bl, [eax+edx]
00435172 and ecx, 0FFh
00435178 mov cl, [esp+ecx+8]
0043517C xor bl, cl
0043517E mov [eax+edx], bl
00435181 inc eax
00435182 cmp eax, 10h
00435185 jnz short loc_435189
00435187 xor eax, eax
00435189
00435189 loc_435189: ; CODE XREF: sub_434E2F+33E&#24;j
00435189 ; sub_434E2F+356&#24;j
00435189 mov cl, [esi+1]
0043518C inc esi
0043518D test cl, cl
0043518F jnz short loc_435168
00435191 pop ebx
00435192
00435192 loc_435192: ; CODE XREF: sub_434E2F+336&#24;j
00435192 pop esi
00435193 add esp, 100h
00435199 retn
00435199 sub_434E2F endp ; sp = 100h
00435199

Donc il est très simple d'obtenir ce buffer qui va être utilisé pour le hash (il le passe en param avec 10h qui est la taille du buffer). Voici le code source en C :

void CreateBuffer(unsigned char Name[], unsigned char buffer[])

{

int i,j;
for (i = 0; i < 16; i++)
buffer [i] = (unsigned char) i;
for(i = j = 0; i < strlen(Name) ; i++)
if (Name[i] != ' ')
{
buffer [j] ^= Name[i];
j++;
}

}

On peut maintenant créer notre keygen (ceci es schématique et ne constitue en rien un code):

CreateBuffer(Name,buffer);
sha1(buffer,10,digest); (+ qq chgt pour qu'il soit le même que ds le prog voir source)
DSASign(digest, privateKey+publicKey, s, r);
CoppyToBuffer(s,r,Buffer2);
Base32(Buffer2, szSerial1);
ConvertSerial(szSerial1, szSerial);

 

Voici maintenant le Code source en C pour créer le serial (pour plus de détail regarder le code source dispo avec source.zip) . Il faut faire attention au fait que CryptoLib utilise directement un buffer en little endian quand il le convertit en big, alors que Miracl (utilisé pour le keygen) le récupère en big endian, il va donc falloir faire qq changements en plus ;-). :

void GenerateCode(HWND hwndname, HWND hwndserial, HWND hwndcomp)
{
static char szG[] = "147BD8CB6E9C0429C8A7";
static char szP[] = "CE6B643BE35209B435A1";
static char szQ[] = "B96EE50831";
static char szX[] = "4935E89076E03CA1CA60";
static char szK[] = "10001" ; //65537


unsigned char szR[100] = {0};
unsigned char szS[100] = {0};


miracl *mip;
big p,q,g,x,y,k,m,r,s,temp1,temp2;

unsigned int len,i;

unsigned char buffer2[10];
unsigned char temp[10];
unsigned char buffer[16];
unsigned char Name[100] = {0};
unsigned char digest[20];
unsigned char digest2[20];
unsigned char szSerial[40];
unsigned char szSerial2[100];

SHA1_CTX context;

if (GetWindowText(hwndname, Name,70) <= 0)
{
SetWindowText(hwndserial, "");
return ;
}

/* On créer le buffer à partir du nom */
CreateBuffer(Name, buffer);

/* Et on le hash avec le SHA-1 */
SHA1Init(&context);
SHA1Update(&context, buffer, 16);
SHA1Final(digest, &context);

InvertDWORD(digest); /* Pour obtenir le resultat du hash comme le prog */
InvertBuff(digest); /* On le convertit en big endian */

/* Initialize Miracl System */
mip=mirsys(100,0);

p=mirvar(0);
q=mirvar(0);
g=mirvar(0);
x=mirvar(0);
y=mirvar(0);
k=mirvar(0);
m=mirvar(0);
r=mirvar(0);
s=mirvar(0);
temp1=mirvar(0);
temp2=mirvar(0);


/* Set IO-BASE = 16 */
mip->IOBASE=16;

/* Input Bignumbers */
cinstr(p, szP) ;
cinstr(q, szQ);
cinstr(g, szG);
cinstr(x, szX);
cinstr(k, szK);

bytes_to_big(20,digest,m);

/* calcul de r: (g^k mod p)mod q */
powmod(g,k,p,r);
divide(r,q,temp2);

/* calcul de s: (k^(-1) *(m+x*r)) mod q */

multiply(x,r,temp1);
add(m, temp1, temp2);
divide(temp2,q,temp1);
xgcd(k,q,k,k,k);
multiply(temp2,k,s);
divide(s,q,temp2);

big_to_bytes(5,r,buffer2); /* on copie r ds le buffer */
big_to_bytes(5,s,buffer2+5); /* puis s */

mirkill(p);
mirkill(q);
mirkill(g);
mirkill(x);
mirkill(y);
mirkill(m);
mirkill(temp1);
mirkill(temp2);
mirkill(r);
mirkill(s);

/* On convertit en little endian car Cryptolib traite les buffer comme étant en little endian */
strncpy(temp, buffer2, 10);
for(i=0;i<5;i++)
buffer2[i] = temp[4-i];
for(i=5;i<10;i++)
buffer2[i] = temp[14-i];

/* on convertit en base32 */
i = ConvertToBase32(szSerial, buffer2, 10);
szSerial[i] = 0;

/* et on met le serial sous sa forme finale */

strcpy(szSerial2, "TR30.");
strncpy(szSerial2+5, szSerial, 4);
szSerial2[9] = '.';
strncpy(szSerial2+10, szSerial+4, 4);
szSerial2[14] = '.';
strncpy(szSerial2+15, szSerial+8, 4);
szSerial2[19] = '.';
strncpy(szSerial2+20, szSerial+12, 4);
szSerial2[24] = 0;

SetWindowText(hwndserial, szSerial2);

 Download: kgntotalrec.exe + sources


Conclusion

Voilà j'espère que cette explication sur comment keygener Total Recorder, vous aura permis soit de comprendre un peu mieux le rapport entre la cryptographie et le monde du cracking, soit vous aura donné envie de découvrir de nouvelle protection basée sur de la crypto, qu'il ne tient qu'à vous de casser (bien entendu si cela est possible ;-) .

Pour me contacter : city_of_bitch@caramail.com .

(c) 2001 - LuTiN NoIR [TMG]