Eu posso dizer por que está falhando, embora eu realmente não saiba qual parte do sistema é responsável. Embora .dtors
esteja marcado como gravável no binário, parece que ele (junto com .ctors
o GOT e algumas outras coisas) está sendo mapeado em uma página separada, não gravável na memória. No meu sistema, .dtors
está sendo colocado em 0x8049f14
:
$ readelf -S test
[17] .ctors PROGBITS 08049f0c 000f0c 000008 00 WA 0 0 4
[18] .dtors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
[19] .jcr PROGBITS 08049f1c 000f1c 000004 00 WA 0 0 4
[20] .dynamic DYNAMIC 08049f20 000f20 0000d0 08 WA 6 0 4
[21] .got PROGBITS 08049ff0 000ff0 000004 04 WA 0 0 4
[22] .got.plt PROGBITS 08049ff4 000ff4 00001c 04 WA 0 0 4
[23] .data PROGBITS 0804a010 001010 000008 00 WA 0 0 4
[24] .bss NOBITS 0804a018 001018 000008 00 WA 0 0 4
Se eu executar o executável e verificar /proc/PID/maps
, vejo:
08048000-08049000 r-xp 00000000 08:02 163678 /tmp/test
08049000-0804a000 r--p 00000000 08:02 163678 /tmp/test
0804a000-0804b000 rw-p 00001000 08:02 163678 /tmp/test
.data
/ .bss
ainda são graváveis em sua própria página, mas os outros 0x8049000-0x804a000
não. Eu assumo que este é um recurso de segurança no kernel (como você disse, "houve um movimento em direção a somente .dtors, plt, recentemente)", mas não sei especificamente o que é chamado (o OpenBSD tem algo muito semelhante chamado W ^ X ; o Linux possui PaX , mas não está embutido na maioria dos kernels)
Você pode contornar isso mprotect
, o que permite alterar os atributos na memória de uma página:
mprotect((void*)0x8049000, 4096, PROT_WRITE);
Com isso, meu programa de teste não falha, mas se eu tentar sobrescrever o sentinela final de .dtors
( 0x8049f18
) pelo endereço de outra função, essa função ainda não será executada; essa parte eu não consigo descobrir.
Espero que alguém saiba o que é responsável por tornar a página somente leitura e por que modificar .dtors
não parece fazer nada no meu sistema