为什么Linux的堆栈区有可执行的保护?

在研究Linux内核时,我发现默认情况下,可执行文件有一个“可执行的”堆栈区域。 我自然认为唯一的(必要的)可执行区域是文本部分。 有没有与此有关的历史原因,或有任何实际用法?

在一些情况下,可执行堆栈是必要的,例如GCC蹦床嵌套函数。

蹦床是在运行时创建嵌套函数的地址时创建的一小段代码。 它通常驻留在堆栈中,位于包含函数的堆栈框架中。 这些宏告诉GCC如何生成代码来分配和初始化一个蹦床。

在大多数发行版中,由于攻击风险和使用堆栈来执行shellcode,此功能是禁用的,尽管您可以使用-z execstack编译代码来启用它。 在编译程序之后,也可以使用名为execstack的程序来启用或禁用此功能。 为了清楚起见,我编写了一个简单的程序来执行exit系统调用,退出代码为32.如果启用此功能,代码将起作用,否则会出现分段错误。

 #include <stdlib.h> #include <unistd.h> char shellCode[] = "\xb8\x01\x00\x00\x00" // mov $0x1,%eax "\xbb\x20\x00\x00\x00" // mov $0x20,%ebx "\xcd\x80"; // int $0x80 int main(){ int *ret; ret = (int*) &ret + 2; *ret = (int) shellCode; return 5; } 

在这个代码中, ret指向它的地址加上2.我们知道在IA32系统中指针的大小是4字节,在每个函数的开头都有一个编译后的push ebp 。 所以为了达到main返回地址,我们需要添加2*stack_chunk_size并且在编译时设置每个chunk 2*stack_chunk_size ,这个工作就完美了。

编译这样的代码来测试:

 gcc -mpreferred-stack-boundary=2 -z execstack -o testShellCode testShellCode.c 

-mpreferred-stack-boundary=2是以4个字节块对齐堆栈, -z execstack是使用可执行堆栈进行编译。

这个简单的代码可以让人深入了解可执行堆栈保护之类的原因。

查找以下链接以获取有关嵌套函数和execstack命令的更多信息:

蹦床

execstack