[french] - Unix Keygenning

Download | Vote Up (1) | Vote Down (0)

Unix Keygenning

Fermons la fenêtre, préparons-nous, et partons vers la banquise.
Là-bas, pas d'outils graphique, pas de belles images. Uniquement de la console. Encore et toujours.

Bon, maintenant que les fainéants et les pleutres ont disparus, nous allons pouvoir commencer.
Pour suivre ce tutoriel, il vous sera nécessaire de posséder quelques acquis en programmation ASM. Même si je vais m'efforcer, au comble du masochisme, de vous décrire point par point chaque instruction, ces acquis vous permettront de combler mes lacunes pédagogiques.

Le keygenning ayant déjà été définis dans l'article sur le keygenning windows, je préfère vous y renvoyer, plutôt que d'en rédiger une moi-même. D'autant plus que la définition me convient très bien (et que je suis fainéant).

1°) Les outils

Dans ce tutoriel nous utiliserons des outils basiques, qui font sûrement déjà partis de votre panoplie, puisqu'ils sont de base installés sur la plupart des distributions GNU/Linux.

Objdump, un desassembleur faisant partie de la sympathique famille des GNU bin utils (qui comprend également as, ld, strings, strip, readelf, pour ne citer qu'eux). Une suite bien utile dans sa totalité, pour l'analyse de fichier binaire.
Gdb, le Gnu DeBugger,un debugger plutôt complet.
Nasm (ou as) & ld, assembleur & linker, pour la création du keygen. Ou le langage de programmation de votre choix.

2°) À l'attaque

Le keygenMe est disponible ici : http://bayfiles.com/file/1kkJ/7Brju2/keygenMe

Avant de partir à toute allure dans des travers techniques et incompréhensibles, pourquoi ne pas lancer le joujou hors debugger, histoire de voir de quoi il retourne ?

Bon, étant donné la masse d'entre vous qui trouvent mon idée géniale, voyons ce que ça donne ;

$ ./KeygenMe
--------------------
KeygenMe
By kallimero
--------------------
Pseudo : kallimero
Serial : trolololo
Registration failed.

Évidemment, ça n'a pas marché. Le contraire aurait été vraiment étonnant. Je ne suis donc pas cocu. CQFD.

Examinons le fichier via la commande file :

$ file KeygenMe
KeygenMe: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, stripped

On a déjà pas mal d'infos utiles ;
Le fichier est linké statiquement. Le linker étant le lien entre le fichier objet et le fichier exécutable. Pour faire court, le fichier objet est un fichier intermédiaire, utilisé lors de la compilation. Il contient du code machine non-exécutable, qui nécessite l'édition des liens (d'où le nom de linker). Quand le linkage est statique, le fichier exécutable n'est pas lié à une bibliothèque externe, il est lié tout seul. Quand il est dynamique, il est lié à une bibliothèque, et ce lien est mis en œuvre au lancement du fichier.

Il est strippé. C'est à dire que toute les entêtes non nécessaires pour le bon déroulement de programme ont été supprimées.

Allez, je ne vous fait pas baver plus longtemps. D'autant que votre salive vous servira pour crier de douleur un peu plus tard.

A) Désassemblons

On est parti pour se pencher sur le code assembleur. On va commencer par regarder le code gentillement désassemblé par objdump, afin de comprendre le fonctionnement intrinsèque et d'isoler la routine de génération du keygen. Le programme étant linké statiquement, cela ne devrai pas trop nous poser de soucis.

Petite parenthèse avant de découvrir le magnifique code, sur les syscalls.
De manière shématique, un syscall est un appel au kernel, pour qu'ill effectue une certaine tâche, en fonction de paramètres (arguments). Un syscall à toujours la même structure. Sous système UNIX :

Numéro du syscall dans eax
1er argument dans ebx
2nd argument dans ecx
3eme argument dans edx
possible 4, 5, 6... argument(s)
Appel au kernel (kernel interrupt) int $0x80

Le numéro de syscall correspond à la fonction que l'on souhaite effectuer.
Vous retrouverez la correspondance entre fonction et numéro ici : http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html
Ou dans le fichier /usr/include/asm/unistd.h

/!\ Attention objdump utilise par défaut la syntaxe AT&T. Si vous préférez la syntaxe intel (utilisée notamment par nasm et masm), vous pouvez ajouter l'option -M intel.

On utilisera l'option -d pour indiquer le fichier à désassembler, utilisez l'option -H pour connaître la foultitude d'options intéressantes d'objdump.

Je vous montre directement l'output commenté par mes soins.

 1
 2$ objdump -d KeygenMe
 3
 4keygenMe:     file format elf32-i386
 5
 6
 7Disassembly of section .text:
 8
 908048080 <.text>:
10
11; Premier syscall, pour afficher « KeygenMe By kallimero »    
12 8048080:        b8 04 00 00 00               mov    $0x4,%eax
13 8048085:        bb 01 00 00 00               mov    $0x1,%ebx
14 804808a:        b9 70 91 04 08               mov    $0x8049170,%ecx
15 804808f:        ba 56 00 00 00               mov    $0x56,%edx
16 8048094:        cd 80                        int    $0x80
17
18; Second syscal pour afficher « pseudo : »
19 8048096:        b8 04 00 00 00               mov    $0x4,%eax
20 804809b:        bb 01 00 00 00               mov    $0x1,%ebx
21 80480a0:        b9 c7 91 04 08               mov    $0x80491c7,%ecx
22 80480a5:        ba 09 00 00 00               mov    $0x9,%edx
23 80480aa:        cd 80                        int    $0x80
24
25; 3eme syscall pour enregistrer le pseudo dans une variable
26 80480ac:        b8 03 00 00 00               mov    $0x3,%eax
27 80480b1:        bb 00 00 00 00               mov    $0x0,%ebx
28 80480b6:        b9 14 92 04 08               mov    $0x8049214,%ecx
29 80480bb:        ba 0f 00 00 00               mov    $0xf,%edx
30 80480c0:        cd 80                        int    $0x80
31
32; 4eme syscall pour afficher « serial : »
33 80480c2:        b8 04 00 00 00               mov    $0x4,%eax
34 80480c7:        bb 01 00 00 00               mov    $0x1,%ebx
35 80480cc:        b9 d1 91 04 08               mov    $0x80491d1,%ecx
36 80480d1:        ba 09 00 00 00               mov    $0x9,%edx
37 80480d6:        cd 80                        int    $0x80
38
39; 5eme syscall pour enregistrer le serial dans une variable
40 80480d8:        b8 03 00 00 00               mov    $0x3,%eax
41 80480dd:        bb 00 00 00 00               mov    $0x0,%ebx
42 80480e2:        b9 34 92 04 08               mov    $0x8049234,%ecx
43 80480e7:        ba 0f 00 00 00               mov    $0xf,%edx
44 80480ec:        cd 80                        int    $0x80
45
46; Routine obscure que nous allons élucider
47 80480ee:        b8 14 92 04 08               mov    $0x8049214,%eax
48 80480f3:        ba 00 00 00 00               mov    $0x0,%edx
49 80480f8:        42                           inc    %edx
50 80480f9:        80 3c 10 00                  cmpb   $0x0,(%eax,%edx,1)
51 80480fd:        75 f9                        jne    0x80480f8
52 80480ff:        bb 00 00 00 00               mov    $0x0,%ebx
53 8048104:        81 ea 01 00 00 00            sub    $0x1,%edx
54 804810a:        01 93 14 92 04 08            add    %edx,0x8049214(%ebx)
55 8048110:        43                           inc    %ebx
56 8048111:        39 d3                        cmp    %edx,%ebx
57 8048113:        75 f5                        jne    0x804810a
58
59
60; Routine de comparaison
61 8048115:        81 c2 01 00 00 00            add    $0x1,%edx
62 804811b:        89 d1                        mov    %edx,%ecx
63 804811d:        be 14 92 04 08               mov    $0x8049214,%esi
64 8048122:        bf 34 92 04 08               mov    $0x8049234,%edi
65 8048127:        f3 a6                        repz cmpsb %es:(%edi),%ds:(%esi)
66
67 8048129:        74 22                        je     0x804814d
68 ; Si la comparaison est bonne, au saute à  0x804814d
69
70; Syscall pour afficher le badBoy
71 804812b:        b8 04 00 00 00               mov    $0x4,%eax
72 8048130:        bb 01 00 00 00               mov    $0x1,%ebx
73 8048135:        b9 fc 91 04 08               mov    $0x80491fc,%ecx
74 804813a:        ba 16 00 00 00               mov    $0x16,%edx
75 804813f:        cd 80                        int    $0x80
76
77; Syscall exit
78 8048141:        b8 01 00 00 00               mov    $0x1,%eax
79 8048146:        bb 00 00 00 00               mov    $0x0,%ebx
80 804814b:        cd 80                        int    $0x80
81
82; Syscall pour afficher le GoodBoy
83 804814d:        b8 04 00 00 00               mov    $0x4,%eax
84 8048152:        bb 01 00 00 00               mov    $0x1,%ebx
85 8048157:        b9 db 91 04 08               mov    $0x80491db,%ecx
86 804815c:        ba 21 00 00 00               mov    $0x21,%edx
87 8048161:        cd 80                        int    $0x80
88
89; Syscall exit
90 8048163:        b8 01 00 00 00               mov    $0x1,%eax
91 8048168:        bb 00 00 00 00               mov    $0x0,%ebx
92 804816d:        cd 80                        int    $0x80

Je ne vous avait pas menti ; le code est principalement composé de syscall. Ceux utilisés majoritairement sont :
sys_write. 4 dans eax, et 1 dans ebx pour stdout, pour écrire du texte,
sys_read, 4 dans eax, et 0 dans ebx pour stdin, pour lire du texte,
sys_exit, 1 dans eax, et un nombre dans ebx qui représente le code d'erreur. 0 signifiant qu'il n'y as pas eu d'erreur.

Stdout est le flux de sortie, soit votre écran, et stdin le flux d'entrée, soit votre clavier.

Mêlé entre les vils et rugueux appels au système, nous pouvons distinguer un sombre morceau de code, qui s'apparenterait à une routine de génération de mot de passe.
C'est à ce moment que des connaissances en assembleur sont de mises, car le keygenning, c'est avant tout de longues nuits à jongler entre documentation et langage d'assemblage, entre café et aspirine, entre jouissances profondes et crises de nerfs.

Voyons ensemble de plus près la désormais fameuse portion de bouillie asm :

 1
 2 80480ee:        b8 14 92 04 08               mov    $0x8049214,%eax
 3 ; Met le pseudo entrer dans eax (voir le 3eme syscall)
 4 80480f3:        ba 00 00 00 00               mov    $0x0,%edx
 5 ; Met 0 dans edx, sûrement pour un compteur
 6 80480f8:        42                           inc    %edx
 7 ; incrémente ebx (edx = edx +1)
 8 80480f9:        80 3c 10 00                  cmpb   $0x0,(%eax,%edx,1)
 9 Compare le byte numéro edx du pseudo avec 0 (byte de fin d'une chaîne)
10 80480fd:        75 f9                        jne    0x80480f8
11 ; Si ce n'est pas un 0, on recommence

;Finalité de cette première partie : eax contient le pseudo, edx contient le nombre de byte du pseudo

 1
 2 80480ff:        bb 00 00 00 00               mov    $0x0,%ebx
 3 ; On met 0 dans ebx, pour un compteur
 4 8048104:        81 ea 01 00 00 00            sub    $0x1,%edx
 5 ; on décrément edx qui contient la taille du pseudo
 6 804810a:        01 93 14 92 04 08            add    %edx,0x8049214(%ebx)
 7 ; On ajoute edx au byte numéro ebx du pseudo  
 8 8048110:        43                           inc    %ebx
 9 ; on incrémente ebx
10 8048111:        39 d3                        cmp    %edx,%ebx
11;On compare edx et ebx (donc quand ebx vaut la taille du pseudo)
12 8048113:        75 f5                        jne    0x804810a ; si ebx ne vaut la taille du pseudo, que tout les bytes n'ont été parcourus, on retourne plus haut

Finalité de cette seconde partie : On décale dans la table ascii chaque caractère du pseudo de sa taille totale.
Pourquoi je parle de la table ascii ? Eh bien parce que tout caractère est représenté par un nombre (l'ordinateur ne comprenant pas les lettres). La correspondance entre les lettre et les nombres est réalisée par la table ascii. Donc l'opération à l'adresse 804810a ajoute a la lettre traduite en ascii, le nombre de lettres total du pseudo, ce qui changera la lettre.
Nous reverrons ça plus tard.

B) Debuggons

Ce terme, assez barbare pour faire rougir mon correcteur orthographique, mérite un peu d'attention pour les plus débutant d'entre nous.
Le concept est assez simple ; un debugger permet, à chaque instant, de voir ce qui ce passe à l'intérieur d'un programme (état des registres, de la mémoire du programme). Littéralement, debugger signifie « qui enlève les bugs ». Bien sûr, nous n'aurons pas cette prétention là. Nous utiliserons le Gnu DeBugger, pour d'autres fin.

Démarrons donc notre fidèle ami Gdb, toujours là pour nous aider dans notre quête de sérial :

$ gdb keygenMe

Pas besoin de re-désassembler le code ici. Si c'est votre désir le plus ardent vous pouvez bien évidemment le faire, en utilisant la commande « disass ». Ici le programme ayant été linké statiquement, vous ne retrouverez qu'une section, .text, que vous ne pourrez désassembler qu'en lui indiquant l'adresse de début et celle de fin. De cette manière :

(gdb) disass 0x08048080 0x0804816d

Ce qui vous renverra à peu de chose près ce que nous avons vu avec objdump.

/!\ Attention, encore une fois, gdb utilise la syntaxe AT&T, si vous souhaitez qu'il utilise la syntaxe intel, vous pouvez le lui indiquer avec la commande « set disassembly-flavor intel ».

Nous allons maintenant poser un point d'arrêt – breakpoint en anglais – Sur la comparaison du serial entré avec celui attendu.

(gdb) b *0x08048127
Breakpoint 1 at 0x8048127

Pour ceux qui ne le savent pas encore, un breakpoint permet d'arrêter le programme en cours d'éxecution, afin d'examiner, à l'instant définis, l' état des registres, des flags, de la stack, etc...

Maintenant lançons le programme avec la commande run (que l'on peut abréger r)

(gdb) r
Starting program: /home/kallimero/Bureau/codes/asm/keygenMe
--------------------
KeygenMe
By kallimero
--------------------
Pseudo : kallimero
Serial : trolololo

Breakpoint 1, 0x08048127 in ?? ()

On rentre encore une fois, tel un rituel, des informations bidons.
Mais cette fois, paf !
Pas de chocapic mais un breakpoint. Notre programme est en stand by.

Vérifions l'état des registres, avec la commande « info registers » (que l'on peux abréger i r)

(gdb) i r
eax 0x8049214 134517268
ecx 0xa 10
edx 0xa 10
ebx 0x9 9
esp 0xffffd5d0 0xffffd5d0
ebp 0x0 0x0
esi 0x8049214 134517268
edi 0x8049234 134517300
eip 0x8048127 0x8048127
eflags 0x206 [ PF IF ]
cs 0x23 35
ss 0x2b 43
ds 0x2b 43
es 0x2b 43
fs 0x0 0
gs 0x0 0

Comme nous l'avons vu plus haut, c'est eax qui nous intéresse, car c'est lui qui au début contient le pseudo, qui est ensuite modifié pour obtenir le sérial voulu par le programme
Tâchons de l'afficher comme une chaîne de caractère, grâce au système de conversion de gdb.

(gdb) x/s $eax
0x8049214: "tjuurvn{x\n"
(gdb)

x/s : convertir hexadécimal en chaîne de caractère (string)
$eax : le registre eax. N'oubliez pas le $

Le serial correspondant au pseudo kallimero serai donc tjuurvn{x. Le \n n'étant pas pris en compte au moment de la comparaison.

Très bien. Testons donc cela (vous pouvez quitter gdb via la commande quit ou q) :

$ ./keygenMe
--------------------
KeygenMe
By kallimero
--------------------
Pseudo : kallimero
Serial : tjuurvn{x
Yeah, you win. Serial accepted.

Ah, la douce odeur de la victoire approchante et chantante.
Comme vous avez pus le voir notre ruse et notre intelligence nous a permis de dénicher un sérial valide.
Mais évidemment, ce n'est pas finis ; sinon toutes les étapes du début ne nous auraient servis à rien, et vous savez bien que je ne me serai pas permis de vous faire perdre votre temps.
Il va falloir réaliser un programme capable de générer, pour n'importe quel pseudo, un serial valide.

Afin d'accroître notre compréhension de l'algorithme, tâchons de déterminer « mathématiquement », le serial correspondant au magnifique pseudo kallimero.
kallimero a une taille de 9 caractères.
k = 107 | 107 + 9 = 116 = t
a = 97 | 97 + 9 = 106 = j
l = 108 | 108 + 9 = 117 = u
l = 108 | 108 + 9 = 117 = u
i = 105 | 105 + 9 = 114 = r
m = 109 | 109 + 9 = 118 = v
e = 101 | 101 + 9 = 110 = n
r = 114 | 114 + 9 = 123 = {
o = 111 | 111 + 9 = 120 = x

Ce qui nous donne bien tjuurvn{x

Bien sûr c'est une étape un peu artificielle que vous pourrez sauter dans le future, mais elle est ici afin de vous faire comprendre en profondeur le keygenMe présent.

C) Le keygen

Écrire le keygen est normalement plutôt aisé quand la routine de génération du sérial est bien assimilée. Ce qui est bien évidemment notre cas.

Je vais personnellement en écrire un en assembleur, ce qui nous permet de carrément récupérer des bouts du code désassemblé. mais libre à vous d'utiliser tout autre langage.

 1
 2;Compilation :
 3;nasm -f elf keygen.asm
 4; ld -s -o keygen keygen.o
 5
 6section .data
 7        hello: db "Type your pseudo : "
 8        ser: db "Your serial : "
 9        
10section .bss
11        pseudo: resb 32
12        
13section .text
14        global _start
15        
16_start:
17        ;syscall « type your pseudo »
18        mov eax, 4
19        mov ebx, 1
20        mov ecx, hello
21        mov edx, 19
22        int 80h
23        
24        ;syscall qui met ce qui est entré dans la variable pseudo
25        mov eax, 3
26        mov ebx, 0
27        mov ecx, pseudo
28        mov edx, 10
29        int 80h
30        
31
32        mov eax, pseudo ; On met le pseudo dans eax
33        mov edx, 0 ; edx sert de compteur
34        
35        ; On cherche la taille
36        boucle:
37                inc edx ; incrementation d'edx
38                cmp byte [eax + edx], 0 ; Compare avec le null byte
39        jne boucle ; Si c'est pas le nullbyte, on retourne en haut
40        
41        
42        push edx ; On sauvegarde edx en le mettant sur le pile
43        sub edx, 1 ; on enlève 1 à edx 
44        boucle2:
45                add dword [pseudo+ebx], edx ; On ajoute edx au byte n°ebx  du pseudo
46                inc ebx        ; on incrémente le compteur
47                cmp ebx, edx ; on compare ebx a la taille totale
48        jne boucle2 ; SI on a pas encore tout parcouru, on retourne en haut
49        
50        
51        ;syscall « your serial »
52        mov eax, 4
53        mov ebx, 1
54        mov ecx, ser
55        mov edx, 14
56        int 80h
57        
58        ;syscall qui affiche le serial
59        mov eax, 4
60        mov ebx, 1
61        mov ecx, pseudo
62        pop edx
63        int 80h
64        
65        ; serial exit
66        mov eax, 1
67        mov ebx, 0
68        int 80h

Schématiquement, ce code demande à l'utilisateur de rentrer un pseudo, puis il détermine la taille de ce pseudo. Et ajoute à chaque caractère la taille obtenu précédemment. Rien de bien compliqué, que vous pourrez facilement traduire dans une autre langage. J'ai essayé de le commenter au maximum.

Groucho


Be the first to give feedback !

Please login to comment !