Sledovat tuto inicializaci je snadné jako u (téměř) každého procesu strace
ukazuje velmi podezřelé systémové volání na samém začátku běhu procesu:
arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0
To je to, co man 2 arch_prctl
říká:
ARCH_SET_FS
Set the 64-bit base for the FS register to addr.
Jo, vypadá to, že to je to, co potřebujeme. Chcete-li zjistit, kdo volá arch_prctl
, podívejme se na 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 ?? ()
Základ segmentu FS je tedy nastaven pomocí ld-linux
, který je součástí glibc
, během načítání programu (pokud je program staticky propojen, je tento kód vložen do binárního kódu). Tady se to všechno odehrává.
Během spouštění zavaděč inicializuje TLS. To zahrnuje alokaci paměti a nastavení základní hodnoty FS tak, aby ukazovala na začátek TLS. To se provádí pomocí arch_prctl
systémové volání. Po inicializaci TLS security_init
je volána funkce, která vygeneruje hodnotu stack guard a zapíše ji do paměťového místa, což fs:[0x28]
odkazuje na:
- Inicializace ochranné hodnoty zásobníku
- Zápis hodnoty ochrany zásobníku, podrobnější
A 0x28
je offset stack_guard
pole ve struktuře, která se nachází na začátku TLS.
To, co vidíte, se nazývá (v GCC) Stack Smashing Protector (SSP), což je forma ochrany proti přetečení vyrovnávací paměti generované kompilátorem. Hodnota je náhodné číslo vygenerované programem při spuštění a jak se zmiňuje článek na Wikipedii, je umístěno do Thread Local Storage (TLS). Jiné kompilátory mohou k implementaci tohoto typu ochrany používat různé strategie.
Proč ukládat hodnotu do TLS? Protože je tam hodnota umístěna, její adresa není přístupná pro registry CS, DS a SS, takže uhodnutí uložené hodnoty je velmi obtížné, pokud se pokoušíte změnit zásobník před škodlivým kódem.