È facile tenere traccia di questa inizializzazione, come per (quasi) ogni processo strace
mostra una chiamata di sistema molto sospetta durante l'inizio dell'esecuzione del processo:
arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0
Questo è ciò che man 2 arch_prctl
dice:
ARCH_SET_FS
Set the 64-bit base for the FS register to addr.
Sì, sembra proprio quello di cui abbiamo bisogno. Per trovare, chi chiama arch_prctl
, cerchiamo 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 ?? ()
Quindi, la base del segmento FS è impostata dal ld-linux
, che fa parte di glibc
, durante il caricamento del programma (se il programma è collegato staticamente, questo codice è incorporato nel binario). Qui è dove tutto accade.
Durante l'avvio, il caricatore inizializza TLS. Ciò include l'allocazione della memoria e l'impostazione del valore di base FS in modo che punti all'inizio di TLS. Questo viene fatto tramite arch_prctl
syscall. Dopo l'inizializzazione TLS security_init
viene chiamata la funzione, che genera il valore della protezione dello stack e lo scrive nella posizione di memoria, quale fs:[0x28]
punta a:
- Inizializzazione del valore di protezione dello stack
- Scrittura del valore di protezione dello stack, più dettagliata
E 0x28
è l'offset del stack_guard
campo nella struttura che si trova all'inizio di TLS.
Quello che vedi è chiamato (in GCC) Stack Smashing Protector (SSP), che è una forma di protezione dall'overflow del buffer generata dal compilatore. Il valore è un numero casuale generato dal programma all'avvio e, come menziona l'articolo di Wikipedia, viene inserito in Thread Local Storage (TLS). Altri compilatori possono utilizzare strategie diverse per implementare questo tipo di protezione.
Perché archiviare il valore in TLS? Poiché il valore si trova lì, il suo indirizzo non è accessibile dai registri CS, DS e SS, rendendo molto difficile indovinare il valore memorizzato se stai tentando di alterare lo stack da codice dannoso.