Was setzt fs: [0x28] (Stapelkanarienvogel)?

11

Aus diesem Beitrag geht hervor, dass FS:[0x28]es sich um einen Stapelkanarienvogel handelt. Ich generiere denselben Code mit GCC für diese Funktion.

void foo () {
    char a[500] = {};
    printf("%s", a);
}

Insbesondere bekomme ich diese Baugruppe.

    0x000006b5      64488b042528.  mov rax, qword fs:[0x28]                ; [0x28:8]=0x1978 ; '(' ; "x\x19"
    0x000006be      488945f8       mov qword [local_8h], rax
...stuff...
    0x00000700      488b45f8       mov rax, qword [local_8h]
    0x00000704      644833042528.  xor rax, qword fs:[0x28]
    0x0000070d      7405           je 0x714
    0x0000070f      e85cfeffff     call sym.imp.__stack_chk_fail           ; void __stack_chk_fail(void)
    ; CODE XREF from 0x0000070d (sym.foo)
    0x00000714      c9             leave
    0x00000715      c3             ret

Wofür wird der Wert eingestellt fs:[0x28]? Der Kernel oder wirft GCC den Code ein? Können Sie den Code im Kernel anzeigen oder in die festgelegte Binärdatei kompilieren fs:[0x28]? Wird der Kanarienvogel regeneriert - beim Booten oder beim Prozess-Spawn? Wo ist das dokumentiert?

Evan Carroll
quelle

Antworten:

17

Es ist einfach, diese Initialisierung zu verfolgen, da (fast) jeder Prozess zu straceBeginn des Prozesslaufs einen sehr verdächtigen Systemaufruf anzeigt:

arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0

Das man 2 arch_prctlsagt:

   ARCH_SET_FS
          Set the 64-bit base for the FS register to addr.

Ja, es sieht so aus, als ob wir das brauchen. Um herauszufinden, wer anruft arch_prctl, suchen wir nach einer Rückverfolgung:

(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 während des Programmladens durch das festgelegt ld-linux, zu dem ein Teil gehört glibc(wenn das Programm statisch verknüpft ist, wird dieser Code in die Binärdatei eingebettet). Hier passiert alles.

Während des Startvorgangs initialisiert der Loader TLS . Dies umfasst die Speicherzuweisung und das Festlegen des FS-Basiswerts, um auf den TLS-Anfang zu zeigen. Dies erfolgt über arch_prctl syscall . Nach dem TLS - Initialisierung - security_init Funktion aufgerufen wird , die den Wert der Stack - Guard erzeugt und schreibt sie auf die Speicherstelle, die fs:[0x28]Punkte an:

Und 0x28ist der Versatz des stack_guardFeldes in der Struktur, die sich am TLS-Start befindet.

Danila Kiver
quelle
zomfg, wirklich tolle Antwort. Ich habe versucht, eine Binärdatei mit Radare zu zerlegen. Dies hat die Form und den Inhalt, die ich gesucht habe. Danke vielmals.
Evan Carroll
Was initialisiert einen Prozess mit arch_prctl(ARCH_SET_FS..)Ich sehe das nicht in der ausführbaren Datei? Ist das Kernel-Code?
Evan Carroll
Siehe den Link "syscall" im Beitrag. Es führt zur eigentlichen Aufrufseite ( git.launchpad.net/glibc/tree/sysdeps/x86_64/nptl/tls.h#n153 ), auf der der Syscall ausgeführt wird. Es wird von ld-linuxwährend der TLS-Initialisierung ausgeführt.
Danila Kiver
6

Was Sie sehen, wird (in GCC) als Stack Smashing Protector (SSP) bezeichnet , eine Form des vom Compiler generierten Pufferüberlaufschutzes . Der Wert ist eine Zufallszahl, die vom Programm beim Start generiert wird und, wie im Wikipedia-Artikel erwähnt, in Thread Local Storage (TLS) abgelegt wird . 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, können die CS-, DS- und SS-Register nicht auf seine Adresse zugreifen, was das Erraten des gespeicherten Werts sehr schwierig macht, wenn Sie versuchen, den Stapel von bösartigem Code zu ändern.

ErikF
quelle
Das ist nicht das, wonach ich suche, also habe ich ein wenig geklärt, um klar zu sein. "Zufallszahl, die vom Programm beim Start generiert wird" Können Sie zeigen, wo in einer ausführbaren Datei sie generiert wird und wie der Code für die Generierung lautet?
Evan Carroll