Estoy golpeando mi cabeza contra la pared con esto.
En mi proyecto, cuando estoy asignando memoria con mmap
el mapeo ( /proc/self/maps
) muestra que es una región legible y ejecutable a pesar de que solo solicité memoria legible.
Después de analizar strace (que se veía bien) y otra depuración, pude identificar lo único que parece evitar este extraño problema: eliminar archivos de ensamblaje del proyecto y dejar solo C. (¡¿qué ?!)
Así que aquí está mi extraño ejemplo, estoy trabajando en Ubunbtu 19.04 y por defecto gcc.
Si compila el ejecutable de destino con el archivo ASM (que está vacío), mmap
devuelve una región legible y ejecutable, si construye sin él se comportará correctamente. Vea el resultado /proc/self/maps
que he incrustado en mi ejemplo.
ejemplo.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;
}
example.s : es un archivo vacío!
Salidas
Con la versión ASM incluida
VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0
Sin la versión incluida de ASM
VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0
-Wa,--noexecstack
.Respuestas:
Linux tiene un dominio de ejecución llamado
READ_IMPLIES_EXEC
, que hacePROT_READ
que también se den todas las páginas asignadas conPROT_EXEC
. Este programa le mostrará si está habilitado para sí mismo:Si compila eso junto con un
.s
archivo vacío , verá que está habilitado, pero sin uno, estará deshabilitado. El valor inicial de esto proviene de la metainformación ELF en su binario . Hacerreadelf -Wl example
. Verá esta línea cuando compiló sin el.s
archivo vacío :Pero este cuando lo compiló:
Nota en
RWE
lugar de soloRW
. La razón de esto es que el enlazador supone que sus archivos de ensamblaje requieren read-implica-exec a menos que se indique explícitamente que no lo hacen, y si alguna parte de su programa requiere read-implica-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 (verá esto si compila con-S
):Pon esa línea
example.s
y servirá para decirle al enlazador que tampoco la necesita, y tu programa funcionará como se espera.fuente
.o
archivos! Pero de todos modos, supongo que este es el mecanismo quegcc -zexecstack
usa, y por qué hace que no solo la pila sino todo sea ejecutable.-Wa,--noexecstack
. Creo que es un filo muy desagradable. La pérdida silenciosa de nx-stacks debería ser una vulnerabilidad de seguridad. La gente de Binutil debería arreglarlo..note.GNU-stack,"",@progbits
se explicara el significado / la lógica de la línea ; en este momento es opaca, equivalente a "esta cadena mágica de caracteres causa este efecto", pero la cadena claramente parece que tiene algún tipo de semántica.Como alternativa a modificar sus archivos de ensamblaje con variantes de directivas de sección específicas de GNU, puede agregar
-Wa,--noexecstack
a su línea de comando para construir archivos de ensamblaje. Por ejemplo, vea cómo lo hago en musl'sconfigure
: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 puede usar
-Wl,-z,noexecstack
en el momento del enlace (inLDFLAGS
) para obtener el mismo resultado. La desventaja de esto es que no ayuda si su proyecto produce.a
archivos de biblioteca estática ( ) para su uso por otro software, ya que entonces no controla las opciones de tiempo de enlace cuando lo utilizan otros programas.fuente