Il est facile de suivre cette initialisation, comme pour (presque) tous les processus strace
affiche un appel système très suspect au tout début de l'exécution du processus :
arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0
C'est ce que man 2 arch_prctl
dit :
ARCH_SET_FS
Set the 64-bit base for the FS register to addr.
Ouais, on dirait que c'est ce dont nous avons besoin. Pour trouver qui appelle arch_prctl
, cherchons un backtrace :
(gdb) catch syscall arch_prctl
Catchpoint 1 (syscall 'arch_prctl' [158])
(gdb) r
Starting program: <program path>
Catchpoint 1 (call to syscall arch_prctl), 0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
(gdb) bt
#0 0x00007ffff7dd9cad in init_tls () from /lib64/ld-linux-x86-64.so.2
#1 0x00007ffff7ddd3e3 in dl_main () from /lib64/ld-linux-x86-64.so.2
#2 0x00007ffff7df04c0 in _dl_sysdep_start () from /lib64/ld-linux-x86-64.so.2
#3 0x00007ffff7dda028 in _dl_start () from /lib64/ld-linux-x86-64.so.2
#4 0x00007ffff7dd8fb8 in _start () from /lib64/ld-linux-x86-64.so.2
#5 0x0000000000000001 in ?? ()
#6 0x00007fffffffecef in ?? ()
#7 0x0000000000000000 in ?? ()
Ainsi, la base du segment FS est définie par le ld-linux
, qui fait partie de glibc
, lors du chargement du programme (si le programme est lié statiquement, ce code est intégré au binaire). C'est ici que tout se passe.
Lors du démarrage, le loader initialise TLS. Cela inclut l'allocation de mémoire et la définition de la valeur de base FS pour pointer vers le début de TLS. Cela se fait via arch_prctl
appel système. Après initialisation TLS security_init
la fonction est appelée, qui génère la valeur de la garde de pile et l'écrit dans l'emplacement de la mémoire, qui fs:[0x28]
pointe vers :
- Initialisation de la valeur Stack Guard
- Écriture de la valeur Stack Guard, plus détaillée
Et 0x28
est le décalage du stack_guard
champ dans la structure qui se trouve au début de TLS.
Ce que vous voyez s'appelle (dans GCC) le Stack Smashing Protector (SSP), qui est une forme de protection contre le dépassement de mémoire tampon générée par le compilateur. La valeur est un nombre aléatoire généré par le programme au démarrage et, comme le mentionne l'article de Wikipedia, est placé dans Thread Local Storage (TLS). D'autres compilateurs peuvent utiliser des stratégies différentes pour implémenter ce type de protection.
Pourquoi stocker la valeur dans TLS ? Comme la valeur s'y trouve, son adresse n'est pas accessible par les registres CS, DS et SS, ce qui rend très difficile de deviner la valeur stockée si vous essayez de modifier la pile à partir d'un code malveillant.