.dtors wygląda na zapisywalne, ale próbuje napisać segfault

9

To jest Ubuntu 9.04, 2.6.28-11-server, 32bit 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
$

Dla niewtajemniczonych: gcc tworzy segment destruktora .dtorsw pliku wykonywalnym elfa, który jest wywoływany po main()wyjściu. Ta tabela od dawna jest zapisywalna i wygląda na to, że powinna być w moim przypadku (patrz readelfdane wyjściowe). Ale próba zapisu do tabeli powoduje awarię.

Zdaję sobie sprawę, że ostatnio nastąpił ruch w kierunku readonly .dtors, plt, ale nie rozumiem, co to jest rozbieżność między readelfsegfault.

Fixee
źródło
Prawdziwe pytanie brzmi: dlaczego chcesz, żeby było zapisywalne?
alex
1
Uczę klasy bezpieczeństwa, która polega na łamaniu serii podatnych programów, ale jedno ćwiczenie obejmuje pisanie do .dtors w celu wykonania kodu powłoki. To już nie działa i próbuję wyśledzić problem.
Fixee
Niedopasowanie jest spowodowane tym, że prawdopodobnie istnieją pewne przeniesienia danych (które muszą zostać naprawione przed oznaczeniem tylko do odczytu i i tak nie mogą być leniwe, więc będą stałe po naprawieniu).
ninjalj

Odpowiedzi:

5

Sekcje te są oznaczone GNU_RELRO (relokacje tylko do odczytu), co oznacza, że ​​jak tylko dynamiczny moduł ładujący naprawi się (w czasie ładowania nie ma tam leniwych relokacji) wszystkie relokacje, oznacza to, że sekcje są tylko do odczytu. Pamiętaj, że większość z nich .got.pltznajduje się na innej stronie, więc nie otrzymujesz leczenia.

Możesz zobaczyć skrypt linkera ld --verbose, jeśli szukasz RELRO, znajdziesz coś podobnego do:

.got            : { *(.got) }
. = DATA_SEGMENT_RELRO_END (12, .);
.got.plt        : { *(.got.plt) }

co oznacza, że ​​sekcje RELRO kończą 12 bajtów na .got.plt(wskaźniki funkcji dynamicznego linkera są już rozwiązane, więc można je oznaczyć jako tylko do odczytu).

Projekt hartowanego Gentoo zawiera dokumentację dotyczącą RELRO pod adresem http://www.gentoo.at/proj/en/hardened/hardened-toolchain.xml#RELRO .

ninjalj
źródło
5

Mogę powiedzieć, dlaczego zawodzi, chociaż tak naprawdę nie wiem, która część systemu jest odpowiedzialna. Chociaż .dtorsjest zapisany w pliku binarnym, wygląda na to, że (wraz z .ctorsGOT i kilkoma innymi rzeczami) jest mapowany na osobną, nie zapisywalną stronę w pamięci. W moim systemie .dtorsustawia się na 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

Jeśli uruchomię plik wykonywalny i sprawdzę /proc/PID/maps, zobaczę:

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/ .bsssą nadal zapisywalne na własnej stronie, ale inni na 0x8049000-0x804a000nich nie. Zakładam, że jest to funkcja bezpieczeństwa w jądrze (jak powiedziałeś: „nastąpił ruch w kierunku readonly .dtors, plt, ostatnio”), ale nie wiem dokładnie, jak się nazywa (OpenBSD ma coś bardzo podobnego o nazwie W ^ X ; Linux ma PaX , ale nie jest wbudowany w większość jąder)

Możesz obejść to za pomocą mprotect, co pozwala zmienić atrybuty strony w pamięci:

mprotect((void*)0x8049000, 4096, PROT_WRITE);

Dzięki temu mój program testowy nie ulega awarii, ale jeśli spróbuję zastąpić końcową wartę .dtors( 0x8049f18) adresem innej funkcji, funkcja ta nadal się nie uruchamia; tej części, której nie mogę zrozumieć.

Mam nadzieję, że ktoś inny wie, co jest odpowiedzialne za wykonanie strony tylko do odczytu i dlaczego modyfikowanie .dtorswydaje się nie robić nic w moim systemie

Michał Mrożek
źródło
3
Jeśli OP Linux z PaX mprotectnie może sprawić, by strona wykonywalna była zapisywalna lub uczynić stronę wykonywalną, która była wcześniej zapisywalna, chyba że ta funkcja zostanie wyłączona za pomocą paxctl -m.
stribika
@stribika Ach, dobrze wiedzieć
Michael Mrozek