Linux a un domaine d'exécution appelé READ_IMPLIES_EXEC
, ce qui fait que toutes les pages sont allouées avec PROT_READ
à attribuer également PROT_EXEC
. Les anciens noyaux Linux l'utilisaient pour les exécutables qui utilisaient l'équivalent de gcc -z execstack
. Ce programme vous montrera si cela est activé pour lui-même :
#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;
}
Si vous compilez cela avec un .s
vide fichier, vous verrez qu'il est activé, mais sans un, il sera désactivé. La valeur initiale de ceci provient des méta-informations ELF dans votre binaire. Faites readelf -Wl example
. Vous verrez cette ligne lorsque vous aurez compilé sans le .s
vide fichier :
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Mais celui-ci lorsque vous l'avez compilé :
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Remarque RWE
au lieu de simplement RW
. La raison en est que l'éditeur de liens suppose que vos fichiers d'assemblage nécessitent read-implies-exec à moins qu'il ne soit explicitement indiqué qu'ils ne le font pas, et si une partie de votre programme nécessite read-implies-exec, alors il est activé pour l'ensemble de votre programme . Les fichiers d'assemblage compilés par GCC lui indiquent qu'il n'en a pas besoin, avec cette ligne (vous le verrez si vous compilez avec -S
):
.section .note.GNU-stack,"",@progbits
Les autorisations de section par défaut n'incluent pas ex
ec. Voir la partie ELF du .section
documentation pour la signification des "drapeaux" et des @attributs.
(Et n'oubliez pas de passer à une autre section comme .text
ou .data
après cela .section
directive, si votre .s
s'appuyait sur .text
car la section par défaut en haut du fichier.)
Mettez cette ligne dans example.s
(et tous les autres .s
fichier dans votre projet). La présence de ce .note.GNU-stack
servira à dire à l'éditeur de liens que ce fichier objet ne dépend pas d'une pile exécutable, donc l'éditeur de liens utilisera RW
au lieu de RWE
sur le GNU_STACK
métadonnées, et votre programme fonctionnera alors comme prévu.
De même pour NASM, un section
directive avec les bons drapeaux spécifie des piles non exécutables.
Les noyaux Linux modernes entre 5.4 et 5.8 ont changé le comportement du chargeur de programme ELF. Pour x86-64, rien ne s'allume READ_IMPLIES_EXEC
plus. Au plus (avec un RWE GNU_STACK
ajouté par ld
), vous obtiendrez la pile elle-même exécutable, pas toutes les pages lisibles. (Cette réponse couvre le dernier changement, dans 5.8, mais il doit y avoir eu d'autres changements avant cela, puisque cette question montre une exécution réussie du code dans .data
sur x86-64 Linux 5.4)
exec-all
(READ_IMPLIES_EXEC
) ne se produit que pour les anciens exécutables 32 bits où l'éditeur de liens n'a pas ajouté de GNU_STACK
entrée d'en-tête du tout. Mais comme montré ici, le ld
moderne ajoute toujours cela avec un paramètre ou l'autre, même lorsqu'une entrée .o
il manque une note au fichier.
Vous devriez toujours utiliser ce .note
section pour signaler les piles non exécutables dans les programmes normaux. Mais si vous espériez tester le code auto-modifiable en .data
ou suivre un ancien tutoriel pour tester le shellcode, ce n'est pas une option sur les noyaux modernes.
Au lieu de modifier vos fichiers d'assemblage avec des variantes de directive de section spécifiques à GNU, vous pouvez ajouter -Wa,--noexecstack
à votre ligne de commande pour créer des fichiers d'assemblage. Par exemple, voyez comment je le fais dans configure
de musl :
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Je crois qu'au moins certaines versions de clang avec assembleur intégré peuvent nécessiter qu'il soit passé en tant que --noexecstack
(sans le -Wa
), donc votre script de configuration devrait probablement vérifier les deux et voir lequel est accepté.
Vous pouvez également utiliser -Wl,-z,noexecstack
au moment de la liaison (en LDFLAGS
) pour obtenir le même résultat. L'inconvénient est que cela n'aide pas si votre projet produit des statiques (.a
) les fichiers de bibliothèque à utiliser par d'autres logiciels, puisque vous ne contrôlez alors pas les options de temps de liaison lorsqu'elles sont utilisées par d'autres programmes.