Es ist einfach, diese Initialisierung zu verfolgen, wie bei (fast) jedem Prozess strace
zeigt einen sehr verdächtigen Systemaufruf ganz am Anfang des Prozesslaufs:
arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0
Das ist was man 2 arch_prctl
sagt:
ARCH_SET_FS
Set the 64-bit base for the FS register to addr.
Yay, sieht so aus, als ob wir das brauchen. Zu finden, wer arch_prctl
anruft , suchen wir nach einem 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 ?? ()
Die FS-Segmentbasis wird also durch ld-linux
festgelegt , das ein Teil von glibc
ist , während des Programmladens (wenn das Programm statisch gelinkt ist, wird dieser Code in die Binärdatei eingebettet). Hier passiert alles.
Während des Starts initialisiert der Loader TLS. Dies umfasst die Speicherzuweisung und das Festlegen des FS-Basiswerts, um auf den TLS-Anfang zu verweisen. Dies geschieht über arch_prctl
Systemaufruf. Nach der TLS-Initialisierung security_init
Funktion aufgerufen, die den Wert des Stackguards generiert und an die Speicherstelle schreibt, die fs:[0x28]
zeigt auf:
- Stack Guard-Wert-Initialisierung
- Stapelschutzwert schreiben, detaillierter
Und 0x28
ist der Offset von stack_guard
Feld in der Struktur, das am Anfang von TLS steht.
Was Sie sehen, wird (in GCC) Stack Smashing Protector (SSP) genannt, eine Art Pufferüberlaufschutz, der vom Compiler generiert wird. Der Wert ist eine vom Programm beim Start generierte Zufallszahl und wird, wie im Wikipedia-Artikel erwähnt, im Thread Local Storage (TLS) abgelegt. Andere Compiler verwenden möglicherweise andere Strategien, um diese Art von Schutz zu implementieren.
Warum den Wert in TLS speichern? Da sich der Wert dort befindet, ist seine Adresse für die CS-, DS- und SS-Register nicht zugänglich, was das Erraten des gespeicherten Werts sehr schwierig macht, wenn Sie versuchen, den Stack durch bösartigen Code zu ändern.