Uderzam w to głową w ścianę.
W moim projekcie, gdy przydzielam pamięć za mmap
pomocą mapowania ( /proc/self/maps
) pokazuje, że jest to region możliwy do odczytu i wykonywania, mimo że zażądałem tylko pamięci do odczytu.
Po przejrzeniu strace (który wyglądał dobrze) i innym debugowaniu udało mi się zidentyfikować jedyną rzecz, która wydaje się unikać tego dziwnego problemu: usunięcie plików asemblera z projektu i pozostawienie tylko czystego C. (co ?!)
Oto mój dziwny przykład: pracuję nad Ubunbtu 19.04 i domyślnym gcc.
Jeśli kompilujesz docelowy plik wykonywalny z plikiem ASM (który jest pusty), a następnie mmap
zwraca czytelny i wykonywalny region, jeśli kompilujesz bez niego, zachowa się poprawnie. Zobacz dane wyjściowe, /proc/self/maps
które osadziłem w moim przykładzie.
przyklad.c
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main()
{
void* p;
p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
{
FILE *f;
char line[512], s_search[17];
snprintf(s_search,16,"%lx",(long)p);
f = fopen("/proc/self/maps","r");
while (fgets(line,512,f))
{
if (strstr(line,s_search)) fputs(line,stderr);
}
fclose(f);
}
return 0;
}
przyklad.s : jest pusty plik!
Wyjścia
Dzięki dołączonej wersji ASM
VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0
Bez wersji dołączonej do ASM
VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0
-Wa,--noexecstack
.Odpowiedzi:
Linux ma domenę wykonawczą o nazwie
READ_IMPLIES_EXEC
, co powoduje,PROT_READ
że podane są również wszystkie przydzielone stronyPROT_EXEC
. Ten program pokaże, czy jest to włączone dla siebie:Jeśli skompilujesz to wraz z pustym
.s
plikiem, zobaczysz, że jest włączony, ale bez niego zostanie wyłączony. Początkowa wartość tego pochodzi z meta-informacji ELF w twoim pliku binarnym . Zrobićreadelf -Wl example
. Zobaczysz ten wiersz, gdy skompilujesz bez pustego.s
pliku:Ale ten, kiedy się z nim skompilowałeś:
Uwaga
RWE
zamiast po prostuRW
. Powodem tego jest to, że linker zakłada, że pliki zestawu wymagają read-implies-exec, chyba że zostanie wyraźnie powiedziane, że nie, a jeśli jakakolwiek część programu wymaga read-implies-exec, to jest włączona dla całego programu . Pliki zestawu, które kompiluje GCC, informują go, że nie potrzebuje tego w tej linii (zobaczysz to, jeśli kompilujesz-S
):Umieść tę linię
example.s
, a będzie ona służyć do poinformowania linkera, że również jej nie potrzebuje, a twój program będzie wtedy działał zgodnie z oczekiwaniami.źródło
.o
pliki! Ale w każdym razie wydaje mi się, że jest to mechanizm, którygcc -zexecstack
wykorzystuje i dlaczego sprawia, że nie tylko stos, ale wszystko jest wykonywalne.-Wa,--noexecstack
. Myślę, że to bardzo paskudna, ostra krawędź. Cicha utrata stosów NX powinna stanowić lukę w zabezpieczeniach. Ludzie Binutil powinni to naprawić..note.GNU-stack,"",@progbits
wyjaśniono znaczenie / logikę linii - w tej chwili jest nieprzejrzysta, co odpowiada „magicznemu ciągowi znaków powoduje ten efekt”, ale ciąg wygląda na to, że ma jakiś rodzaj semantyka.Alternatywnie do modyfikowania plików asemblacji za pomocą wariantów dyrektywy sekcji specyficznych dla GNU, możesz dodać
-Wa,--noexecstack
do wiersza poleceń budowę plików asemblacji. Na przykład zobacz, jak to robię w Muslconfigure
:https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Uważam, że przynajmniej niektóre wersje clanga ze zintegrowanym asemblerem mogą wymagać przekazania go jako
--noexecstack
(bez-Wa
), więc twój skrypt konfiguracyjny powinien prawdopodobnie sprawdzić oba i zobaczyć, który jest akceptowany.Możesz również użyć
-Wl,-z,noexecstack
w czasie połączenia (wLDFLAGS
), aby uzyskać ten sam wynik. Wadą tego jest to, że nie pomaga, jeśli twój projekt tworzy statyczne.a
pliki bibliotek do użytku przez inne oprogramowanie, ponieważ wtedy nie kontrolujesz opcji czasu łącza, gdy są one używane przez inne programy.źródło