Eu posso dizer por que está falhando, embora eu não saiba realmente qual parte do sistema é responsável. Enquanto .dtors
é marcado como gravável no binário, parece que (junto com .ctors
, o GOT e algumas outras coisas) estão sendo mapeados em uma página separada e 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 podem ser gravados em sua própria página, mas os outros em 0x8049000-0x804a000
não são. Eu suponho que este é um recurso de segurança no kernel (como você disse, "houve um movimento em direção a readonly .dtors, plt, got ultimamente"), mas eu não sei especificamente o que é chamado (OpenBSD tem algo muito semelhante chamado W ^ X ; o Linux tem PaX , mas não embutido na maioria dos kernels)
Você pode contornar isso com 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 trava, mas se eu tentar sobrescrever o sentinela final de .dtors
( 0x8049f18
) com o 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 readonly, e por que modificar .dtors
não parece fazer nada no meu sistema