gcc execstack标志究竟允许在什么情况下使用以及如何执行呢?

时间:2018-11-16 22:31:29

标签: c gcc x86

我这里有一些示例代码,用于理解初学者CTF的某些C行为:

// example.c

#include <stdio.h>


void main() {
        void (*print)();

        print = getenv("EGG");
        print();
}

编译:{{1​​}}

用法:gcc -z execstack -g -m32 -o example example.c

如果我使用EGG=$(echo -ne '\x90\xc3) ./example标志编译代码,则该程序将执行我上面注入的操作码。没有该标志,该程序将由于分段错误而崩溃。

这到底是为什么?是因为execstack在堆栈上存储了实际的操作码,而execstack标志允许跳转到堆栈吗?还是getenv将指针推到堆栈上,还有其他一些规则可以执行哪些内存部分?我阅读了联机帮助页,但无法确切了解规则是什么以及如何执行这些规则。

另一个问题是,我认为我也确实缺少在调试时可视化内存的好工具,因此很难弄清楚这一点。任何建议将不胜感激。

1 个答案:

答案 0 :(得分:6)

getenv不会在堆栈中存储 env var的值。从进程启动开始,它已经在堆栈中 ,并且getenv获得指向它的指针。

请参见i386 System V ABI对argv []和envp []在进程启动时位于何处的描述:[esp]上。

_start不会在调用main之前复制它们,只是计算指向它们的指针作为args传递给main。 (链接到https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI的最新版本,其中保留了当前的官方版本。)


您的代码正在将指向堆栈内存的指针(包含env var的值)转换为函数指针并对其进行调用。查看编译器生成的asm(例如,在https://godbolt.org/上):它将类似于call getenv / call eax

-zexecstack使您所有的页面都可执行,而不仅仅是堆栈。它也适用于.data.bss.rodata部分,以及用malloc / new分配的内存。

在2018年底左右之前,使用binutils的GNU / Linux ld.rodata链接到与.text相同的ELF段中,因此const char code[] = {0xc3}或字符串文字是可执行文件。

当前的ld赋予.rodata自己的段,该段被映射为读取而没有exec,因此,除非您使用-zexecstack,否则不再可能在数据中查找ROP / Spectre“小工具”。 >

手动使用mmapmprotect仍会给您不可执行的页面; -zexecstack影响ELF标头。 Glibc malloc使用brk进行少量分配,因此它扩展了现有的可写映射。大型分配(它使用mmap)可能不是可执行的,除非-zexecstack也改变了在main之前调用glibc初始化函数的方式。

相关问题