Crypto KeygenMe #02 de jB
Solution par Bigbang

Type de Protection: FSG 1.33 / SHA256 / Logarithme Discret
Outils utilisés: OllyDbg, http://www.alpertron.com.ar/DILOG.HTM
Langage: ASM32 (biglib de Roy)

Sommaire :

I. Analyse du KeygenMe
II. Codage du keygen

I. Analyse du KeygenMe

Un petit coup de PEiD nous indique que l'exe a été compréssé avec FSG 1.33. Je l'ai unpacké manuellement avec le plugin OllyDump de Olly. Cette partie ne présente aucune difficulté, je me permet de ne pas en parler. Le dump fonctionnel est dans l'archive.

Connaissant jB, j'ai passé l'exe dans le cryptosearcher de x3chun qui a détecté comme tout le monde s'y attendait des signatures de crypto.
Voici le screen :

Grosse information : le keygen utilise la biglib de Roy.
Je charge donc l'exe dans IDA. Et grâce à cet outil merveilleux qu'est IDA et ses signatures FLIRT, on a remplacé tous les call 00xxxxxx par le nom réel des fonctions de la biglib de Roy. J'ai joint le fichier biglib.sig dans l'archive, il suffit ensuite de le charger depuis le menu File > Load File > Flirt Signature File...
Je rappel car ça peut être très pratique que n'importe quel fichier .lib peut permettre de construire un fichier .sig
Pour cela, IDA a codé Flair. Vous devez avant tout convertir votre .lib et .pcf, puis le .pcf donnera le .sig que vous pourrez charger dans IDA.
Bien, revenons à nos moutons, nous avons maintenant les fonctions utilisées :

seg000:0040443F ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
seg000:0040443F
seg000:0040443F ; Attributes: bp-based frame
seg000:0040443F
seg000:0040443F sub_40443F proc near ; CODE XREF: DialogFunc+EDp
seg000:0040443F
seg000:0040443F lpAddress = dword ptr -28h
seg000:0040443F var_24 = dword ptr -24h
seg000:0040443F var_20 = dword ptr -20h
seg000:0040443F var_1C = dword ptr -1Ch
seg000:0040443F var_18 = dword ptr -18h
seg000:0040443F var_14 = dword ptr -14h
seg000:0040443F var_10 = dword ptr -10h
seg000:0040443F var_C = dword ptr -0Ch
seg000:0040443F var_8 = dword ptr -8
seg000:0040443F var_4 = dword ptr -4
seg000:0040443F
seg000:0040443F push ebp
seg000:00404440 mov ebp, esp
seg000:00404442 add esp, 0FFFFFFD8h
seg000:00404445 pusha
seg000:00404446 mov ds:dword_407960, 0
seg000:00404450 mov esi, offset dword_4078B0
seg000:00404455 xor ebx, ebx
seg000:00404457 xor ecx, ecx
seg000:00404459 xor edx, edx
seg000:0040445B
seg000:0040445B loc_40445B: ; CODE XREF: sub_40443F+46j
seg000:0040445B ; sub_40443F+58j
seg000:0040445B movzx eax, byte ptr [ecx+esi]
seg000:0040445F test eax, eax
seg000:00404461 jz short loc_404499
seg000:00404463 cmp eax, 41h
seg000:00404466 jl short loc_404472
seg000:00404468 cmp eax, 5Ah
seg000:0040446B ja short loc_404472
seg000:0040446D sub eax, 41h
seg000:00404470 jmp short loc_40447F
seg000:00404472 ; ---------------------------------------------------------------------------
seg000:00404472
seg000:00404472 loc_404472: ; CODE XREF: sub_40443F+27j
seg000:00404472 ; sub_40443F+2Cj
seg000:00404472 cmp eax, 30h
seg000:00404475 jl short loc_404487
seg000:00404477 cmp eax, 35h
seg000:0040447A ja short loc_404487
seg000:0040447C sub eax, 16h
seg000:0040447F
seg000:0040447F loc_40447F: ; CODE XREF: sub_40443F+31j
seg000:0040447F shl ebx, 5
seg000:00404482 inc ecx
seg000:00404483 add ebx, eax
seg000:00404485 jmp short loc_40445B
seg000:00404487 ; ---------------------------------------------------------------------------
seg000:00404487
seg000:00404487 loc_404487: ; CODE XREF: sub_40443F+36j
seg000:00404487 ; sub_40443F+3Bj
seg000:00404487 mov ds:dword_407914[edx*4], ebx
seg000:0040448E xor ebx, ebx
seg000:00404490 inc edx
seg000:00404491 inc ecx
seg000:00404492 cmp edx, 9
seg000:00404495 ja short loc_404499
seg000:00404497 jmp short loc_40445B
seg000:00404499 ; ---------------------------------------------------------------------------
seg000:00404499
seg000:00404499 loc_404499: ; CODE XREF: sub_40443F+22j
seg000:00404499 ; sub_40443F+56j
seg000:00404499 mov ds:dword_407914[edx*4], ebx
seg000:004044A0 push 10h
seg000:004044A2 push offset aKeygenme2ByJb ; "KeygenMe 2 by jB"
seg000:004044A7 call sub_4041E6
seg000:004044AC push 24h
seg000:004044AE push offset dword_407914
seg000:004044B3 call sub_40423A
seg000:004044B8 push 0
seg000:004044BA call __BigCreate@4 ; _BigCreate(x)
seg000:004044BF mov [ebp+var_4], eax
seg000:004044C2 push 0
seg000:004044C4 call __BigCreate@4 ; _BigCreate(x)
seg000:004044C9 mov [ebp+var_8], eax
seg000:004044CC push 0
seg000:004044CE call __BigCreate@4 ; _BigCreate(x)
seg000:004044D3 mov [ebp+var_C], eax
seg000:004044D6 push 0
seg000:004044D8 call __BigCreate@4 ; _BigCreate(x)
seg000:004044DD mov [ebp+var_10], eax
seg000:004044E0 push 0
seg000:004044E2 call __BigCreate@4 ; _BigCreate(x)
seg000:004044E7 mov [ebp+var_14], eax
seg000:004044EA push 0
seg000:004044EC call __BigCreate@4 ; _BigCreate(x)
seg000:004044F1 mov [ebp+var_18], eax
seg000:004044F4 push 0
seg000:004044F6 call __BigCreate@4 ; _BigCreate(x)
seg000:004044FB mov [ebp+var_1C], eax
seg000:004044FE push 0
seg000:00404500 call __BigCreate@4 ; _BigCreate(x)
seg000:00404505 mov [ebp+var_20], eax
seg000:00404508 push 0
seg000:0040450A call __BigCreate@4 ; _BigCreate(x)
seg000:0040450F mov [ebp+var_24], eax
seg000:00404512 push 0
seg000:00404514 call __BigCreate@4 ; _BigCreate(x)
seg000:00404519 mov [ebp+lpAddress], eax
seg000:0040451C push [ebp+var_4]
seg000:0040451F push 10h
seg000:00404521 push offset a357a3c9d045740 ; "357A3C9D0457404AE1A83BA9DF6BD3CB7DF92C0"...
seg000:00404526 call __BigIn@12 ; _BigIn(x,x,x)
seg000:0040452B push [ebp+var_C]
seg000:0040452E push 10h
seg000:00404530 push offset a6ab353f03e1 ; "6AB353F03E1"
seg000:00404535 call __BigIn@12 ; _BigIn(x,x,x)
seg000:0040453A push [ebp+var_10]
seg000:0040453D push 10h
seg000:0040453F push offset a276bebf74a8157 ; "276BEBF74A8157614CBF6B2A5FD51C2B7CF3B65"...
seg000:00404544 call __BigIn@12 ; _BigIn(x,x,x)
seg000:00404549 push [ebp+var_14]
seg000:0040454C push 10h
seg000:0040454E push offset a1740384ccebad6 ; "1740384CCEBAD64F59ACE57FE5C0F9A3A499CE7"...
seg000:00404553 call __BigIn@12 ; _BigIn(x,x,x)
seg000:00404558 push [ebp+var_20]
seg000:0040455B push 0Ah
seg000:0040455D push offset dword_40784C
seg000:00404562 call __BigIn@12 ; _BigIn(x,x,x)
seg000:00404567 push offset dword_407934 ; lpString
seg000:0040456C call lstrlenA
seg000:00404571 mov edx, 20h
seg000:00404576 add edx, eax
seg000:00404578 push [ebp+var_1C]
seg000:0040457B push edx
seg000:0040457C push offset dword_407914
seg000:00404581 call __BigInB256@12 ; CGUFOV06DISAWL36CATSYDH6DZFDG3W6CV4P5236GV3UDH6BIDO5TN6BXN3DJE6G3D3Q6A6(x,x,x)
seg000:00404586 push [ebp+var_4]
seg000:00404589 push [ebp+var_1C]
seg000:0040458C call __BigCompare@8 ; _BigCompare(x,x)
seg000:00404591 cmp eax, 0FFFFFFFFh
seg000:00404594 jnz loc_40464E
seg000:0040459A push [ebp+var_C]
seg000:0040459D push [ebp+var_20]
seg000:004045A0 call __BigCompare@8 ; _BigCompare(x,x)
seg000:004045A5 cmp eax, 0FFFFFFFFh
seg000:004045A8 jnz loc_40464E
seg000:004045AE push [ebp+var_8]
seg000:004045B1 push 2
seg000:004045B3 push [ebp+var_4]
seg000:004045B6 call __BigSub32@12 ; _BigSub32(x,x,x)
seg000:004045BB push [ebp+var_24]
seg000:004045BE push [ebp+var_4]
seg000:004045C1 push [ebp+var_1C]
seg000:004045C4 push [ebp+var_14]
seg000:004045C7 call __BigPowMod@16 ; _BigPowMod(x,x,x,x)
seg000:004045CC push [ebp+var_14]
seg000:004045CF push [ebp+var_4]
seg000:004045D2 push [ebp+var_8]
seg000:004045D5 push [ebp+var_24]
seg000:004045D8 call __BigPowMod@16 ; _BigPowMod(x,x,x,x)
seg000:004045DD push [ebp+var_24]
seg000:004045E0 push [ebp+var_4]
seg000:004045E3 push [ebp+var_20]
seg000:004045E6 push [ebp+var_10]
seg000:004045E9 call __BigPowMod@16 ; _BigPowMod(x,x,x,x)
seg000:004045EE push [ebp+var_10]
seg000:004045F1 push [ebp+var_4]
seg000:004045F4 push [ebp+var_14]
seg000:004045F7 push [ebp+var_24]
seg000:004045FA call __BigMulMod@16 ; _BigMulMod(x,x,x,x)
seg000:004045FF push [ebp+var_14]
seg000:00404602 push [ebp+var_4]
seg000:00404605 push [ebp+var_1C]
seg000:00404608 push [ebp+var_10]
seg000:0040460B call __BigMulMod@16 ; _BigMulMod(x,x,x,x)
seg000:00404610 push offset dword_407938
seg000:00404615 push [ebp+var_14]
seg000:00404618 call __BigOutB256@8 ; _BigOutB256(x,x)
seg000:0040461D push offset String ; lpString
seg000:00404622 call lstrlenA
seg000:00404627 push eax
seg000:00404628 push offset String
seg000:0040462D call sub_404196
seg000:00404632 mov esi, eax
seg000:00404634 mov edi, offset dword_407938
seg000:00404639 mov ecx, 20h
seg000:0040463E repe cmpsb
seg000:00404640 test ecx, ecx
seg000:00404642 jnz short loc_40464E
seg000:00404644 mov ds:dword_407960, 1
seg000:0040464E
seg000:0040464E loc_40464E: ; CODE XREF: sub_40443F+155j
seg000:0040464E ; sub_40443F+169j ...
seg000:0040464E push [ebp+lpAddress] ; lpAddress
seg000:00404651 call __BigDestroy@4 ; _BigDestroy(x)
seg000:00404656 push [ebp+var_24] ; lpAddress
seg000:00404659 call __BigDestroy@4 ; _BigDestroy(x)
seg000:0040465E push [ebp+var_20] ; lpAddress
seg000:00404661 call __BigDestroy@4 ; _BigDestroy(x)
seg000:00404666 push [ebp+var_1C] ; lpAddress
seg000:00404669 call __BigDestroy@4 ; _BigDestroy(x)
seg000:0040466E push [ebp+var_18] ; lpAddress
seg000:00404671 call __BigDestroy@4 ; _BigDestroy(x)
seg000:00404676 push [ebp+var_14] ; lpAddress
seg000:00404679 call __BigDestroy@4 ; _BigDestroy(x)
seg000:0040467E push [ebp+var_10] ; lpAddress
seg000:00404681 call __BigDestroy@4 ; _BigDestroy(x)
seg000:00404686 push [ebp+var_C] ; lpAddress
seg000:00404689 call __BigDestroy@4 ; _BigDestroy(x)
seg000:0040468E push [ebp+var_4] ; lpAddress
seg000:00404691 call __BigDestroy@4 ; _BigDestroy(x)
seg000:00404696 xor eax, eax
seg000:00404698 popa
seg000:00404699 leave
seg000:0040469A retn
seg000:0040469A sub_40443F endp
seg000:0040469A
seg000:0040469A ; ---------------------------------------------------------------------------

Cette étape nous a fait gagner un temps colossale. Merci IDA :)

J'ai posé un bpx sur le début de la routine, et j'ai rentré les paramètres suivant :
Name = bigbang
ID = 1111111111
Serial = 123456789ABCDEF

Je presse check, et olly break :)

Dans le début de cette routine, il y a une première boucle qui génère, à partir du serial, une série de DWORD. Nous y reviendrons plus tard.
Juste après, il y a des procédures appelées, une qui génère un tableau de 256 éléments (d'abord initialisé par 0..255 puis crypté avec la clé "KeygenMe 2 by jB")
Quels que soient les paramètres que l'on rentre, ce tableau est le même :

C'est le deuxième call qui est intéressant, il procède à des échanges d'éléments, et effectue des xor sur les DWORDs trouvés dans la première boucle. De même, nous reviendrons sur ceci par la suite.

Comme on peut le voir sur le listing, on crée ensuite 10 bignums de la biglib de Roy a l'aide de _BigCreate. Ils sont initialisés à 0
Juste après et logiquement, on en fixe certains que je vais noter comme suit (dans l'ordre ) :

n = 357A3C9D0457404AE1A83BA9DF6BD3CB7DF92C0BAD68FB3A5E9ACB9CF54E7D7A07E3
n2 = 6AB353F03E1
a = 276BEBF74A8157614CBF6B2A5FD51C2B7CF3B652DCC1EB0D34FDD38BB9934D9203E2
p = 1740384CCEBAD64F59ACE57FE5C0F9A3A499CE7BF174B2D92BC15E7997B46791CBFA

Le dernier, vous l'aurez reconnu, correspond à l'id que l'on a rentré. On peut noter ici qu'il doit être rentré en base 10.

Une dernière fonction est appelée avant de commencer les festivités, c'est la fonction __BigInB256 de la lib de Roy. Définie comme ceci dans la doc de la librairie :

_BigInB256 (char *)Data, (dword)Len, (big *)Big
[LIBio.asm]
fills Big with Len bytes of Data in base 256

En fait, on convertit en bignum (que j'appelerais q par la suite) ce qu'on a trouvé dans les deux calls suivant la première boucle.

On compare ensuite deux bignums entre eux avec _BigCompare : q et n.

_BigCompare (big *)BigA, (big *)BigB
[LIBcore.asm]
compares BigA and BigB
returns 1 if BigA > BigB
0 if BigA = BigB
-1 if BigA < BigB

Ici, on doit avoir q < n
Une deuxième comparaison, entre n2 et ID. De la même manière, on doit avoir ID < n2

Avec ce que j'ai rentré, la première condition n'est pas remplie. C'est pas bien grave pour comprendre la suite. Je change donc l'état du ZF sur le premier JNZ (en 00404594). On passe donc outre la difficulté.
Pour ce qui est de la deuxième comparaison, tout est au mieux dans le meilleur des mondes, et on passe sans faire quoi que ce soit.

C'est maintenant que nous attaquons le coeur du problème.
On calcule d'abord n-2 avec
_BigSub32
Puis trois
_BigPowMod sur les bignums que l'on a défini auparavant
Pour finir, on a deux
_BigMulMod

J'ai essayé de commenter les opérations dans Olly :

Le résultat final est comparé au hash sha256 du nom. En fait, ce n'est pas tout à fait le sha256 du nom. Il s'agit bien de hasher le nom par sha256, mais les huits DWORD sont bswaped.

Pour "bigbang" par exemple, on a sha = CCC1A4AD5C9D53F3258D3678213537728C63548C34E4F9B3B504E27F7A3B50A3
et pas sha256("bigbang") = ADA4C1CCF3539D5C78368D25723735218C54638CB3F9E4347FE204B5A3503B7A

J'appelerais sha par la suite ce pseudo-sha256

Voici un résumé de ce que doit vérifer le keygenme :

(p^q)^(n-2) * a^id * q = sha (mod n)


Il s'agit de trouver id et q qui dépend du serial.

Il faut donc chercher à simplifier cette expression afin d'en tirer ce que l'on veut
Après quelques recherches sur les liens que pouvaient avoir entre eux, j'ai constater que (p^q)^(n-2) = p^(-q) (mod n)

En effet, dans le cas présent, nous avons :

n = 357A3C9D0457404AE1A83BA9DF6BD3CB7DF92C0BAD68FB3A5E9ACB9CF54E7D7A07E3
p = 1740384CCEBAD64F59ACE57FE5C0F9A3A499CE7BF174B2D92BC15E7997B46791CBFA
a = 276BEBF74A8157614CBF6B2A5FD51C2B7CF3B652DCC1EB0D34FDD38BB9934D9203E2

q = D3EFB46A21C4732242ED0FF997AE6680119CB3275334A7BED87AD52F5EB68ECDB08F6E = f(SERIAL)
id = 423A35C7 = base_16(ID)

L'inverse de p est p^(-1) mod n = 148309670FBE0A8100BEBE1906EBC72BF410F71047F97F987F31A6A9712073F3067A
donc p^(-q) mod n = (p^(-1))^q mod n = 32732A4832ED09D773905BE8622542CCB4E5F71C47EDB7434090A4485B23BA863993

Et si on calcule (p^q)^(n-2) mod n, on trouve la même chose.

On peut donc simplifier l'écriture :

p^(-q) * a^id * q = sha (mod n)
a^id * q = sha * p^q (mod n)
a^id = sha * p^q * q^(-1) (mod n)

q est une fonction du serial. On peut très bien se permettre de fixer q = sha. Il faudra alors reverser le début du keygenme que je n'ai pas expliqué afin de tomber sur q = sha

Ce qui simplifie encore l'expression:

a^id = sha * p^sha * sha^(-1) (mod n)
a^id = p^sha (mod n)

On peut calculer p^sha, ce qui revient donc à résoudre le logarithme discret : a^x = p^sha (mod n)

J'ai utilisé un applet java pour résoudre ce logarithme discret. Vous pouvez le trouver ici : http://www.alpertron.com.ar/DILOG.HTM
Attention toutefois, tous les nombres doivent d'abord être convertit en décimal.

Au passage, on peut remarquer que l'on travail dans un sous-groupe multiplicatif de 7332402496481 éléments
et 7332402496481d = 6AB353F03E1h = n2

Cette remarque va nous servira juste après.

Pendant ce temps, on a résolu le logarithme discret, on a trouvé :
x = 32869882155584600956504510666659520182545221391386231386953693134495147283615761 (in dec)
x = 11BDEAE028EA6B42922D592CC55ADFBE996AB6F85DF8391C09C352F589A332E9011 (in hex)

Le problème si vous rentrez cette valeur pour id dans le keygenme, c'est la limite de taille imposée par la deuxième comparaison
Heureusement, nous avons remarqué que a ^ x = a ^ (x mod n2) mod n

Donc notre id est le reste de la division euclidienne de x par n2
C'est à dire que notre id = x mod n2 et convertit en base 10 !

Ici, je trouve id = 3604367769262

Nous avons l'id, il nous faut maintenant le serial
Nous avons posé q = sha et q = f(serial)
Il faut donc reverser f pour trouver le serial. Je reviens donc aux éléments du début que j'avais laissé tombé.

Je commence par le tabbleau des 256 éléments.
En regardant d'un plus près la routine et surtout la boucle répétée 36 fois, on s'aperçoit qu'il y a des échanges d'éléments et aussi (et surtout) un xor sur les DWORDs générés dans la tout première boucle du keygenme. Ce sont ces DWORDs qui nous intéressent afin de remonter au serial.

Je relance donc le keygenme. J'entre de nouveau "bigbang", l'id correct correspondant, et une chaine bidon dans serial ("123456789ABCDEF"). Ca break de nouveau sur le début de la procédure de vérification.
Je passe pour le moment la première boucle et je pose un bpx en 004044AC. Je mets à zéro la zone où les 9 DWORDS seront xored.
Le tableau étant indépendants des paramètres rentrés, on aura toujours la même séquence de xor. Donc en mettant à zéro, on pour facilement retrouver les DWORDs que doit produire la première boucle, ceci à partir de sha.

Après avoir tout mis à zéro, je passe par dessus le call par un F8 et on constate que les 9 DWORDs de zéros on été xored
Voici ce que ça donne (j'appelle cst cette constante) :

Et comme xor(0,a) = a alors il suffit d'appliquer un xor cst[i] à chaque sha[i] :

76 7B E6 20 21 C4 73 22 42 ED 0F F9 97 AE 66 80 11 9C B3 27 53 34 A7 BE D8 7A D5 2F 5E B6 8E CD B0 8F 6E 00 <= cst

CC C1 A4 AD 5C 9D 53 F3 25 8D 36 78 21 35 37 72 8C 63 54 8C 34 E4 F9 B3 B5 04 E2 7F 7A 3B 50 A3 00 00 00 00 <= sha

BA BA 42 8D 7D 59 20 D1 67 60 39 81 B6 9B 51 F2 9D FF E7 AB 67 D0 5E 0D 6D 7E 37 50 24 8D DE 6E B0 8F 6E 00 <= cst xor sha

Il ne nous reste plus qu'à reverser la première boucle du keygenme. Celle-ci doit donc produire cette séquence :
BABA428D7D5920D167603981B69B51F29DFFE7AB67D05E0D6D7E3750248DDE6EB08F6E00

Voici le début du keygenme :

Voici grossièrement ce qui se passe :
On prends un caractère c du serial
si 41h < c < 5Ah alors c = c - 41h
si 30h < c < 35h alors c = c - 16h

si c = 36h (par exemple), alors on copie EBX dans un des fameux DWORDs
sinon, on multiplie EBX par 20h et on ajoute c
En tout, on copie 9 DWORDs

Je vais détailler pour le premier, et je vous donnerais ensuite ma fonction C qui automatise cela
Nous cherchons à obtenir en premier en mémoire :
BA BA 42 8D
On doit donc avoir EBX = 8D42BABAh juste avant la copie en mémoire.
Il s'agit donc de trouver EAX compris entre 0 et 20h tel que 8D42BABAh - EAX soit un multiple de 20h
La division eucidienne de 8D42BABAh par 20h donne 8D42BABAh = 20h * 46A15D5h + 1Ah
En procédant de la même avec 46A15D5h et encore jusqu'à obtenir un nombre plus petit que 20h, on construit une séquence de nombres compris entre 0 et 20h qui sont les restes des divisions successives par 20h
Pour 8D42BABAh, on trouve la séquence : 2, 6, 14h, 5, E, 15h, 1A
Ce qui conduit à la séquence de caractère (suivant les conditions sur c mentionnées plus haut) : CGUFOV0
Mais si on rentre ceci tel quel dans le serial, la boucle ne copiera pas EBX en mémoire, il faut donc lui dire de le faire. En ajoutant un '6' par exemple à la fin : CGUFOV06

J'ai automatisé ceci dans une fonction C en faisant bien attention à l'ordre des termes :

void RemEucl(unsigned int a,char r[8])
{

int i,j,len,rem;
char tmp;
char rr[8];

i=0;rem=0;
while(a>0x20)
{
rem=a%0x20;
if(rem<=0x19) r[i]=rem+0x41;
if(rem>=0x1A) r[i]=rem+0x16;
a/=0x20;
i++;
}
if(a<=0x19) r[i]=a+0x41;
if(a>=0x1A) r[i]=a+0x16;
r[i+1]=0;

len=strlen(r);

for(i=0,j=len-1;i<len;i++,j--) rr[j]=r[i];
rr[i]=0;

for(i=0;i<len;i++) r[i]=rr[i];
r[i]='6';
r[i+1]=0;

}

Le code en lui même n'est pas super, mais ca marche à peu près :)

Il suffit donc de répeter l'opération pour tous les DWORDs, et on a notre serial.

Pour "bigbang", j'obtient serial = CGUFOV06DISAWL36CATSYDH6DZFDG3W6CV4P5236GV3UDH6BIDO5TN6BXN3DJE6G3D3Q6A6

I. Codage du keygen


Pour le keygen, j'ai utilisé la librairie MIRACL que l'on peut trouver sur internet
Dans la doc miracl, ils proposent une implémentation de la méthode Rho de Pollard pour résoudre le logarithme discret (cf. index.c). C'est ceci que j'ai utilisé dans le keygen après quelques modifications.
Pour ce qui est du sha 256, la source C est dans l'archive
Voici le principal du keygen :

int main()
{

char name[48];
char hash[48];
char id[48];
char serial[48];
char serialf[100];

char eucl[8];
unsigned int itmp;

int i,np=NPRIMES;
long iter;
big pp[NPRIMES],rem[NPRIMES];
big m,n,Q,R,q,w,x,proot;
big big_hash,big_p,big_mod,big_mod2,big_un;
FILE *fout;
big_chinese bc;
miracl *mip=mirsys(100,0);

// pour le hash sha256
sha256_context ctx;
unsigned char sha256sum[32];

for(i=0;i<48;i++)
{

name[i]=0;
hash[i]=0;
serial[i]=0;
id[i]=0;

}

for(i=0;i<8;i++) eucl[i]=0;
itmp=0;
for(i=0;i<100;i++) serialf[i]=0;

printf("jB Crypto KeygenMe 2\nKeygen by Bigbang\n\nAttention : la generation du serial demande environ une minute.\nName:\t");
gets(name);

// on hash le nom
sha256_starts(&ctx);
sha256_update(&ctx,(uint8 *)name,strlen(name));
sha256_finish(&ctx,sha256sum);


pp[0]=mirvar(0);
pp[1]=mirvar(0);
pp[2]=mirvar(0);
pp[3]=mirvar(0);
pp[4]=mirvar(0);
pp[5]=mirvar(0);
pp[6]=mirvar(0);
pp[7]=mirvar(0);
pp[8]=mirvar(0);
proot=mirvar(0); // proot = a
q=mirvar(0);
Q=mirvar(0);
R=mirvar(0);
w=mirvar(0);
m=mirvar(0);
n=mirvar(0);
x=mirvar(0);
p=mirvar(0);
p1=mirvar(1);
order=mirvar(0);
lim1=mirvar(0);
lim2=mirvar(0);
big_hash=mirvar(0);
big_p=mirvar(0);
big_mod=mirvar(0);
big_mod2=mirvar(0);
big_un=mirvar(1);

// n-1 décomposé en produit de facteurs premiers
cinstr(pp[0],"2");
cinstr(pp[1],"2815877");
cinstr(pp[2],"29111609");
cinstr(pp[3],"886858327");
cinstr(pp[4],"7420154887");
cinstr(pp[5],"51016951651");
cinstr(pp[6],"7332402496481");
cinstr(pp[7],"535680266029");
cinstr(pp[8],"7332402471571");
cinstr(proot,"1168564702938616060860205625779134330139232618057968293581930542603891111661536226"); // proot = a
cinstr(big_p,"689219980367200448228233469551774431281832371213335848762696196467567412087213050");
cinstr(big_mod,"1585221117854722153076651463706576284202774603262818801054484035317780332416731107");
cinstr(big_mod2,"7332402496481");

// le hash se présente renversé dans le keygenme.
// on fait donc la même chose
__asm
{
xor ecx, ecx
_bswp:
mov eax, dword ptr [sha256sum+4*ecx]
bswap eax
mov dword ptr [sha256sum+4*ecx], eax

inc ecx
cmp ecx,8
jne short _bswp
}

strcpy(serial,sha256sum);

sprintf(hash,"%.8X%.8X%.8X%.8X%.8X%.8X%.8X%.8X",str2dword(sha256sum+0),
str2dword(sha256sum+4),
str2dword(sha256sum+8),
str2dword(sha256sum+12),
str2dword(sha256sum+16),
str2dword(sha256sum+20),
str2dword(sha256sum+24),
str2dword(sha256sum+28));

mip->IOBASE=16;
cinstr(big_hash,hash);
mip->IOBASE=10;

powmod(big_p,big_hash,big_mod,q); // q = big_p ^ big_hash (modulo big_mod)

for (i=0;i<np;i++) multiply(p1,pp[i],p1);
incr(p1,1,p);

subdiv(p,3,lim1);
premult(lim1,2,lim2);

for (i=0;i<NPRIMES;i++) rem[i]=mirvar(0);

crt_init(&bc,np,pp);

for (i=0;i<np;i++)
{ // accumulate solutions for each pp
copy(p1,w);
divide(w,pp[i],w);
powmod(q,w,p,Q);
powmod(proot,w,p,R);
copy(pp[i],order);
iter=rho(Q,R,m,n);
xgcd(m,order,w,w,w);
mad(w,n,n,order,order,rem[i]);
}
crt(&bc,rem,x); // apply Chinese remainder thereom

powmod(x,big_un,big_mod2,x);

mip->IOBASE=10;

cotstr(x,id); // on a l'id
crt_end(&bc);

// on s'occupe maintenant du serial
serial[32]=0;
serial[33]=0;
serial[34]=0;
serial[35]=0;
for(i=0;i<36;i++) serial[i] ^= cste[i];

__asm
{
lea eax,serial
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+4
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+8
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+12
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+16
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+20
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+24
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+28
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+32
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);

__asm
{
lea eax,serial+36
mov eax, DWORD PTR [eax]
mov itmp, eax
}
RemEucl(itmp,eucl);
strcat(serialf,eucl);


// affichage des résultats
printf("id:\t%s\n",id);
printf("serial=\t%s\n",serialf);

// écriture dans un fichier
fout=fopen("jB KeygenMe 2 - Serial by Bigbang.txt","w");
fputs("Autor: jB [Crypto KeygenMe 2]\nKeygenner: Bigbang\n\n",fout);
fputs("Name:\t",fout);fputs(name,fout);fputs("\n",fout);
fputs("ID:\t",fout);fputs(id,fout);fputs("\n",fout);
fputs("Serial:\t",fout);fputs(serialf,fout);
fclose(fout);

printf("\n\nSee jB KeygenMe 2 - Serial by Bigbang.txt\n\n");

system("pause");
return 0;

}


Remarque :

Dans un premier temps, pour convertir le hash à l'aide de sprintf, j'avais simplement mis %X%X%X%X%X%X%X%X au lieu de %.8X%.8X%.8X%.8X%.8X%.8X%.8X%.8X
C'était une grossière erreur et j'obtenais de mauvais résultats. Erreur car dans le cas où dans le hash une sous-partie commençait par un zéro, le big_hash était faux, et donc toute la suite était fausse
Ceci est maintenant corrigé, et tout est pour le mieux dans le meilleur des mondes :)



bigbang / 3604367769262 / CGUFOV06DISAWL36CATSYDH6DZFDG3W6CV4P5236GV3UDH6BIDO5TN6BXN3DJE6G3D3Q6A6


Greetz :

- Ma petite Charlotte
- Membres de FFF, de la ShmeitCorp
- Challengers du BigContest (http://bigcontest.securityhack.net)
- Tous ceux qui passent sur le CrFF
- elooo, jB, the Analyst, Neitsa, Tone, GBillou, Darus, SeVen, SynApsus et les autres :)
- Shad0w, Nelio, P41f0x, castlegirl, Tyrael, ex0d et vous bien sur !

Bigbang