C'est quoi un ELF, et pourquoi t'en
croises partout
Tour d'horizon du format ELF (Executable and Linkable Format) sous Linux x86_64 : compilation, identification d'un binaire avec file et strings, et premier apercu de la segmentation memoire.
Maxime Jérôme··4 min de lecture
Prerequis
- Comprehension basique de Linux
- Shell
- Maniement de GCC
- Differentes adresses (virtuelles, logiques, physiques)
Hello ! o/
Je vais vous introduire le format ELF (Executable and Linkable Format) sous architecture Intel x86_64, qui est le format de fichier le plus populaire pour la conception d'un fichier executable sous systemes Unix-like.
Creation d'un Programme
Commençons par la base, c'est-a-dire comment vous, lecteur, pouvez concevoir un programme. Voici un exemple simple :
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc > 1) {
fprintf(stderr, "[!] Error: Usage: ./program\n");
exit(EXIT_FAILURE);
}
fprintf(stdout, "Hello World !\n");
return EXIT_SUCCESS;
}Pour compiler et executer ce programme, vous pouvez utiliser les commandes suivantes :
$ gcc source.c -o program
$ ./program
Hello World !
$ ./program ARG
[!] Error: Usage: ./programDans cet exemple, notre programme source est source.c et le programme executable est program. Lorsque nous utilisons la commande file, nous voyons que notre programme executable est bien de format ELF :
$ file program
program: ELF 64-bit LSB pie executable, x86-64,
version 1 (SYSV),
dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2,
BuildID[sha1]=...,
for GNU/Linux 3.2.0,
not strippedAnalyse du Programme Executable
Sans entrer dans les details, si nous executons strings sur program, voici un extrait de la sortie que nous obtenons :
/lib64/ld-linux-x86-64.so.2
libc.so.6
exit
stdout
stderr
fwrite
main
...On remarque que certaines chaines de caracteres sont hardcodees (mises en brut) dans le programme executable. Parmi les motifs identifiables, nous avons :
libc.so.6GCC: (Debian 8.3.0-6) 8.3.0fwritemainexitstderr
Ces elements nous donnent un apercu de ce qui est encapsule dans le programme executable, mais ne revelent pas beaucoup d'informations exploitables.
Pourquoi strings revele autant ?
-s (strip) reduit mais ne supprime pas totalement ces informations.Segmentation de la Memoire
Il est important de noter qu'un programme executable s'execute sur le CPU, et que la memoire chargee peut se situer physiquement en cache, en RAM, sur le disque dur ou ailleurs. De plus, le programme ne se charge pas dans un gros bloc d'adresses virtuelles ; il est divise en plusieurs segments dans la memoire.
Adresses virtuelles hautes
┌────────────────────────────────────────┐
│ [stack] │ variables locales, adresses de retour
│ │ │
│ ▼ │
│ │
│ ▲ │
│ │ │
│ [heap] │ allocations dynamiques (malloc)
├────────────────────────────────────────┤
│ .bss │ variables globales non initialisees
├────────────────────────────────────────┤
│ .data │ variables globales initialisees
├────────────────────────────────────────┤
│ .text │ code executable (lecture seule)
├────────────────────────────────────────┤
│ bibliotheques partagees (libc, ...) │
└────────────────────────────────────────┘
Adresses virtuelles bassesArticle suivant
.text, .data, .got, .plt : la carto d'un ELF