função de saída do Linux

3

Eu estou tentando entender o mecanismo syscalls do Linux. Eu estou lendo um livro e ele no livro diz que a função de saída é assim (com o gdb):

mov $0x0,%ebx
mov $0x1,%eax
80 int $0x80

Eu entendo que isso é um syscall para sair, mas no meu Debian parece que:

jmp    *0x8049698
push   $0x8
jmp    0x80482c0

alguém pode me explicar por que não é o mesmo? Quando tento fazer desastres em 0x80482c0 gdb me imprime:

No function contains specified address.

Obrigado!

    
por gtalst12 21.01.2016 / 18:03

1 resposta

2

A chamada exit() do seu código acaba sendo vinculada à função da biblioteca C (libc) exit() , que pode na verdade não fazer o int $0x80 .

A chamada da invocação do seu código da função exit() é, na verdade, compilada como call na Tabela de Ligação do Programa, ou PLT. O vinculador dinâmico de tempo de execução cuida do mapeamento do arquivo /usr/lib/libc.so na memória. Essa é a biblioteca C. O vinculador dinâmico de tempo de execução também corrige entradas no PLT para, eventualmente, acabar chamando o código mapeado em /usr/lib/libc.so .

Por mais que eu saiba (estou usando o Arch linux), suas segundas 3 instruções são a entrada PLT, que gdb chama de "exit @ plt" quando eu dou um passo para dentro dela. O jmp 0x80482c0 pula para outro endereço que finalmente salta para o código libc.so .

Você pode demonstrar isso para si mesmo com um exercício bastante complicado. Primeiro, você tem o endereço da entrada da tabela PLT, seja qual for o gdb que é o endereço do jmp *0x8049698 - esse é o endereço de "exit @ plt". Na minha caixa xux Arch linux:

(gdb) disassemble 0x8048310,+20
Dump of assembler code from 0x8048310 to 0x8048324:
   0x08048310 <exit@plt+0>:     jmp    *0x80496e8
   0x08048316 <exit@plt+6>:     push   $0x10
   0x0804831b <exit@plt+11>:    jmp    0x80482e0

Em seguida, faça readelf -e _program_ > elf.headers . Procure no arquivo elf.headers . Você encontrará uma linha de texto que diz "Cabeçalhos da seção:" Em algum lugar nos cabeçalhos da seção, você verá algo assim:

  [ 9] .rel.dyn          REL             08048290 000290 000008 08   A  5   0  4
  [10] .rel.plt          REL             08048298 000298 000020 08  AI  5  12  4
  [11] .init             PROGBITS        080482b8 0002b8 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        080482e0 0002e0 000050 04  AX  0   0 16

"exit @ plt" está no endereço 0x8048310. É isso mesmo na seção ".rel.plt". ".rel.plt" provavelmente significa "tabela de ligação do programa de realocação".

Agora chegamos à parte em que o int $0x80 pode nem existir. Do ldd _program_ . Novamente, Arch linux x86 diz isso:

linux-gate.so.1 (0xb77d9000)
libc.so.6 => /usr/lib/libc.so.6 (0xb7603000)
/lib/ld-linux.so.2 (0xb77da000)

Veja que "linux-gate.so.1"? Isso contém o código real que faz a chamada do sistema. Pode ser int $0x80 , ou pode ser uma instrução sysenter , ou pode ser outra coisa. Supõe-se que o kernel Linux coloque uma "pequena biblioteca compartilhada" no espaço de endereço de um processo com o código real e, em seguida, entregue o endereço dessa pequena biblioteca compartilhada no "vetor auxiliar" do ELF. Do man vdso para alguns detalhes. O vinculador dinâmico, /lib/ld-linux.so.2 , conhece os detalhes do vetor auxiliar ELF e, em última instância, coloca o endereço linx-gate.so.1 no PLT em algum lugar, portanto chamadas de função C reais podem acabar fazendo chamadas de sistema eficientes.

Se você fizer várias chamadas de ldd _program_ , verá que o endereço de linux-gate.so.1 não é o mesmo de invocação para invocação. O kernel na verdade não coloca o topo da pilha no mesmo endereço todas as vezes para tentar confundir o malware que precisa conhecer os locais da pilha para obter seu próprio código executado.

    
por 21.01.2016 / 20:30

Tags