【问题标题】:Trying to implement enable_execute_stack (Mac OS X)尝试实现 enable_execute_stack (Mac OS X)
【发布时间】:2015-06-24 04:46:37
【问题描述】:

我已经下载并编译了 Apple 的源代码并将其添加到 Xcode.app/Contents/Developer/usr/bin/include/c++/v1。现在我该如何在 C 中实现呢?我正在使用的代码来自this 关于 Hackadays shellcode 执行器的帖子。我的代码目前是这样的:

#include <stdio.h>
#include <stdlib.h>

unsigned char shellcode[] = "\x31\xFA......";

int main()
{
  int *ret;
  ret = (int *)&ret + 2;
  (*ret) = (int)shellcode;
  printf("2\n");
}

我都编译过:

gcc -fno-stack-protector shell.c
clang -fno-stack-protector shell.c

我想我的最后一个问题是,我如何告诉编译器实现“__enable_execute_stack”?

【问题讨论】:

    标签: c macos


    【解决方案1】:

    堆栈保护器不同于可执行堆栈。这引入了金丝雀来检测堆栈何时损坏。

    要获得可执行堆栈,您必须链接说使用可执行堆栈。不用说,这是一个坏主意,因为它使攻击更容易。

    链接器的选项是-allow_stack_execute,变成gcc/clang命令行:

    clang -Wl,-allow_stack_execute -fno-stack-protector shell.c
    

    但是,您的代码不会尝试在堆栈上执行代码,而是会尝试更改少量堆栈内容,尝试完成对 shellcode 的返回,这是最常见的@987654321 之一@攻击。

    在通常编译的OSX 32bit environment 上,这将试图覆盖所谓的链接区域(这是函数返回时将调用的下一条指令的地址)。这假定代码不是用-fomit-frame-pointer 编译的。如果它是用这个选项编译的,那么你实际上是向上移动了一个额外的地址。

    在 OSX 64 位 it uses the 64bit ABI 上,寄存器是 64 位的,所有值都需要由 long 而不是 int 引用,但方式类似。

    但是,您所获得的 shellcode 实际上位于代码的数据段中(因为它是 char [],这意味着它是可读/可写的,而不是可读可执行的。您需要将其映射到 (就像 nneonneo 的回答一样)或将其复制到现在可执行的堆栈中,获取它的地址并以这种方式调用它。

    但是,如果您只是想让代码运行,那么 nneonneo 的回答会很容易,但如果您尝试使用漏洞代码,那么您将不得不做一些事情更多的工作。由于不可执行的堆栈,新的孩子使用返回库机制,试图返回调用,例如,使用堆栈中的数据进行的 exec/system 调用之一。

    【讨论】:

    • 值得注意的是,发帖人正在试图通过覆盖保存的 EIP 来破坏他自己的堆栈。 (这是否有效取决于优化设置、框架布局、堆栈对齐以及可能的其他因素)。因此,关闭堆栈保护可能是必要的。 (虽然,由于main 缺少堆栈缓冲区,它可能根本没有堆栈保护器)。
    【解决方案2】:

    有了现代执行保护,让 shellcode 像这样运行有点棘手。请注意,您的代码不会尝试在堆栈上执行代码;而是将shellcode的地址存放在栈上,实际代码在程序的数据段中。

    您有几个选项可以让它发挥作用:

    1. 把shellcode放在一个实际的可执行部分,所以它是可执行代码。您可以使用 GCC 和 Clang 使用 __attribute__((section("name"))) 来执行此操作。在 OS X 上:

      const char code[] __attribute__((section("__TEXT,__text"))) = "...";
      

      后跟一个

      ((void (*)(void))code)();
      

      效果很好。在 Linux 上,请改用部分名称 ".text"。

    2. 使用mmap创建内存的读写部分,复制你的shellcode,然后mprotect让它具有读-执行权限,然后执行它。这就是现代 JIT 执行动态生成代码的方式。一个例子:

      #include <sys/mman.h>
      
      void execute_code(const void *code, size_t codesize) {
          size_t pagesize = (codesize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
          void *chunk = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
          if(chunk == MAP_FAILED) return;
          memcpy(chunk, code, codesize);
          mprotect(chunk, pagesize, PROT_READ|PROT_EXEC);
          ((void (*)(void)chunk)();
          munmap(chunk, pagesize);
      }
      

    这些方法都不需要您指定任何特殊的编译器标志才能正常工作,而且它们都不需要摆弄堆栈上保存的 EIP。

    【讨论】:

    • 出于某种原因 ((void (*)(void))chunk/code)();使用任一解决方案都会导致段错误...
    猜你喜欢
    • 1970-01-01
    • 2010-11-04
    • 2016-04-14
    • 1970-01-01
    • 2013-01-21
    • 1970-01-01
    • 2012-10-13
    • 1970-01-01
    • 2011-01-01
    相关资源
    最近更新 更多