Linux tiene un dominio de ejecución llamado READ_IMPLIES_EXEC
, lo que hace que todas las páginas se asignen con PROT_READ
también se le dará PROT_EXEC
. Los kernels de Linux más antiguos solían usar esto para ejecutables que usaban el equivalente de gcc -z execstack
. Este programa le mostrará si eso está habilitado por sí mismo:
#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 compila eso junto con un .s
vacío archivo, verá que está habilitado, pero sin uno, estará deshabilitado. El valor inicial de esto proviene de la metainformación de ELF en su binario. Haz readelf -Wl example
. Verás esta línea cuando compilaste sin el .s
vacío archivo:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Pero este cuando compilaste con él:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Nota RWE
en lugar de solo RW
. La razón de esto es que el enlazador asume que sus archivos de ensamblaje requieren read-imlies-exec a menos que se le indique explícitamente que no lo hacen, y si alguna parte de su programa requiere read-imlies-exec, entonces está habilitado para todo su programa. . Los archivos de ensamblaje que compila GCC le dicen que no necesita esto, con esta línea (lo verá si compila con -S
):
.section .note.GNU-stack,"",@progbits
Los permisos de sección predeterminados no incluyen ex
CE. Ver la parte ELF del .section
documentación sobre el significado de las "banderas" y @atributos.
(Y no olvide cambiar a otra sección como .text
o .data
después de eso .section
directiva, si su .s
confiaba en .text
porque la sección predeterminada en la parte superior del archivo.)
Pon esa línea en example.s
(y cualquier otro .s
archivo en su proyecto). La presencia de ese .note.GNU-stack
servirá para decirle al enlazador que este archivo de objeto no depende de una pila ejecutable, por lo que el enlazador usará RW
en lugar de RWE
en el GNU_STACK
metadatos, y su programa funcionará como se esperaba.
De manera similar para NASM, un section
directiva con las banderas correctas especifica pilas no ejecutables.
Los kernels de Linux modernos entre 5.4 y 5.8 cambiaron el comportamiento del cargador de programas ELF. Para x86-64, nada enciende READ_IMPLIES_EXEC
más. Como máximo (con un RWE GNU_STACK
añadido por ld
), obtendrá que la pila en sí sea ejecutable, no todas las páginas legibles. (Esta respuesta cubre el último cambio, en 5.8, pero debe haber habido otros cambios antes de eso, ya que esa pregunta muestra la ejecución exitosa del código en .data
en x86-64 Linux 5.4)
exec-all
(READ_IMPLIES_EXEC
) solo ocurre con ejecutables heredados de 32 bits donde el enlazador no agregó un GNU_STACK
entrada de encabezado en absoluto. Pero como se muestra aquí, ld
moderno siempre agrega eso con una configuración u otra, incluso cuando una entrada .o
al archivo le falta una nota.
Deberías seguir usando este .note
sección para señalar pilas no ejecutables en programas normales. Pero si esperaba probar el código automodificable en .data
o siguiendo algún tutorial antiguo para probar Shellcode, esa no es una opción en los núcleos modernos.
Como alternativa a la modificación de sus archivos ensamblados con variantes de directivas de sección específicas de GNU, puede agregar -Wa,--noexecstack
a su línea de comando para crear archivos de ensamblaje. Por ejemplo, vea cómo lo hago en configure
de musl :
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Creo que al menos algunas versiones de clang con ensamblador integrado pueden requerir que se pase como --noexecstack
(sin el -Wa
), por lo que su script de configuración probablemente debería verificar ambos y ver cuál es aceptado.
También puedes usar -Wl,-z,noexecstack
en el momento del enlace (en LDFLAGS
) para obtener el mismo resultado. La desventaja de esto es que no ayuda si su proyecto produce estática (.a
) archivos de biblioteca para que los use otro software, ya que no controla las opciones de tiempo de enlace cuando otros programas lo usan.