mercredi 2 février 2011

[Reversing] Ge0's KeygenMe fast

Bonjour,

Ce soir nous allons étudier rapidement un keygen me "basique", c'est-à-dire sans aucunes obfuscation, anti-debugging ou autre "protections" particulières.

C'est un challenge du site Zenk-Sécurité, pour cette raison, je ne vais pas dévoiler mon keygen mais juste donner des pistes (fortement détaillées).
Seuls ceux qui auront résolu le challenge pourront avoir accès à mon keygen.
Ca forcera les "gens" à lire entièrement ce write-up et à taper dans le challenge un minimum ^^.

Le page du challenge se trouve : ici.

On nous dit que la page http://venom630.free.fr/keygenmefast/keygenmefast.php va nous envoyer un keygenme sous forme d'un fichier ELF (exécutable Linux donc).
Le pseudo dont il faut générer le sérial se trouve dans les entêtes HTTP:
X-Pseudo-Keygenmefast: Overclok (par exemple)
Et pour finir, nous devons envoyer notre solution concaténée au pseudo sous la forme d'un hash SHA1.
http://venom630.free.fr/keygenmefast/verifkeygenmefast.php?solution=sha1.

De ces informations, nous pouvons tirer plusieurs hypothèses:
- un binaire de base est utilisé
- certaines données vont changer

Pour vérifier ces hypothèses, c'est somme toute assez simple, on va faire usage de diff et hexdump comme ci-dessous:
$ diff -u <(hexdump -C ./keygenmefast1) <(hexdump -C ./keygenmefast2)
--- /dev/fd/63 2011-02-02 16:50:47.833079643 +0100
+++ /dev/fd/62 2011-02-02 16:50:47.833079643 +0100
@@ -91,25 +91,25 @@
 000005a0  00 00 bb 42 a0 04 08 01  c3 8a 13 8b 7c 24 08 81  |...B........|$..|
 000005b0  c7 05 00 00 00 8a 37 38  f2 0f 85 ba fe ff ff 68  |......78.......h|
 000005c0  83 a0 04 08 e8 a7 fd ff  ff 31 c0 c9 c3 55 89 e5  |.........1...U..|
-000005d0  8b 45 08 25 ff 00 00 00  34 49 25 3f 00 00 00 c9  |.E.%....4I%?....|
+000005d0  8b 45 08 25 ff 00 00 00  34 13 25 3f 00 00 00 c9  |.E.%....4.%?....|
 000005e0  c2 04 00 55 89 e5 81 ec  08 00 00 00 8b 4d 0c 8b  |...U.........M..|
 000005f0  75 08 01 ce 31 c0 e9 0b  00 00 00 4e 8a 16 81 e2  |u...1......N....|
 00000600  ff 00 00 00 01 d0 49 81  f9 ff ff ff ff 75 ec 34  |......I......u.4|
-00000610  08 25 3f 00 00 00 c9 c2  08 00 55 89 e5 b8 01 00  |.%?.......U.....|
+00000610  84 25 3f 00 00 00 c9 c2  08 00 55 89 e5 b8 01 00  |.%?.......U.....|
 00000620  00 00 8b 75 08 8b 4d 0c  e9 0f 00 00 00 31 db 8a  |...u..M......1..|
 00000630  1e 80 e3 ff f7 e3 25 ff  00 00 00 46 49 81 f9 ff  |......%....FI...|
-00000640  ff ff ff 75 e8 34 7e 25  3f 00 00 00 c9 c2 08 00  |...u.4~%?.......|
+00000640  ff ff ff 75 e8 34 2f 25  3f 00 00 00 c9 c2 08 00  |...u.4/%?.......|
 00000650  55 89 e5 81 ec 04 00 00  00 8b 75 08 8a 06 8b 4d  |U.........u....M|
 00000660  0c e9 09 00 00 00 46 8a  1e 38 c3 76 02 88 d8 49  |......F..8.v...I|
-00000670  81 f9 ff ff ff ff 75 ee  34 a7 50 e8 b0 fc ff ff  |......u.4.P.....|
+00000670  81 f9 ff ff ff ff 75 ee  34 ed 50 e8 b0 fc ff ff  |......u.4.P.....|
 00000680  e8 fb fc ff ff 25 3f 00  00 00 c9 c2 08 00 55 89  |.....%?.......U.|
 00000690  e5 31 db 8b 75 08 8b 4d  0c 49 e9 1d 00 00 00 31  |.1..u..M.I.....1|
 000006a0  d2 8a 16 51 53 68 02 00  00 00 52 e8 58 00 00 00  |...QSh....R.X...|
 000006b0  5b 59 01 c3 81 e3 ff 00  00 00 46 49 81 f9 ff ff  |[Y........FI....|
-000006c0  ff ff 75 db 89 d8 34 46  25 3f 00 00 00 c9 c2 08  |..u...4F%?......|
+000006c0  ff ff 75 db 89 d8 34 77  25 3f 00 00 00 c9 c2 08  |..u...4w%?......|
 000006d0  00 55 89 e5 31 c0 8b 75  08 81 fe 00 00 00 00 74  |.U..1..u.......t|
 000006e0  1e e8 9a fc ff ff 8b 4d  08 e9 07 00 00 00 51 e8  |.......M......Q.|
-000006f0  8c fc ff ff 59 49 75 f6  25 ff 00 00 00 34 39 25  |....YIu.%....49%|
+000006f0  8c fc ff ff 59 49 75 f6  25 ff 00 00 00 34 57 25  |....YIu.%....4W%|
 00000700  3f 00 00 00 c9 c2 04 00  55 89 e5 8b 4d 0c b8 01  |?.......U...M...|
 00000710  00 00 00 81 f9 00 00 00  00 74 0f 8b 5d 08 f7 e3  |.........t..]...|
 00000720  e9 02 00 00 00 f7 e3 49  75 fb c9 c2 08 00 90 90  |.......Iu.......|
@@ -145,11 +145,11 @@
 00001010  76 83 04 08 86 83 04 08  00 00 00 00 00 00 00 00  |v...............|
 00001020  55 74 69 6c 69 73 61 74  69 6f 6e 20 3a 20 25 73  |Utilisation : %s|
 00001030  20 3c 70 73 65 75 64 6f  3e 20 3c 63 6c 65 66 3e  | &ltpseudo> &ltclef>|
-00001040  0a 00 52 76 6e 64 35 56  78 4b 4e 44 45 6b 70 5a  |..Rvnd5VxKNDEkpZ|
-00001050  2b 31 67 71 53 54 2d 75  62 73 72 68 6d 43 42 33  |+1gqST-ubsrhmCB3|
-00001060  63 61 30 38 6c 37 55 6f  46 32 66 69 50 79 77 4d  |ca08l7UoF2fiPywM|
-00001070  6a 7a 57 59 41 58 48 49  51 34 4c 47 65 74 36 39  |jzWYAXHIQ4LGet69|
-00001080  4a 4f 00 42 72 61 76 6f  20 21 21 0a 00 00 00 00  |JO.Bravo !!.....|
+00001040  0a 00 31 68 69 2b 59 42  65 56 61 4e 4c 33 44 4a  |..1hi+YBeVaNL3DJ|
+00001050  4d 45 58 7a 32 6c 74 50  5a 2d 78 6d 34 6f 47 53  |MEXz2ltPZ-xm4oGS|
+00001060  75 76 71 77 49 55 66 79  43 36 6a 70 6b 62 4b 54  |uvqwIUfyC6jpkbKT|
+00001070  35 6e 63 4f 57 73 46 39  72 67 38 64 52 48 37 30  |5ncOWsF9rg8dRH70|
+00001080  51 41 00 42 72 61 76 6f  20 21 21 0a 00 00 00 00  |QA.Bravo !!.....|
 00001090  47 43 43 3a 20 28 55 62  75 6e 74 75 2f 4c 69 6e  |GCC: (Ubuntu/Lin|
 000010a0  61 72 6f 20 34 2e 34 2e  34 2d 31 34 75 62 75 6e  |aro 4.4.4-14ubun|
 000010b0  74 75 35 29 20 34 2e 34  2e 35 00 00 2e 73 68 73  |tu5) 4.4.5...shs|

Ainsi on remarque qu'on a 6 octets qui changent et une chaîne de 64 caractères aussi qui est générée. Ca n'a pas l'air d'être de la base64 (ne finit pas par des =).

On récupère donc les différents offsets de ces diffs :
- offsets octets: 0x5d9, 0x610, 0x646, 0x679, 0x6c7, 0x6fe
- offset string: 0x1042
Et les valeurs correspondantes:
- octets: 0x13, 0x84, 0x2f, 0xed, 0x77, 0x57
- string:1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC6jpkbKT5ncOWsF9rg8dRH70QAs
On verra à quoi ils servent par la suite.

Analysons la routine principale:

.text:08048450 ; =============== S U B R O U T I N E =======================================
.text:08048450
.text:08048450 ; Attributes: bp-based frame
.text:08048450
.text:08048450 main            proc near               ; DATA XREF: start+17 o
.text:08048450
.text:08048450 argc            = dword ptr  8
.text:08048450 argv            = dword ptr  0Ch
.text:08048450
.text:08048450                 push    ebp
.text:08048451                 mov     ebp, esp
.text:08048453                 mov     eax, [ebp+argc]
.text:08048456                 cmp     eax, 3
.text:0804845B                 jnb     short loc_8048485
.text:0804845D                 mov     esi, [ebp+argv]
.text:08048460                 mov     edi, [esi]
.text:08048462                 sub     esp, 8
.text:08048468                 mov     esi, offset format ; "Utilisation : %s  \n"
.text:0804846D                 mov     [esp], esi      ; s
.text:08048470                 mov     [esp+4], edi
.text:08048474                 call    _printf
.text:08048479
.text:08048479 main_exit:                              ; CODE XREF: main+6F j
.text:08048479                                         ; main+9B j ...
.text:08048479                 mov     eax, 1
.text:0804847E                 mov     ebx, 1          ; status
.text:08048483                 int     80h             ; LINUX - sys_exit
.text:08048485 ; ---------------------------------------------------------------------------
.text:08048485
.text:08048485 loc_8048485:                            ; CODE XREF: main+B j
.text:08048485                 sub     esp, 0Ch
.text:0804848B                 mov     ebx, [ebp+argv]
.text:0804848E                 mov     edx, ebx
.text:08048490                 add     edx, 4
.text:08048496                 mov     edx, [edx]
.text:08048498                 mov     [esp+4], edx    ; argv[1] <=> username
.text:0804849C                 mov     edx, ebx
.text:0804849E                 add     edx, 8
.text:080484A4                 mov     edx, [edx]
.text:080484A6                 mov     [esp+8], edx
.text:080484AA                 mov     esi, [esp+8]    ; argv[2] <=> usersserial
.text:080484AE                 push    esi             ; userserial
.text:080484AF                 call    _strlen
.text:080484B4                 add     esp, 4
.text:080484BA                 cmp     eax, 6
.text:080484BF                 jnz     short main_exit
.text:080484C1                 mov     edi, [esp+4]
.text:080484C5                 push    edi             ; username
.text:080484C6                 call    _strlen
.text:080484CB                 add     esp, 4
.text:080484D1                 mov     [esp], eax      ; szUsername
.text:080484D4                 push    eax
.text:080484D5                 call    get_offset0
.text:080484DA                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:080484DF                 add     ebx, eax
.text:080484E1                 mov     dl, [ebx]
.text:080484E3                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:080484E7                 mov     dh, [edi]
.text:080484E9                 cmp     dl, dh
.text:080484EB                 jnz     short main_exit
.text:080484ED                 mov     edi, [esp]      ; szUsername
.text:080484F0                 push    edi
.text:080484F1                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:080484F5                 push    edi
.text:080484F6                 call    get_offset1
.text:080484FB                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048500                 add     ebx, eax
.text:08048502                 mov     dl, [ebx]
.text:08048504                 mov     edi, [esp+8]
.text:08048508                 inc     edi
.text:08048509                 mov     dh, [edi]
.text:0804850B                 cmp     dl, dh
.text:0804850D                 jnz     main_exit
.text:08048513                 mov     edi, [esp]      ; szUsername
.text:08048516                 push    edi
.text:08048517                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:0804851B                 push    edi
.text:0804851C                 call    get_offset2
.text:08048521                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048526                 add     ebx, eax
.text:08048528                 mov     dl, [ebx]
.text:0804852A                 mov     edi, [esp+8]
.text:0804852E                 add     edi, 2
.text:08048534                 mov     dh, [edi]
.text:08048536                 cmp     dl, dh
.text:08048538                 jnz     main_exit
.text:0804853E                 mov     edi, [esp]      ; szUsername
.text:08048541                 push    edi
.text:08048542                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048546                 push    edi
.text:08048547                 call    get_offset3
.text:0804854C                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:08048551                 add     ebx, eax
.text:08048553                 mov     dl, [ebx]
.text:08048555                 mov     edi, [esp+8]
.text:08048559                 add     edi, 3
.text:0804855F                 mov     dh, [edi]
.text:08048561                 cmp     dl, dh
.text:08048563                 jnz     main_exit
.text:08048569                 mov     edi, [esp]      ; szUsername
.text:0804856C                 push    edi
.text:0804856D                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048571                 push    edi
.text:08048572                 call    get_offset4
.text:08048577                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:0804857C                 add     ebx, eax
.text:0804857E                 mov     dl, [ebx]
.text:08048580                 mov     edi, [esp+8]    ; argv[2] <=> userserial
.text:08048584                 add     edi, 4
.text:0804858A                 mov     dh, [edi]
.text:0804858C                 cmp     dl, dh
.text:0804858E                 jnz     main_exit
.text:08048594                 mov     edi, [esp+4]    ; argv[1] <=> username
.text:08048598                 xor     edx, edx
.text:0804859A                 mov     dl, [edi]
.text:0804859C                 push    edx
.text:0804859D                 call    get_offset5
.text:080485A2                 mov     ebx, offset hash ; "1hi+YBeVaNL3DJMEXz2ltPZ-xm4oGSuvqwIUfyC"...
.text:080485A7                 add     ebx, eax
.text:080485A9                 mov     dl, [ebx]
.text:080485AB                 mov     edi, [esp+8]
.text:080485AF                 add     edi, 5
.text:080485B5                 mov     dh, [edi]
.text:080485B7                 cmp     dl, dh
.text:080485B9                 jnz     main_exit
.text:080485BF                 push    offset aBravo   ; "Bravo !!\n"
.text:080485C4                 call    _printf
.text:080485C9                 xor     eax, eax
.text:080485CB                 leave
.text:080485CC                 retn
.text:080485CC main            endp ; sp-analysis failed

On remarque que des blocs ont le même pattern:
- appel d'une fonction qui calcule un dword
- ajout de ce dword à hash
- comparaison du caractère calculé et du caractère entré
    -> si incorrect on sort du keygen
    -> si correct on continue
- lorsqu'on arrive à la fin, on obtient un "Bravo !!\n" ^^

Maintenant, en analysant chaque fonction de calcul d'offset, on remarque les choses suivantes:
get_offset0:
.text:080485D8                 xor     al, 13h
.text:080485DA                 and     eax, 3Fh
.text:080485DF                 leave
.text:080485E0                 retn    4
.text:080485E0 get_offset0     endp
get_offset1:
.text:0804860F                 xor     al, 84h
.text:08048611                 and     eax, 3Fh
.text:08048616                 leave
.text:08048617                 retn    8
.text:08048617 get_offset1     endp
get_offset2:
.text:08048645                 xor     al, 2Fh
.text:08048647                 and     eax, 3Fh
.text:0804864C                 leave
.text:0804864D                 retn    8
.text:0804864D get_offset2     endp
get_offset3:
.text:08048678                 xor     al, 0EDh
.text:0804867A                 push    eax             ; seed
.text:0804867B                 call    _srand
.text:08048680                 call    _rand
.text:08048685                 and     eax, 3Fh
.text:0804868A                 leave
.text:0804868B                 retn    8
.text:0804868B get_offset3     endp
get_offset4:
.text:080486C6                 xor     al, 77h
.text:080486C8                 and     eax, 3Fh
.text:080486CD                 leave
.text:080486CE                 retn    8
.text:080486CE get_offset4     endp
get_offset5:
.text:080486FF                 and     eax, 3Fh
.text:08048704                 leave
.text:08048705                 retn    4
.text:08048705 get_offset5     endp
Vous vous souvenez de nos 6 octets "changeant"?
- octets: 0x13, 0x84, 0x2f, 0xed, 0x77, 0x57
Eh oui, ce sont les "clés" utilisées par les fonctions de calcul d'offset.

En fait, le keygenme va faire les choses suivantes:
- vérification de la longueur de l'username
- vérification de la longueur du serial
    -> bye bye si pas égal à 6
- calculs des offsets en utilisant les 6 bytes
- calcul du sérial
- comparaison entre le serial rentré et le sérial calculé
- bravo ou exit

On se rend compte que pour calculer les différents offsets, le serial est utilisé.
Je suis parti dans l'idée d'utiliser l'username pour générer le serial.

Et voilà, reste plus qu'à coder un beau keygen ;).

Have fun,

m_101

- Mon keygen: Keygen

2 commentaires :

  1. Hello,

    Il y a un password pr ton keygen.zip ??

    RépondreSupprimer
  2. Faut croire que oui :).
    Je me souviens plus du mdp par contre.
    Ca doit être le mot de passe du challenge ;).

    RépondreSupprimer