Les objets REL, sont ici, les fichiers objets produits par gcc. Par exemple, si nous avons le fichier source fonctions.c suivant:
1 2 3 4 5 6 7 8 9 |
|
Il contient deux fonctions inc qui retourne l'entier x fournit en paramètre incrémenté de 1 et add qui retroune la somme des deux paramètres fournis. Nous avons choisis, deux fonctions simples pour commencer.
Si l'on souhaite voir le code assembleur généré par gcc il faut d'abord compiler le fichier source ci-dessus avec la commande suivante:
$ gcc -c fonctions.c -Wall
Nous obtenons alors un fichier fonctions.o, et nous allons maintenant le désassembler avec une commande Linux, toujours présente sur une station de développement qui se respecte. Cette commande, c'est objdump et l'exemple ci-dessous vous permet de comprendre comment l'utiliser pour désassembler notre fichier .o:
$ objdump -d fonctions.o
Ci-dessous le résultat affiché par la commande:
fonctions.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <inc>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 89 7d fc mov %edi,-0x4(%rbp)
7: 8b 45 fc mov -0x4(%rbp),%eax
a: 8d 50 01 lea 0x1(%rax),%edx
d: 89 55 fc mov %edx,-0x4(%rbp)
10: 5d pop %rbp
11: c3 retq
0000000000000012 <add>:
12: 55 push %rbp
13: 48 89 e5 mov %rsp,%rbp
16: 89 7d fc mov %edi,-0x4(%rbp)
19: 89 75 f8 mov %esi,-0x8(%rbp)
1c: 8b 55 fc mov -0x4(%rbp),%edx
1f: 8b 45 f8 mov -0x8(%rbp),%eax
22: 01 d0 add %edx,%eax
24: 5d pop %rbp
25: c3 retq
Le code désassemblé est affiché, avec la syntaxe at&t utilisée par le compilateur gas (GNU Assembler). Or, il s'avère que nombre d'entre nous utilisent plus courremment la syntaxe intel... Et bien c'est possible, pour cela, la commande est très proche comme vous allez le voir ci-dessous:
$ objdump -d fonctions.o -Mintel
Ci-dessous le résultat affiché en mode intel:
fonctions.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <inc>:
0: 55 push rbp
1: 48 89 e5 mov rbp,rsp
4: 89 7d fc mov DWORD PTR [rbp-0x4],edi
7: 8b 45 fc mov eax,DWORD PTR [rbp-0x4]
a: 8d 50 01 lea edx,[rax+0x1]
d: 89 55 fc mov DWORD PTR [rbp-0x4],edx
10: 5d pop rbp
11: c3 ret
0000000000000012 <add>:
12: 55 push rbp
13: 48 89 e5 mov rbp,rsp
16: 89 7d fc mov DWORD PTR [rbp-0x4],edi
19: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
1c: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
1f: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
22: 01 d0 add eax,edx
24: 5d pop rbp
25: c3 ret
Nous allons maintenant, dans un but péddagogique tricher un peu. Et nous allons faire cela en demandant à gcc de générer le code assembleur qui a servi à construire notre fichier objet fonctions.o. Et ceci pour vous permettre de comprendre quelques mécanismes de compilation.
Recompilons le source fonctions.c mais cette fois-ci avec cette commande:
$ gcc -masm=intel -S fonctions.c
Nous obtenons alors un fichier fonctions.s, qui est le source assembleur de notre fichier, généré par gcc:
.file "fonctions.c"
.intel_syntax noprefix
.text
.globl inc
.type inc, @function
inc:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR -4[rbp], edi
mov eax, DWORD PTR -4[rbp]
lea edx, 1[rax]
mov DWORD PTR -4[rbp], edx
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size inc, .-inc
.globl add
.type add, @function
add:
.LFB1:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR -4[rbp], edi
mov DWORD PTR -8[rbp], esi
mov edx, DWORD PTR -4[rbp]
mov eax, DWORD PTR -8[rbp]
add eax, edx
pop rbp
.cfi_def_cfa 7, 8
ret
On remarquera au passage que l'option -masm=intel permet d'obtenir la syntaxe intel au lieu de at&t.
On retrouve bien dans le code généré depuis le source ci-dessus, les mêmes instructions que lorsque nous avons désassemblé le binaire.
Voilà, vous avez déjà une première solution pour désassembler des bibliothèques binaires. Le faire avec un programme complet est identique, à par que le code est bien plus complexe que celui des fonctions de cet exemple.
Bien entendu on se rend compte qu'il est nécessaire de connaître le language d'assemblage pour comprendre quelquechose à tout cela.
Apprendre le language assembleur peut être intimident au début, mais sachez que cela n'est pas si difficile que cela en à l'air, et si vous voulez vraiment réaliser des programmes de qualité, c'est malheureusement incontournable.