Type de protection : name / 2 serials
Outils utilisés :
Ce keygenme utilise ElGamal signature scheme. Il a été codé avec la librairie MIRACL, ce qui le rend plus facile
à comprendre, étant donné qu'elle est très documentée.
Le programme n'est ni packé, ni crypté.
On ouvre le keygenme avec Olly, et on regarde rapidement le listing.
La routine qui récupère le nom et les deux serials se trouve facilement :
00401053 . 8B7C24 28 MOV EDI,DWORD PTR SS:[ESP+28] ; Case 3EB of switch 00401041 00401057 . 8B1D B4B04000 MOV EBX,DWORD PTR DS:[<&USER32.GetDlgIte>; USER32.GetDlgItemTextA 0040105D . 6A 1F PUSH 1F ; /Count = 1F (31.) 0040105F . 68 20D74000 PUSH pDrKeyge.0040D720 ; |Buffer = pDrKeyge.0040D720 00401064 . 68 E8030000 PUSH 3E8 ; |ControlID = 3E8 (1000.) 00401069 . 57 PUSH EDI ; |hWnd 0040106A . FFD3 CALL EBX ; \GetDlgItemTextA 0040106C . 8BF0 MOV ESI,EAX ; esi <-- taille du nom 0040106E . 83FE 01 CMP ESI,1 00401071 . 7D 1F JGE SHORT pDrKeyge.00401092 00401073 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL 00401075 . 68 B4C14000 PUSH pDrKeyge.0040C1B4 ; |Title = "input error" 0040107A . 68 98C14000 PUSH pDrKeyge.0040C198 ; |Text = "You have to type in a name" 0040107F . 57 PUSH EDI ; |hOwner 00401080 . FF15 ACB04000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA |
Le même schéma est utilisé pour récupérer les deux serials.
Une fois que tout est récupéré, on se trouve ici :
004010F8 > 83FE 1E CMP ESI,1E 004010FB . 8BC6 MOV EAX,ESI 004010FD . 7D 25 JGE SHORT pDrKeyge.00401124 |
Le programme compare la longueur du nom entré à 0x1E.
On pose un bpx en .4010F8 et on lance le programme (F9).
On entre un nom ( jB ) et deux serials bidons ( 1122334455 et 5544332211 ), puis on clique sur Check.
Olly break, et on arrive ici :
004010F8 > 83FE 1E CMP ESI,1E 004010FB . 8BC6 MOV EAX,ESI 004010FD . 7D 25 JGE SHORT pDrKeyge.00401124 004010FF . BA 1E000000 MOV EDX,1E 00401104 . 8DBE 20D74000 LEA EDI,DWORD PTR DS:[ESI+40D720] 0040110A . 2BD6 SUB EDX,ESI 0040110C . B8 2A2A2A2A MOV EAX,2A2A2A2A 00401111 . 8BCA MOV ECX,EDX 00401113 . 8BD9 MOV EBX,ECX 00401115 . C1E9 02 SHR ECX,2 00401118 . F3:AB REP STOS DWORD PTR ES:[EDI] 0040111A . 8BCB MOV ECX,EBX 0040111C . 83E1 03 AND ECX,3 0040111F . F3:AA REP STOS BYTE PTR ES:[EDI] 00401121 . 8D0432 LEA EAX,DWORD PTR DS:[EDX+ESI] 00401124 > C680 20D74000 >MOV BYTE PTR DS:[EAX+40D720],0 0040112B . BF 20D74000 MOV EDI,pDrKeyge.0040D720 ; ASCII "jB****************************" |
Cette routine ajoute des * à la fin du nom entré, jusqu'à obtenir 0x1E = 30 caractères pour le nom.
Le code est classique, il n'y a pas grand chose à dire de plus.
0040113A . 50 PUSH EAX 0040113B . 894C24 20 MOV DWORD PTR SS:[ESP+20],ECX 0040113F . C785 38020000 >MOV DWORD PTR SS:[EBP+238],3C 00401149 . E8 02070000 CALL pDrKeyge.00401850 0040114E . 6A 00 PUSH 0 00401150 . 894424 28 MOV DWORD PTR SS:[ESP+28],EAX 00401154 . E8 F7060000 CALL pDrKeyge.00401850 00401159 . 6A 00 PUSH 0 0040115B . 894424 20 MOV DWORD PTR SS:[ESP+20],EAX 0040115F . E8 EC060000 CALL pDrKeyge.00401850 00401164 . 6A 00 PUSH 0 00401166 . 8BF0 MOV ESI,EAX 00401168 . E8 E3060000 CALL pDrKeyge.00401850 0040116D . 6A 00 PUSH 0 0040116F . 8BD8 MOV EBX,EAX 00401171 . E8 DA060000 CALL pDrKeyge.00401850 00401176 . 6A 00 PUSH 0 00401178 . 894424 28 MOV DWORD PTR SS:[ESP+28],EAX 0040117C . E8 CF060000 CALL pDrKeyge.00401850 00401181 . 6A 00 PUSH 0 00401183 . 894424 4C MOV DWORD PTR SS:[ESP+4C],EAX 00401187 . E8 C4060000 CALL pDrKeyge.00401850 0040118C . 6A 00 PUSH 0 0040118E . 8BE8 MOV EBP,EAX 00401190 . E8 BB060000 CALL pDrKeyge.00401850 00401195 . 894424 4C MOV DWORD PTR SS:[ESP+4C],EAX |
Cela ressemble à des initialisations de bignums.
La partie intéressante commence...
00401199 . BF 20D84000 MOV EDI,pDrKeyge.0040D820 ; ASCII "1122334455" 0040119E . 83C9 FF OR ECX,FFFFFFFF 004011A1 . 33C0 XOR EAX,EAX 004011A3 . 83C4 20 ADD ESP,20 004011A6 . 33D2 XOR EDX,EDX 004011A8 . F2:AE REPNE SCAS BYTE PTR ES:[EDI] 004011AA . F7D1 NOT ECX 004011AC . 49 DEC ECX 004011AD . 85C9 TEST ECX,ECX 004011AF . 7E 32 JLE SHORT pDrKeyge.004011E3 004011B1 > 8A82 20D84000 MOV AL,BYTE PTR DS:[EDX+40D820] 004011B7 . 3C 41 CMP AL,41 ; serial[i] < 'A'? 004011B9 . 7C 04 JL SHORT pDrKeyge.004011BF 004011BB . 3C 5A CMP AL,5A ; serial[i] > 'Z'? 004011BD . 7E 10 JLE SHORT pDrKeyge.004011CF 004011BF > 3C 61 CMP AL,61 ; serial[i] < 'a'? 004011C1 . 7C 04 JL SHORT pDrKeyge.004011C7 004011C3 . 3C 7A CMP AL,7A ; serial[i] > 'z'? 004011C5 . 7E 08 JLE SHORT pDrKeyge.004011CF 004011C7 > 3C 30 CMP AL,30 ; serial[i] < '0'? 004011C9 . 7C 61 JL SHORT pDrKeyge.0040122C 004011CB . 3C 39 CMP AL,39 ; serial[i] > '9'? 004011CD . 7F 5D JG SHORT pDrKeyge.0040122C 004011CF > BF 20D84000 MOV EDI,pDrKeyge.0040D820 ; ASCII "1122334455" 004011D4 . 83C9 FF OR ECX,FFFFFFFF 004011D7 . 33C0 XOR EAX,EAX 004011D9 . 42 INC EDX 004011DA . F2:AE REPNE SCAS BYTE PTR ES:[EDI] 004011DC . F7D1 NOT ECX 004011DE . 49 DEC ECX 004011DF . 3BD1 CMP EDX,ECX 004011E1 .^7C CE JL SHORT pDrKeyge.004011B1 |
Le programme vérifie le format du 1er serial entré. Les caractères autorisés sont les chiffres, les majuscules et les minuscules.
Même procédure pour le 2e serial entré, de .4011E3 à .401228.
Puis il y a un saut vers .401272 si le format des deux serials est correct :
00401272 > 68 20D84000 PUSH pDrKeyge.0040D820 ; ASCII "1122334455" 00401277 . 55 PUSH EBP 00401278 . E8 A32F0000 CALL pDrKeyge.00404220 |
On passe deux arguments à la procédure, dont le premier serial.
A la sortie de la procédure, on regarde à l'offset ebp :
02ABACA0 02 00 00 00 AC AC AB 02 00 00 00 00 71 FF B2 E2 ............q... 02ABACB0 8C 6B 24 00 00 00 00 00 00 00 00 00 00 00 00 00 .k$.............
On obtient donc un bignum au format utilisé par MIRACL :
02 00 00 00 : nombre de dwords qu'occupe le bignum
AC AC AB 02 : offset où commence le bignum
00 00 00 00
71 FF B2 E2 8C 6B 24 : valeur du bignum (il faut lire de droite à gauche: 246B8CE2B2FF71).
La procédure semble donc être instr (lire la doc MIRACL pour plus d'informations là dessus).
On renomme donc la procédure avec Olly. En .404220, on tape : puis instr
Reste à trouver la base à partir de laquelle est converti ce nombre. On remarque plus haut :
0040113F . C785 38020000 >MOV DWORD PTR SS:[EBP+238],3C ; mip->IOBASE=60;
Le premier serial à entrer est donc en base 60.
Le deuxième serial est récupéré de la même façon.
On obtient une fois converti : B60A36616BEADD
On arrive là :
0040128C . 8B4424 28 MOV EAX,DWORD PTR SS:[ESP+28] 00401290 . 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C] 00401294 . 53 PUSH EBX 00401295 . 68 20D74000 PUSH pDrKeyge.0040D720 ; ASCII "jB****************************" 0040129A . 51 PUSH ECX ; 0x1E, longueur du nom 0040129B . C780 38020000 >MOV DWORD PTR DS:[EAX+238],10 ; mip->IOBASE=16; 004012A5 . E8 062E0000 CALL pDrKeyge.004040B0 |
On remarque un changement de la base d'entrée des nombres: on passe maintenant en base 16 (hexadécimal).
Trois arguments sont passés à la procédure: le nom auquel le programme a ajouté les *, sa longueur (0x1E=30), et ebx.
A la sortie de la procédure on a, à l'adresse pointée par ebx :
02AE2040 08 00 00 00 4C 20 AE 02 00 00 00 00 2A 2A 2A 2A ....L ......**** 02AE2050 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A 2A **************** 02AE2060 2A 2A 2A 2A 2A 2A 2A 2A 42 6A 00 00 00 00 00 00 ********Bj......
On reconnaît donc la fonction bytes_to_big.
On renomme dans Olly cette fonction.
Puis on est ici :
004012AA . 68 18C14000 PUSH pDrKeyge.0040C118 ; ASCII "AC2DB4FEC8C62992DB4F" 004012AF . 56 PUSH ESI 004012B0 . E8 6B2F0000 CALL <pDrKeyge.instr> 004012B5 . 53 PUSH EBX 004012B6 . 56 PUSH ESI 004012B7 . 6A 02 PUSH 2 004012B9 . 53 PUSH EBX 004012BA . E8 F1260000 CALL pDrKeyge.004039B0 |
instr va convertir AC2DB4FEC8C62992DB4F en bignum.
Une procédure intéressante ensuite, avec 4 arguments :
Pas de doute, c'est power (la seule procédure miracl avec ce prototype).
La fonction calcule donc :
nom ^ 2 mod AC2DB4FEC8C62992DB4F et retourne le résultat à l'adresse pointée par ebx.
On obtient : 2FDFBF53DF73D87E870
Ensuite trois chaînes sont converties en bignums :
004012BF . 68 00C14000 PUSH pDrKeyge.0040C100 ; ASCII "B54F430648C6B2A10FFB" 004012C4 . 56 PUSH ESI 004012C5 . E8 562F0000 CALL <pDrKeyge.instr> 004012CA . 8B5424 50 MOV EDX,DWORD PTR SS:[ESP+50] 004012CE . 68 E8C04000 PUSH pDrKeyge.0040C0E8 ; ASCII "2E0C2DB4FEC8C6299A0C" 004012D3 . 52 PUSH EDX 004012D4 . E8 472F0000 CALL <pDrKeyge.instr> 004012D9 . 8B7C24 64 MOV EDI,DWORD PTR SS:[ESP+64] 004012DD . 83C4 44 ADD ESP,44 004012E0 . 68 D0C04000 PUSH pDrKeyge.0040C0D0 ; ASCII "4E0F2ACAD51C4CCDFB51" 004012E5 . 57 PUSH EDI 004012E6 . E8 352F0000 CALL <pDrKeyge.instr> |
Puis on arrive à un autre endroit intéressant :
004012F3 . 50 PUSH EAX ; 0 004012F4 . 56 PUSH ESI ; B54F430648C6B2A10FFB 004012F5 . 53 PUSH EBX ; 2FDFBF53DF73D87E870 004012F6 . 51 PUSH ECX ; 2E0C2DB4FEC8C6299A0C 004012F7 . E8 54220000 CALL pDrKeyge.00403550 |
La procédure prend 4 bignums en arguments.
On obtient en sortie, à l'offset pointé par eax auparavant: 81E3FB6D45348B21495E
La fonction semble être un powmod (comme power, mais l'exposant est un big et pas un int).
Du moins c'est ce qui parait le plus probable en regardant les procédures prenant en entrée 4 bignums.
Après une vérification, on s'aperçoit que c'est bien powmod qui est effectué.
2E0C2DB4FEC8C6299A0C ^ 2FDFBF53DF73D87E870 mod B54F430648C6B2A10FFB
Ensuite vient une autre procédure :
00401304 . 52 PUSH EDX ; 0 00401305 . 56 PUSH ESI ; B54F430648C6B2A10FFB 00401306 . 50 PUSH EAX ; B60A36616BEADD (serial2) 00401307 . 55 PUSH EBP ; 246B8Ce2B2FF71 (serial1) 00401308 . 55 PUSH EBP ; 246B8Ce2B2FF71 (serial1) 00401309 . 57 PUSH EDI ; 4E0F2ACAD51C4CCDFB51 0040130A . E8 A1210000 CALL pDrKeyge.004034B0 |
Elle prend 5 bignums en entrée.
C'est donc soit mad, soit power2.
Vus les argument passés (cf la doc sur mad), on se doute que ce n'est pas mad. Donc c'est très certainement power2.
Donc ça calcule : (4E0F2ACAD51C4CCDFB51 ^ serial1) * (serial1 ^ serial2) mod B54F430648C6B2A10FFB
On obtient : 1C53CB4C2720A0755483
On a alors :
0040130F . 8B4C24 60 MOV ECX,DWORD PTR SS:[ESP+60] 00401313 . 8B5424 40 MOV EDX,DWORD PTR SS:[ESP+40] 00401317 . 51 PUSH ECX ; 1C53CB4C2720A0755483 00401318 . 52 PUSH EDX ; 81E3FB6D45348B21495E 00401319 . E8 D2120000 CALL pDrKeyge.004025F0 0040131E . 83C4 38 ADD ESP,38 00401321 . 85C0 TEST EAX,EAX 00401323 . 6A 00 PUSH 0 00401325 . 75 11 JNZ SHORT pDrKeyge.00401338 00401327 . 8B4424 2C MOV EAX,DWORD PTR SS:[ESP+2C] 0040132B . 68 C4C04000 PUSH pDrKeyge.0040C0C4 ; ASCII "GOOOD!!!" 00401330 . 68 B0C04000 PUSH pDrKeyge.0040C0B0 ; ASCII "Your key is good." 00401335 . 50 PUSH EAX 00401336 . EB 0F JMP SHORT pDrKeyge.00401347 00401338 > 8B4C24 2C MOV ECX,DWORD PTR SS:[ESP+2C] ; | 0040133C . 68 A8C04000 PUSH pDrKeyge.0040C0A8 ; |Title = "Faild" 00401341 . 68 88C04000 PUSH pDrKeyge.0040C088 ; |Text = ".... Better luck next time .." 00401346 . 51 PUSH ECX ; |hOwner 00401347 > FF15 ACB04000 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA |
On se doute que la procédure appelée en .401319 est compare.
Les deux nombres passés comme arguments à compare doivent être égaux.
Il faut donc :
(4E0F2ACAD51C4CCDFB51 ^ serial1) * (serial1 ^ serial2) mod B54F430648C6B2A10FFB = 2E0C2DB4FEC8C6299A0C ^ (nom ^ 2 mod AC2DB4FEC8C62992DB4F) mod B54F430648C6B2A10FFB
On note :
y = 4E0F2ACAD51C4CCDFB51
p = B54F430648C6B2A10FFB
alpha = 2E0C2DB4FEC8C6299A0C
M = nom
h(M) = nom ^ 2 mod AC2DB4FEC8C62992DB4F
On doit alors avoir :
(y ^ serial1) * (serial1 ^ serial2) mod p = alpha^h(M) mod p
On reconnaît alors la procédure de vérification de la signature ElGamal.
Pour plus d'informations sur cet algorithme, lisez Handbook of Applied Cryptography.
C'est traité page 454.
Il faut donc résoudre le logarithme discret :
y = alpha^x mod p
Pour cela j'ai utilisé une applet java disponible ici
Le calcul se fait en une trentaine de secondes.
On obtient :
x = 299376145767585197811667 = 3F6536A02CD18F3B67D3 en hexa
Pour simplifier les calculs, on prend k= 1 (en suivant les notations de HoAC).
Pour le keygen j'ai fait varier k, afin d'obtenir plusieurs solutions pour chaque nom.
Alors :
serial1= alpha^k mod p = alpha
serial2= k^(-1)*(h(M) - x * alpha) mod (p-1) = h(M) - x * alpha mod (p-1)
Soit :
serial1= 2E0C2DB4FEC8C6299A0C
serial2= 9845EA6D772B57E80138
On convertit en base 60 :
(le charset utilisé pour la base 60 par miracl est: 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx)
Serial 1: 1drmL9dBJgRSLW
Serial 2: 3oJxWxsfhojWQS
Et c'est terminé