为什么execstack需要在堆上执行代码?
我编写了下面的代码来测试安全类中的赋值的shellcode(用于取消链接/tmp/passwd
)。
当我使用gcc -o test -g test.c
编译时,我在跳转到shellcode时得到了一个段错误。
当我使用execstack -s test
对二进制文件进行后处理时,我不再获得段错误并且shellcode正确执行,删除了/tmp/passwd
。
我正在运行gcc 4.7.2
。 为了使堆可执行,似乎要求堆栈是可执行的似乎是个坏主意,因为后者比前者有更多合法的用例。
这是预期的行为吗? 如果是这样,理由是什么?
#include #include char* shellcode; int main(){ shellcode = malloc(67); FILE* code = fopen("shellcode.bin", "rb"); fread(shellcode, 1, 67, code); int (*fp)(void) = (int (*) (void)) shellcode; fp(); }
这是xxd shellcode.bin
的输出:
0000000: eb28 5e89 760c 31c0 8846 0bfe c0fe c0fe .(^.v.1..F...... 0000010: c0fe c0fe c0fe c0fe c0fe c0fe c0fe c089 ................ 0000020: f3cd 8031 db89 d840 cd80 e8d3 ffff ff2f ...1...@......./ 0000030: 746d 702f 7061 7373 7764 tmp/passwd
真正的“意外”行为是设置标志使堆可执行以及堆栈。 该标志用于生成基于堆栈的thunk的可执行文件(例如,当您获取嵌套函数的地址时使用gcc),并且不应该真正影响堆。 但Linux通过全局使所有可读页面可执行来实现这一点。
如果你想要更细粒度的控制,你可以改为使用mprotect
系统调用来控制每页的可执行权限 – 添加如下代码:
uintptr_t pagesize = sysconf(_SC_PAGE_SIZE); #define PAGE_START(P) ((uintptr_t)(P) & ~(pagesize-1)) #define PAGE_END(P) (((uintptr_t)(P) + pagesize - 1) & ~(pagesize-1)) mprotect((void *)PAGE_START(shellcode), PAGE_END(shellcode+67) - PAGE_START(shellcode), PROT_READ|PROT_WRITE|PROT_EXEC);
这是预期的行为吗?
看一下Linux内核代码,我认为这个标志的内核内部名称是“read implies exec”。 是的,我认为这是预期的。
为了使堆可执行,似乎要求堆栈是可执行的似乎是个坏主意,因为后者比前者有更多合法的用例。
为什么需要完整的堆可执行? 如果您确实需要动态生成机器代码并运行它,则可以使用mmap
系统调用显式分配可执行内存。
理由是什么?
我认为这个想法是这个标志可以用于遗留程序,期望所有可读的东西都是可执行的。 那些程序可能会尝试在堆栈上运行东西,他们可能会尝试在堆上运行东西,所以这一切都是允许的。