Linux hat eine Ausführungsdomäne namens READ_IMPLIES_EXEC
, was bewirkt, dass alle Seiten mit PROT_READ
zugewiesen werden auch PROT_EXEC
zu geben . Ältere Linux-Kernel verwendeten dies für ausführbare Dateien, die das Äquivalent von gcc -z execstack
verwendeten . Dieses Programm zeigt Ihnen, ob das für sich selbst aktiviert ist:
#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;
}
Wenn Sie das zusammen mit einem leeren .s
kompilieren -Datei sehen Sie, dass sie aktiviert ist, aber ohne eine wird sie deaktiviert. Der Anfangswert davon stammt aus den ELF-Metainformationen in Ihrer Binärdatei. Führen Sie readelf -Wl example
aus . Sie werden diese Zeile sehen, wenn Sie ohne den leeren .s
kompiliert haben Datei:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Aber dieses, als Sie damit kompiliert haben:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Beachten Sie RWE
statt nur RW
. Der Grund dafür ist, dass der Linker davon ausgeht, dass Ihre Assemblydateien read-implies-exec erfordern, es sei denn, es wird ausdrücklich darauf hingewiesen, dass dies nicht der Fall ist, und wenn ein Teil Ihres Programms read-implies-exec erfordert, ist es für Ihr gesamtes Programm aktiviert . Die Assembly-Dateien, die GCC kompiliert, teilen ihm mit dieser Zeile mit, dass dies nicht erforderlich ist (Sie werden dies sehen, wenn Sie mit -S
kompilieren ):
.section .note.GNU-stack,"",@progbits
Die standardmäßigen Abschnittsberechtigungen enthalten nicht ex
ec. Siehe den ELF-Teil des .section
Dokumentation zur Bedeutung der "Flags" und @attributes.
(Und vergessen Sie nicht, zu einem anderen Abschnitt wie .text
zu wechseln oder .data
danach .section
Direktive, wenn Ihr .s
stützte sich auf .text
weil der Standardabschnitt am Anfang der Datei.)
Fügen Sie diese Zeile in example.s
ein (und alle anderen .s
Datei in Ihrem Projekt). Das Vorhandensein dieses .note.GNU-stack
-Abschnitt dient dazu, dem Linker mitzuteilen, dass diese Objektdatei nicht von einem ausführbaren Stack abhängt, sodass der Linker RW
verwendet statt RWE
auf der GNU_STACK
Metadaten, und Ihr Programm wird dann wie erwartet funktionieren.
Ähnlich für NASM, ein section
Direktive mit den richtigen Flags gibt nicht ausführbare Stacks an.
Moderne Linux-Kernel zwischen 5.4 und 5.8 haben das Verhalten geändert des ELF-Programmladers. Für x86-64 wird READ_IMPLIES_EXEC
nicht aktiviert mehr. Höchstens (mit einem RWE GNU_STACK
hinzugefügt von ld
), erhalten Sie den Stapel selbst ausführbar, nicht jede lesbare Seite. (Diese Antwort deckt die letzte Änderung in 5.8 ab, aber es muss vorher andere Änderungen gegeben haben, da diese Frage die erfolgreiche Ausführung des Codes in .data
zeigt auf x86-64 Linux 5.4)
exec-all
(READ_IMPLIES_EXEC
) tritt nur bei älteren ausführbaren 32-Bit-Dateien auf, bei denen der Linker keinen GNU_STACK
hinzugefügt hat Header-Eintrag überhaupt. Aber wie hier gezeigt, modernes ld
fügt das immer mit der einen oder anderen Einstellung hinzu, auch wenn eine Eingabe .o
Datei fehlt ein Hinweis.
Sie sollten trotzdem diesen .note
verwenden -Abschnitt, um nicht ausführbare Stacks in normalen Programmen zu signalisieren. Aber wenn Sie gehofft haben, selbstmodifizierenden Code in .data
zu testen oder einem alten Tutorial zum Testen von Shellcode zu folgen, das ist keine Option für moderne Kernel.
Als Alternative zum Modifizieren Ihrer Assembly-Dateien mit GNU-spezifischen Abschnittsdirektiven-Varianten können Sie -Wa,--noexecstack
hinzufügen zu Ihrer Befehlszeile zum Erstellen von Assemblydateien. Sehen Sie sich zum Beispiel an, wie ich es in configure
von musl mache :
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Ich glaube, dass zumindest einige Versionen von Clang mit integriertem Assembler erfordern, dass es als --noexecstack
übergeben wird (ohne den -Wa
), also sollte Ihr Konfigurationsskript wahrscheinlich beide überprüfen und sehen, welches akzeptiert wird.
Sie können auch -Wl,-z,noexecstack
verwenden zur Verbindungszeit (in LDFLAGS
) um das gleiche Ergebnis zu erhalten. Der Nachteil davon ist, dass es nicht hilft, wenn Ihr Projekt statische (.a
)-Bibliotheksdateien für die Verwendung durch andere Software, da Sie dann die Verbindungszeitoptionen nicht kontrollieren, wenn sie von anderen Programmen verwendet werden.