Linux má spouštěcí doménu s názvem READ_IMPLIES_EXEC
, což způsobí, že všechny stránky budou přiřazeny PROT_READ
má být také uvedeno PROT_EXEC
. Starší jádra Linuxu to používala pro spustitelné soubory, které používaly ekvivalent gcc -z execstack
. Tento program vám ukáže, zda je tato funkce povolena:
#include <stdio.h>
#include <sys/personality.h>
int main(void) {
printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
return 0;
}
Pokud to zkompilujete spolu s prázdným .s
soubor, uvidíte, že je povoleno, ale bez něj bude zakázáno. Počáteční hodnota pochází z metainformací ELF ve vašem binárním souboru. Proveďte readelf -Wl example
. Tento řádek uvidíte, když zkompilujete bez prázdného .s
soubor:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Ale tohle, když jste s ním kompilovali:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Poznámka RWE
místo pouze RW
. Důvodem je to, že linker předpokládá, že vaše soubory sestavení vyžadují read-implies-exec, pokud není výslovně řečeno, že ne, a pokud některá část vašeho programu vyžaduje read-implies-exec, pak je povolena pro celý váš program. . Soubory sestavení, které GCC zkompiluje, mu tímto řádkem říkají, že to nepotřebuje (uvidíte to, pokud kompilujete s -S
):
.section .note.GNU-stack,"",@progbits
Výchozí oprávnění sekce nezahrnují ex
ec. Viz část ELF .section
dokumentaci pro význam „vlajek“ a @attributes.
(A nezapomeňte přepnout na jinou sekci, například .text
nebo .data
poté .section
direktivu .s
spoléhal na .text
protože výchozí sekce v horní části souboru.)
Vložte tento řádek do example.s
(a každý další .s
soubor ve vašem projektu). Přítomnost tohoto .note.GNU-stack
sekce bude sloužit k tomu, aby sdělil linkeru, že tento objektový soubor nezávisí na spustitelném zásobníku, takže linker použije RW
místo RWE
na GNU_STACK
metadata a váš program pak bude fungovat podle očekávání.
Podobně pro NASM, section
direktiva se správnými příznaky určuje nespustitelné zásobníky.
Moderní linuxová jádra mezi 5.4 a 5.8 změnila chování zavaděče programu ELF. Pro x86-64 se nic nezapne READ_IMPLIES_EXEC
už Maximálně (s RWE GNU_STACK
přidal ld
), získáte spustitelný samotný zásobník, ne každou čitelnou stránku. (Tato odpověď pokrývá poslední změnu v 5.8, ale před tím musely být jiné změny, protože tato otázka ukazuje úspěšné provedení kódu v .data
na x86-64 Linux 5.4)
exec-all
(READ_IMPLIES_EXEC
) dochází pouze u starších 32bitových spustitelných souborů, do kterých linker nepřidal GNU_STACK
záhlaví vůbec. Ale jak je zde ukázáno, moderní ld
vždy přidá, že s jedním nebo druhým nastavením, i když je vstup .o
v souboru chybí poznámka.
Měli byste stále používat toto .note
sekce pro signalizaci nespustitelných zásobníků v normálních programech. Ale pokud jste doufali, že otestujete samomodifikační kód v .data
nebo následovat nějaký starý tutoriál pro testování shell kódu, to není na moderních jádrech možnost.
Jako alternativu k úpravě souborů sestavení pomocí variant direktiv sekce specifických pro GNU můžete přidat -Wa,--noexecstack
do příkazového řádku pro vytváření souborů sestavy. Podívejte se například, jak to dělám v muslově configure
:
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Věřím, že alespoň některé verze clang s integrovaným-assemblerem mohou vyžadovat, aby byl předán jako --noexecstack
(bez -Wa
), takže váš konfigurační skript by měl pravděpodobně zkontrolovat obojí a zjistit, která je přijata.
Můžete také použít -Wl,-z,noexecstack
v čase odkazu (v LDFLAGS
), abyste dosáhli stejného výsledku. Nevýhodou tohoto je, že nepomůže, pokud váš projekt vytváří statický náboj (.a
) soubory knihovny pro použití jiným softwarem, protože pak neřídíte možnosti doby propojení, když je používán jinými programy.