在研究Linux内核时,我发现,默认情况下,可执行文件具有可执行文件'堆栈区域。我自然认为唯一(必要的)可执行区域是文本部分。是否有任何与此相关的历史原因或任何实际用法?
答案 0 :(得分:6)
在某些情况下,可执行堆栈是必需的,例如嵌套函数的GCC trampoline。
trampoline是在运行时创建嵌套函数地址时创建的一小段代码。它通常驻留在堆栈中,包含函数的堆栈框架中。这些宏告诉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
并通过在编译时设置每个块4byte,这非常有效。
编译这样的代码来测试:
gcc -mpreferred-stack-boundary=2 -z execstack -o testShellCode testShellCode.c
-mpreferred-stack-boundary=2
是将堆栈放在4个字节的块中,-z execstack
是使用可执行堆栈进行编译。
这个简单的代码可以让我们深入了解为什么存在可执行堆栈保护这样的事情。
查看以下链接,了解有关嵌套函数和execstack
命令的更多信息: