数据段和栈段为什么可以执行?

22

我刚刚注意到我的简单程序的数据和栈段是可执行的。

我在/proc/[pid]/maps中看到了它,并且使用简单代码确认了它。

例如:

; prog.asm
section .data
    code:   db 0xCC    ;int3

section .text
global _start
_start:
    jmp    code

    mov    rax, 60    ; sys_exit
    mov    rdi, 0
    syscall
然后
nasm -f elf64 prog.asm
ld -o prog prog.o
./prog

导致程序执行int3指令。

C语言编写并使用gcc构建的程序具有不可执行的数据、栈和堆,因此为什么用汇编语言编写的程序会表现出不同的行为呢?

1个回答

24

在现代Linux系统中,链接器只有当参与链接的所有对象都有一个特殊的“标记”部分.note.GNU-stack时,才会将栈/数据标记为不可执行。

如果你将例如int foo() { return 1; }编译成汇编代码(使用gcc -S foo.c),你会看到这个:

    .section    .note.GNU-stack,"",@progbits

对于nasm,语法在手册的第8.9.2节中有展示;你需要像这样的内容:

 section .note.GNU-stack noalloc noexec nowrite progbits

注意

对于每个放入可执行文件中的.o文件都必须执行此操作。如果任何目标文件需要可执行堆栈或数据,则为整个段设置。


可以了,谢谢。顺便说一下,我发现这个链接:link,在第7.9.2节“elf扩展到SECTION指令”中有一个声明,即默认情况下,.data部分没有noexec标志。但是,即使在代码中显式地写入noexec和.section .data,也无法正常工作。很奇怪。 - witosx
1
你能提供任何记录GNU_STACK行为的来源吗?我看到你可以通过向ld传递-z noexecstack来使其具有相同的行为...但这不是一个加载器问题吗?当明确标记为noexec时,加载器将.data节映射为可执行文件。 - Marco
2
@Marco:更多信息请参见当在项目中包含汇编文件时,mmap会出现意外的exec权限,其中包括Linux内核源代码的链接。 - Peter Cordes
谢谢。这提供了一些清晰度。我认为我理解了它的基本原理:旧的 ELF(在现代架构中 NX 位之前),可能依赖于具有可执行数据,因为没有办法禁用它。因此,为了检测“较新”的 ELF,加载器使用可执行堆栈功能。遗憾的是,nasm 没有一个标志可以自动添加它。 - Marco
2
请注意,Linux内核的行为最近发生了变化。在较新的内核中,使用nasm编译的部分默认情况下将不再在x86_64上可执行。请参见提交记录 - Marco

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接