O que define fs: [0x28] (canário de pilha)?

11

A partir deste post , é mostrado que FS:[0x28]é um canário de pilha. Estou gerando o mesmo código usando o GCC nessa função,

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

Especificamente, estou recebendo esta montagem ..

    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

Qual é a definição do valor fs:[0x28]? O kernel ou o GCC está lançando o código? Você pode mostrar o código no kernel ou compilado no binário que define fs:[0x28]? O canário é regenerado - na inicialização ou no processo? Onde isso está documentado?

Evan Carroll
fonte

Respostas:

17

É fácil rastrear essa inicialização, pois (quase) todo processo stracemostra uma chamada muito suspeita durante o início da execução do processo:

arch_prctl(ARCH_SET_FS, 0x7fc189ed0740) = 0

É o que man 2 arch_prctldiz:

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

Sim, parece que é disso que precisamos. Para descobrir quem telefona arch_prctl, vamos procurar um 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 ?? ()

Portanto, a base do segmento FS é definida por ld-linux, que faz parte glibc, durante o carregamento do programa (se o programa estiver estaticamente vinculado, esse código será incorporado no binário). É aqui que tudo acontece.

Durante a inicialização, o carregador inicializa o TLS . Isso inclui alocação de memória e configuração do valor base do FS para apontar para o início do TLS. Isso é feito via arch_prctl syscall . Depois que a security_init função de inicialização do TLS é chamada, o que gera o valor do protetor de pilha e o grava no local da memória, que fs:[0x28]aponta para:

E 0x28é o deslocamento do stack_guardcampo na estrutura que está localizada no início do TLS.

Danila Kiver
fonte
zomfg, realmente ótima resposta. Eu estava tentando desmontar um binário com radare. isso tem a forma e o conteúdo que eu estava procurando. Muito obrigado.
Evan Carroll
O que inicializa um processo arch_prctl(ARCH_SET_FS..), não o vejo no executável? Esse é o código do kernel?
Evan Carroll
Veja o link "syscall" na postagem. Isso leva ao site de chamada real ( git.launchpad.net/glibc/tree/sysdeps/x86_64/nptl/tls.h#n153 ) onde o syscall é executado. É executado ld-linuxdurante a inicialização do TLS.
Danila Kiver
6

O que você vê é chamado (no GCC) de Stack Smashing Protector (SSP) , que é uma forma de proteção contra estouro de buffer gerada pelo compilador. O valor é um número aleatório gerado pelo programa na inicialização e, como menciona o artigo da Wikipedia, é colocado no TLS (Thread Local Storage) . Outros compiladores podem usar estratégias diferentes para implementar esse tipo de proteção.

Por que armazenar o valor no TLS? Como o valor está localizado lá, seu endereço não é acessível pelos registros CS, DS e SS, tornando muito difícil adivinhar o valor armazenado se você estiver tentando alterar a pilha do código malicioso.

ErikF
fonte
Não é isso que estou procurando, por isso esclareço um pouco na tentativa de esclarecer. "número aleatório gerado pelo programa na inicialização" você pode mostrar onde um executável é gerado e o que está colocando o código para gerá-lo?
Evan Carroll