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

Why does Linux stack area have executable protection?

在研究Linux 内核时,我刚刚发现,默认情况下,可执行文件有一个'executable' 堆栈区域。我自然认为唯一(必要的)可执行区域是文本部分。有没有什么历史原因,或者实际使用情况?

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

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

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

#include <stdlib.h>
#include <unistd.h>

char shellCode[] =  "\xb8\x01\x00\x00\x00" //          mov    [=10=]x1,%eax
                    "\xbb\x20\x00\x00\x00" //          mov    [=10=]x20,%ebx
                    "\xcd\x80";            //          int [=10=]x80

int main(){
    int *ret;
    ret = (int*) &ret + 2;
    *ret = (int) shellCode;
    return 5;
}

在这段代码中,ret 指向它的地址加 2。正如我们所知,在 IA32 系统中指针大小为 4 字节,并且在每个函数的开头都有一个编译后的 push ebp。因此,为了到达 main 的 return 地址,我们需要添加 2*stack_chunk_size 并通过在编译时将每个块设置为 4 字节,这非常有效。

像这样编译代码进行测试:

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

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

这个简单的代码可以让我们深入了解为什么会有可执行堆栈保护这样的东西。

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

Trampolines

execstack