Ubuntu 9.04, 2.6.28-11 서버, 32비트 x86입니다.
$ cat test.c
main() { int *dt = (int *)0x08049f18; *dt = 1; }
$ readelf -S ./test
...
[18] .dtors PROGBITS 08049f14 000f14 000008 00 WA 0 0 4
...
$ ./test
Segmentation fault
$
초보자의 경우: gcc는 .dtors
종료 후에 호출되는 elf 실행 파일에 소멸자 섹션을 만듭니다 main()
. 테이블은 오랫동안 쓰기 가능했으며 제 경우에는 그래야 할 것 같습니다( readelf
출력 참조). 그러나 테이블에 쓰려고 하면 세그폴트가 발생합니다.
최근 읽기 전용 .dtors, plts에 대한 추세가 있다는 것을 알고 있지만 내가 이해하지 못하는 것은 readelf
segfault와의 불일치입니다.
답변1
이러한 섹션은 GNU_RELRO(읽기 전용 재배치)로 표시됩니다. 즉, 동적 로더가 모든 재배치를 수정하면(로드 시 지연된 재배치가 없음) 해당 섹션을 읽기 전용으로 표시합니다. 대부분의 콘텐츠는 .got.plt
다른 페이지에 있으므로 처리되지 않습니다.
을 사용하여 링크 스크립트를 볼 수 ld --verbose
있으며 RELRO를 검색하면 다음과 같은 내용을 찾을 수 있습니다.
.got : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt : { *(.got.plt) }
이는 RELRO 섹션이 12바이트로 끝남을 의미합니다 .got.plt
(동적 링커 함수에 대한 포인터가 확인되었으므로 읽기 전용으로 표시될 수 있음).
강화된 젠투 프로젝트에는 RELRO에 관한 문서가 있습니다.http://www.gentoo.at/proj/en/hardened/hardened-toolchain.xml#RELRO.
답변2
실제로 시스템의 어느 부분이 책임이 있는지는 모르지만 실패한 이유는 알 수 있습니다. .dtors
바이너리에 쓰기 가능으로 표시되어 있지만 ( GOT .ctors
및 기타 몇 가지 항목과 함께) 메모리에서 쓰기 불가능한 별도의 페이지에 매핑된 것처럼 보입니다. 내 시스템에는 .dtors
다음이 배치되었습니다 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
실행 파일을 실행하고 확인하면 /proc/PID/maps
다음이 표시됩니다.
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
여전히 자체 페이지에 쓸 수 있지만 0x8049000-0x804a000
다른 페이지에는 쓸 수 없습니다. 나는 이것이 커널의 보안 기능이라고 생각합니다(당신이 말했듯이 "최근 읽기 전용 .dtors, plts에 대한 추세가 있습니다"). 그러나 그것이 정확히 무엇인지는 모르겠습니다(OpenBSD에는너비^X; 리눅스는ParX, 그러나 대부분의 커널에는 내장되어 있지 않음)
mprotect
페이지의 메모리 속성을 변경할 수 있는 :을 사용하면 이를 우회할 수 있습니다 .
mprotect((void*)0x8049000, 4096, PROT_WRITE);
.dtors
이렇게 하면 테스트 프로그램이 충돌하지 않지만 ( 0x8049f18
)의 닫는 표시를 다른 함수의 주소로 덮어쓰려고 하면 함수가 여전히 실행되지 않습니다.
다른 사람이 페이지를 읽기 전용으로 만드는 원인과 페이지를 수정해도 .dtors
내 시스템에서 아무 작업도 수행되지 않는 이유를 알고 있기를 바랍니다.