[PRGN crackme by bart/xt ]
par Amenesia 
Analyse du code:

Un breakpoint sur GetDlgItemTextA...

_text:004013A1                 push    100h            ; nMaxCount
_text:004013A6                 push    offset Nom      ; lpString
_text:004013AB                 push    65h             ; nIDDlgItem
_text:004013AD                 push    edi             ; hDlg
_text:004013AE                 call    GetDlgItemTextA

_text:004013B3                 mov     ds:NameSize, eax
_text:004013B8                 push    100h            ; nMaxCount
_text:004013BD                 push    offset Serial   ; lpString
_text:004013C2                 push    67h             ; nIDDlgItem
_text:004013C4                 push    edi             ; hDlg
_text:004013C5                 call    GetDlgItemTextA
_text:004013CA                 mov     ds:SizeSerial, eax
_text:004013CF                 mov     eax, ds:NameSize
_text:004013D4                 or      eax, eax
_text:004013D6                 jz      ErreurSerial
_text:004013DC                 cmp     eax, 40h
_text:004013DF                 jnb     ErreurSerial
_text:004013E5                 cmp     ds:SizeSerial, 0
_text:004013EC                 jz      ErreurSerial

Le serial fait donc moins de 64 caracteres...


... et il n'est composé que des caracteres [ 0 - 9 ] [ A - F ] ( hexadecimal ) :

_text:004013FB A_F: 
_text:004013FB                 mov     eax, ds:Flag
_text:00401400                 movzx   eax, ds:Serial[eax]
_text:00401408                 mov     ds:lettres, eax
_text:0040140D                 cmp     eax, 41h
_text:00401410                 jb      short Chiffres
_text:00401412                 cmp     eax, 46h
_text:00401415                 jbe     short Suite
_text:00401417 
_text:00401417 Chiffres: 
_text:00401417                 mov     eax, ds:lettres
_text:0040141C                 cmp     eax, 30h
_text:0040141F                 jb      short ErreurLettre
_text:00401421                 cmp     eax, 39h
_text:00401424                 jbe     short Suite
_text:00401426 
_text:00401426 ErreurLettre: 
_text:00401426                 push    offset aInvalidKey ; lpString
_text:0040142B                 push    67h             ; nIDDlgItem
_text:0040142D                 push    edi             ; hDlg
_text:0040142E                 call    SetDlgItemTextA
_text:00401433                 jmp     loc_0_401593
_text:00401438 
_text:00401438 Suite: 
_text:00401438                 inc     ds:Flag
_text:0040143E 
_text:0040143E loc_0_40143E: 
_text:0040143E                 mov     eax, ds:SizeSerial
_text:00401443                 cmp     ds:Flag, eax
_text:00401449                 jb      short A_F


Puis le programme initialise des bignums (cf la doc de Miracl )

_text:00401461                 push    0
_text:00401463                 call    mirvar
_text:00401468                 mov     ds:Big1, eax
_text:0040146D                 push    0
_text:0040146F                 call    mirvar
_text:00401474                 mov     ds:Big2, eax
_text:00401479                 push    0
_text:0040147B                 call    mirvar
_text:00401480                 mov     ds:Big3, eax
_text:00401485                 push    0
_text:00401487                 call    mirvar
_text:0040148C                 mov     ds:Big4, eax
_text:00401491                 push    0
_text:00401493                 call    mirvar


et leur affectent la valeur du serial, du nom ( valeur ascii des lettres ) et de la Magic Key...

_text:004014A0                 mov     dword ptr [eax+238h], 10h ; base 16
_text:004014AA                 push    ds:Big5
_text:004014B0                 push    offset Nom
_text:004014B5                 push    ds:NameSize
_text:004014BB                 call    bytes_to_big

_text:004014C0                 push    offset Serial
_text:004014C5                push    ds:Big4
_text:004014CB                 call    sub_0_4065BC

_text:004014D0                 push    offset MagicKey
_text:004014D5                 push    ds:Big2
_text:004014DB                 call    sub_0_4065BC


Le Big3 recoit la valeur 10001h.. il ya donc de forte chance que le programme utilise RSA...

_text:004014E0                 push    ds:Big3
_text:004014E6                 push    10001h
_text:004014EB                 call    cinstr


Et effectivement on reconnait, quelques lignes plus bas, la signature de la fonction powmod (cf la doc de la librarie Miracl )

_text:004014F0                 push    ds:Big1
_text:004014F6                 push    ds:Big2    ; Magic Key
_text:004014FC                 push    ds:Big3    ; 10001h
_text:00401502                 push    ds:Big4    ; Serial
_text:00401508                 call    powmod


Le resultat est ensuite comparé au Nom... 

_text:0040150D                 push    ds:Big1
_text:00401513                 push    ds:Big5
_text:00401519                 call    compare

Donc pour trouver un serial valide il "suffit" de calculer le RSA du nom... ;)
Le petit probleme est qu'ici la longueur du modulo ( la Magic Key)  depasse les 500 bits... ;p 


D'ou vient la Magic Key ? En posant un "bpx SetDlgItemTextA" on tombe ici, or quelques lignes plus haut le programme semble utiliser des Bignums....

_text:0040134E                 push    offset MagicKey
_text:00401353                 push    ds:Big2
_text:00401359                 call    BigToString
_text:0040135E                 push    offset MagicKey ; lpString
_text:00401363                 push    66h             ; nIDDlgItem
_text:00401365                 push    [ebp+hDlg]      ; hDlg
_text:00401368                 call    SetDlgItemTextA



Donc tout semble indiquer que la valeur de la Magic Key soit crée au cours de l'execution de ces quelques lignes:

_text:004012E7                 mov     dword ptr [edi+238h], 10h ; base
_text:004012F1                 push    0
_text:004012F3                 call    mirvar
_text:004012F8                 mov     ds:Big1g, eax
_text:004012FD                 push    0
_text:004012FF                 call    mirvar
_text:00401304                 mov     ds:Big2g, eax
_text:00401309                 push    0
_text:0040130B                 call    mirvar
_text:00401310                 mov     ds:Big2, eax

_text:00401315                 push    [ebp+arg_4]
_text:00401318                 call    IniSeed
_text:0040131D                 push    ds:Big1g
_text:00401323                 push    20h
_text:00401325                 call    Rand
_text:0040132A                 push    ds:Big2g
_text:00401330                 push    20h
_text:00401332                 call    Rand
_text:00401337                 push    ds:Big2
_text:0040133D                 push    ds:Big2g
_text:00401343                 push    ds:Big1g
_text:00401349                 call    multiply

Deux bignums recoivent une valeur aleatoire puis sont multipliés. Le resultat de la multiplication est la Magic Key...
Comment deviner que le call ligne 00401349 est une multiplication en observant les parametres d'entrées / sorties et en etant un peu logique: le modulo intervenant dans le cryptage RSA est le produit de 2 grands nombres premiers... 


Effectivment si on se penche un peu sur Rand : que la valeur de Seed est utilisée pour generer une suite pseudo aleatoire qui est par la suite affecté a un des 2 bignums servant a generer la Magic Key... Or comme les 2 bignums doivent etre premiers, il y a de forts chances que le call de la ligne 004012C8 soit en fait un appel à la fonction nxPrime...

_text:00401297 Rand            proc near 
_text:00401297 
_text:00401297 Size            = dword ptr  8
_text:00401297 Big             = dword ptr  0Ch
_text:00401297 
_text:00401297                 push    ebp
_text:00401298                 mov     ebp, esp
_text:0040129A                 push    ebx
_text:0040129B                 push    esi
_text:0040129C                 push    edi
_text:0040129D                 mov     esi, [ebp+Size]
_text:004012A0                 mov     ebx, [ebp+Big]
_text:004012A3                 xor     edi, edi
_text:004012A5                 jmp     short RandGen
_text:004012A7 LoopGenRand: 
_text:004012A7                 call    sub_0_40126A
_text:004012AC                 mov     edx, eax
_text:004012AE                 mov     ds:RandNumber[edi], dl
_text:004012B5                 inc     edi
_text:004012B6 
_text:004012B6 RandGen: 
_text:004012B6                 cmp     edi, esi
_text:004012B8                 jb      short LoopGenRand
_text:004012BA                 push    ebx
_text:004012BB                 push    offset RandNumber
_text:004012C0                 push    esi
_text:004012C1                 call    bytes_to_big
_text:004012C6                 push    ebx
_text:004012C7                 push    ebx
_text:004012C8                 call    sub_0_4091B8
_text:004012CD                 add     esp, 14h
_text:004012D0                 pop     edi
_text:004012D1                 pop     esi
_text:004012D2                 pop     ebx
_text:004012D3                 pop     ebp
_text:004012D4                 retn
_text:004012D4 Rand            endp

_text:0040126A sub_0_40126A    proc near 
_text:0040126A 
_text:0040126A var_4           = dword ptr -4
_text:0040126A 
_text:0040126A                 push    ebp
_text:0040126B                 mov     ebp, esp
_text:0040126D                 push    ecx
_text:0040126E                 mov     eax, 14EF2A01h
_text:00401273                 mul     ds:Seed
_text:00401279                 mov     [ebp+var_4], eax
_text:0040127C                 mov     ds:Seed, eax
_text:00401281                 add     ds:Seed, 3F5743FAh
_text:0040128B                 mov     eax, ds:Seed
_text:00401290                 xor     eax, 42398544h
_text:00401295                 leave
_text:00401296                 retn
_text:00401296 sub_0_40126A    endp

Apres verification il s'agit bien de la fonction nxPrime, on est sur la bonne voie ;)


 Comment fait le programme pour créer des nombres (pseudo-)aleatoires ? 
En observant la fonction IniSeed on se rend compte qu'a chaque execution Seed recoit une nouvelle valeur ( qui est  à l'origine de la generation des bignums aleatoires )

_text:00401250 IniSeed         proc near 
_text:00401250 
_text:00401250                 mov     ecx, [esp+arg_0]      ; <= GetTickCount 
_text:00401254                 or      ecx, ecx
_text:00401256                 jnz     short loc_0_40125D
_text:00401258                 mov     ecx, 0FADEh
_text:0040125D 
_text:0040125D loc_0_40125D: 
_text:0040125D                 mov     eax, ecx
_text:0040125F                 and     eax, 0FFFFh         ; <== La faille !!! ;p
_text:00401264                 mov     ds:Seed, eax
_text:00401269                 retn
_text:00401269 IniSeed         endp
_text:00401269 
 

Autrement dit tout depend de cette valeur puisque pour une valeur donnés qu'une seule suite aleatoire existe... or cette valeur  est comprise entre 0 et FFFFh. Autrement dit rien ne nous empeche de generer les 65536 couples de Bignums possibles et de verifier si ce sont eux qui ont servit a creer la Magic Key...  Certes ca peut etre un peu long ( moins de deux heures dans le pire des cas ) mais c'est quand meme beaucoup plus raisonnable que de tenter de factoriser directement la Magic Key... ;)

KeyGen:

Une fois le couple trouvé on n'a plus qu'a calculer la clef privee correspondante et calculer le serial... 



J'espere n'avoir embrouillé personne... ;)

Bonne continuation ;)
 

 
-