Cours du 10-12-19
int calc(int a, int b)
{
int c;
c = g + a + b;
return c;
}
#if 0
calc:
push %rbp
mov %rsp, %rbp
sub $24, %rsp
movl %rsp, -8
movl %esi, -16
movl g, -24
addl -8, %edi
addl %edi, -24
==>False
#addl -8, -24
addl -16, %edi
addl %edi, -24
movl -24, %eax
leave
ret
#endif
Relocation : la localisation des fonctions se fait au link.
Il s’agit de la localisation (l’adresse) des differentes fonctions appelees.
Lea
Lea : Load Effective Address
Instruction sur x86
Elle permet de calculer l’adresse plutot que de la dereferencer.
mov -0x10(%rbp), %rdx %rdx <- local;
lea -0x10(%rbp), %rdx %rdx <- &local;
lea (%rax, %rax, 2), %rdx %rdx = %rax * 3
Cette operation est plus rapide qu’avec les instructions de multiplication
lea 0xeaf(%rip)
Avant c’etait une @ en dure, maintenant on essaye d’avoir des programmes qui
puissent etre n’importe ou en memoire.
ELF : Extensible Loading Format, Format des binaire
- Executable
- Relocatable (.o)
- Dynamic (.so, …)
- Coredump : image memoire d’un programme sur le disque. Utilise pour debugger
en ecrivant tout le programme dans un fichier. Ce qui permet d’avoir son etat
au moment d’une erreur (SEGV)
2 utilisations de l’ELF
- statique (compilation, edition de lien)
- dynamique (execution)
Header:
readelf -h <name>
Program header:
readelf -l <name>
Section:
readelf -S <name>
Table des symboles:
readelf -s <name>
Relocation section:
readelf -r <name>
2 tables:
- Table des sections : Correspond a la partie statique
- Table des segments (ou program header) : Correspong a la partie dynamique
Dans un .o, la partie dynamique est un peu vide -> on ne pas executer un .o
Type Load : Doit etre charge en memoire
Chargeur de programme : execve
| |<-|
| |<||
|------| ||
| Auxv | || Vecteur auxiliaire (le kernel place des infos supplementaire pour
| | || la libc et le chargeur de programme)
|------| ||
| Envp |-||
|------| |
| Argv |--|
|------|
| Argc |<- RSP
|------|
| |
De base il n’y a pas d’executable.
On veut du code PIC (Position independante Code) pour que le code puisse etre
charge a une adresse arbitraire.
Les PIE (Position independante Executable), ce sont des objets dynamiques.
La premiere adresse virtuel est a 0x0, ce qui signifie que cette adresse peut
etre n’importe ou. Les autres sont des offsets.
Section
Permet de donner du sens a chaque bout. Le linker met tout bout a bout. Tout
les .text
de tous les .o sont mis bout a bout et formera le .text
du
binaire finale.
STRTAB : Table de string
SYMTAB : Table de symbole (@ des fonctions pour le link que j’ai et que
j’aimerais avoir) il y en a au moins 2.
La table de symbole pointe sur la table de string. En effet, les strings sont
de taille variable. Donc la table de symbole stocke l’emplacement du nom du
symbole dans la table de string.
file.s : fichier assembleur (generes) -> Appel directement GCC
file.S : fichier assembleur avec directives preprocesseur (humain) -> Appel
avant le preprocesseur.
.section .text
.global calc
.type calc, @function
calc:
xor %eax, %eax
movl g, %eax
addl %esi, %eax
addl %edi, %eax
ret
.Lend_calc:
.size calc, .Lend_calc - calc
.section .data
.type g, @object
g:
.long 12
.size g, . - g
Fail compilation : recompile with -fPIE
Relocation Statique
Tentative de chargement de g comme @ absolue.
g -> g(%rip)
Passage en adresse relative
Faire un syscall
build.sh
#!/bin/sh
gcc -c empty.S -o empty.o
ld -o empty empty.o
empty.S //.S -> Possibilite d’include
#include <asm/unistd.h>
.global _start
_start:
mov $1, %rdi
call _exit
_exit:
mov $__NR_exit, %rax
syscall
ret
man 2 syscall
instr : syscall
numero doit etre dans rax
valeur de retour : rax
et rdx
strace <exe>
Trouver le numero de syscall
/usr/include/asm/unistd_64.h
Cours du 10-12-19
int calc(int a, int b) { int c; c = g + a + b; return c; }
#if 0 // argument: a = %edi (car int, sinon %rdi), b = %esi // return value: %eax // stack: // -8(%rbp) a // -16(%rbp) b // -24(%rbp) c calc: push %rbp mov %rsp, %rbp sub $24, %rsp movl %rsp, -8(%rbp) movl %esi, -16(%rbp) movl g, -24(%rbp) //addl a, c addl -8(%rbp), %edi addl %edi, -24(%rbp) ==>False #addl -8(%rbp), -24(%rbp) //Fonctionne pas, 2 deref en meme temps //On doit passer par un registre intermediaire pour faire l'operation //addl b, c addl -16(%rbp), %edi addl %edi, -24(%rbp) movl -24(%rbp), %eax leave ret #endif
Relocation : la localisation des fonctions se fait au link.
Il s’agit de la localisation (l’adresse) des differentes fonctions appelees.
Lea
Lea : Load Effective Address
Instruction sur x86
Elle permet de calculer l’adresse plutot que de la dereferencer.
mov -0x10(%rbp), %rdx %rdx <- local; //Met la valeur de (par deref) lea -0x10(%rbp), %rdx %rdx <- &local; //Met l'adresse de (juste l'adress) lea (%rax, %rax, 2), %rdx %rdx = %rax * 3
Cette operation est plus rapide qu’avec les instructions de multiplication
lea 0xeaf(%rip) //-> %rip = notre adresse courante du programme
Avant c’etait une @ en dure, maintenant on essaye d’avoir des programmes qui
puissent etre n’importe ou en memoire.
Format ELF
ELF : Extensible Loading Format, Format des binaire
en ecrivant tout le programme dans un fichier. Ce qui permet d’avoir son etat
au moment d’une erreur (SEGV)
2 utilisations de l’ELF
Header
Header:
readelf -h <name>
Program header:
readelf -l <name>
Section:
readelf -S <name>
Table des symboles:
readelf -s <name>
Relocation section:
readelf -r <name>
2 tables:
Dans un .o, la partie dynamique est un peu vide -> on ne pas executer un .o
Type Load : Doit etre charge en memoire
Chargeur de programme : execve
De base il n’y a pas d’executable.
On veut du code PIC (Position independante Code) pour que le code puisse etre
charge a une adresse arbitraire.
Les PIE (Position independante Executable), ce sont des objets dynamiques.
La premiere adresse virtuel est a 0x0, ce qui signifie que cette adresse peut
etre n’importe ou. Les autres sont des offsets.
Section
Permet de donner du sens a chaque bout. Le linker met tout bout a bout. Tout
les
.text
de tous les .o sont mis bout a bout et formera le.text
dubinaire finale.
STRTAB : Table de string
SYMTAB : Table de symbole (@ des fonctions pour le link que j’ai et que
j’aimerais avoir) il y en a au moins 2.
La table de symbole pointe sur la table de string. En effet, les strings sont
de taille variable. Donc la table de symbole stocke l’emplacement du nom du
symbole dans la table de string.
file.s : fichier assembleur (generes) -> Appel directement GCC
file.S : fichier assembleur avec directives preprocesseur (humain) -> Appel
avant le preprocesseur.
.section .text //Maintenant nous sommes dans '.text' .global calc //La fonction est global, d'autre peuvent l'utiliser .type calc, @function //Calc est de type fonction calc: xor %eax, %eax movl g, %eax addl %esi, %eax addl %edi, %eax ret .Lend_calc: .size calc, .Lend_calc - calc //Utile pour le debugger, avec deux symbole //On peut avoir facilement la taille //En prefixant un label par '.L', ce dernier n'auras pas de symbole associe // . - calc : '.' represente l'adresse courante .section .data .type g, @object g: .long 12 //Il ecrit 12 sur 4 bytes .size g, . - g
Fail compilation : recompile with -fPIE
Relocation Statique
Tentative de chargement de g comme @ absolue.
g -> g(%rip)
Passage en adresse relativeFaire un syscall
build.sh
#!/bin/sh gcc -c empty.S -o empty.o ld -o empty empty.o
empty.S //.S -> Possibilite d’include
#include <asm/unistd.h> .global _start _start: mov $1, %rdi call _exit _exit: mov $__NR_exit, %rax syscall ret
man 2 syscall
instr : syscall
numero doit etre dans
rax
valeur de retour :
rax
etrdx
strace <exe>
Trouver le numero de syscall
/usr/include/asm/unistd_64.h