Co ustawia fs: [0x28] (kanarek stosu)?

13

Z tego postu pokazano, że FS:[0x28]jest kanarek-stos. Generuję ten sam kod za pomocą GCC w tej funkcji,

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

W szczególności otrzymuję ten zestaw ..

    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

Co to jest ustawienie wartości fs:[0x28]? Jądro, czy GCC wrzuca kod? Czy potrafisz pokazać kod w jądrze lub skompilować do pliku binarnego, który ustawia fs:[0x28]? Czy kanarek jest regenerowany - przy starcie, czy proces spawnowania? Gdzie to jest udokumentowane?

Evan Carroll
źródło

Odpowiedzi:

18

Łatwo jest śledzić tę inicjalizację, ponieważ (prawie) każdy proces stracewykazuje bardzo podejrzane wywołanie systemowe na samym początku uruchamiania procesu:

arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0

Tak man 2 arch_prctlmówi się:

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

Tak, wygląda na to, że potrzebujemy. Aby dowiedzieć się, kto dzwoni arch_prctl, poszukajmy śladu wstecz:

(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 ?? ()

Tak więc podstawa segmentu FS jest ustawiana przez ld-linux, który jest częścią glibc, podczas ładowania programu (jeśli program jest statycznie powiązany, kod ten jest osadzony w pliku binarnym). Tutaj wszystko się dzieje.

Podczas uruchamiania moduł ładujący inicjuje TLS . Obejmuje to przydział pamięci i ustawienie wartości bazowej FS tak, aby wskazywała początek TLS. Odbywa się to poprzez arch_prctl syscall . Po security_init wywołaniu funkcji inicjalizacji TLS , która generuje wartość ochrony stosu i zapisuje ją w lokalizacji pamięci, co fs:[0x28]wskazuje na:

I 0x28jest przesunięciem stack_guardpola w strukturze, która znajduje się na początku TLS.

Danila Kiver
źródło
zomfg, naprawdę świetna odpowiedź. Próbowałem zdemontować plik binarny z radarem. ma to formę i treść, której szukałem. Wielkie dzięki.
Evan Carroll
Co inicjuje proces, arch_prctl(ARCH_SET_FS..)którego nie widzę w pliku wykonywalnym? Czy to jest kod jądra?
Evan Carroll,
Zobacz link „syscall” w poście. Prowadzi do faktycznej strony wywołań ( git.launchpad.net/glibc/tree/sysdeps/x86_64/nptl/tls.h#n153 ), w której wykonywane jest wywołanie systemowe . Jest wykonywany ld-linuxpodczas inicjowania TLS.
Danila Kiver
6

To, co widzisz, nazywa się (w GCC) Stack Smashing Protector (SSP) , który jest formą ochrony przed przepełnieniem bufora generowanej przez kompilator. Wartość jest liczbą losową generowaną przez program podczas uruchamiania i, jak wspomniano w artykule w Wikipedii, jest umieszczana w wątku Local Storage (TLS) . Inne kompilatory mogą stosować różne strategie do implementacji tego rodzaju ochrony.

Po co przechowywać wartość w TLS? Ponieważ wartość znajduje się tam, jej adres nie jest dostępny dla rejestrów CS, DS i SS, co utrudnia odgadnięcie przechowywanej wartości, jeśli próbujesz zmienić stos ze złośliwego kodu.

ErikF
źródło
Nie tego szukam, więc trochę wyjaśniłem, próbując się wyrazić. „liczba losowa generowana przez program przy starcie”, czy możesz pokazać, gdzie w pliku wykonywalnym jest generowany i co składa się na kod, aby go wygenerować?
Evan Carroll,