我已下载并编译了Apples源代码并将其添加到Xcode.app/Contents/Developer/usr/bin/include/c++/v1。现在我如何在C中实现?我正在处理的代码来自关于Hackadays shellcode执行者的this帖子。我的代码目前是这样的:
#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”?
答案 0 :(得分:1)
堆栈保护程序与可执行堆栈不同。这引入了金丝雀来检测堆栈何时被破坏。
要获得可执行堆栈,您必须链接说使用可执行堆栈。毫无疑问,这是一个坏主意因为它使攻击变得更容易。
链接器的选项是-allow_stack_execute
,它变成了gcc / clang命令行:
clang -Wl,-allow_stack_execute -fno-stack-protector shell.c
但是,您的代码不会尝试在堆栈上执行代码,但它会尝试更改少量的堆栈内容,尝试完成返回shellcode,这是最常见的{{{ 3}}攻击。
在通常编译的ROP上,这将尝试覆盖所谓的链接区域(这是在函数返回时将调用的下一条指令的地址)。这假定代码未使用-fomit-frame-pointer
进行编译。如果它是用这个选项编译的,那么你实际上是在移动一个额外的地址。
在OSX 64位OSX 32bit environment上,寄存器为64位,所有值都需要long
而不是int
引用,但方式类似。
你所拥有的shellcode实际上是在你代码的数据段中(因为它是char []
,这意味着它是可读/可写的,不可读的 - 可执行的。你需要mmap它(就像nneonneo的答案)或者将它复制到现在可执行的堆栈中,获取它的地址并以这种方式调用它。
然而,如果您只是想让代码运行,那么nneonneo的答案会让它变得非常简单,但是如果您尝试使用exploit-y代码,那么您将不得不做一些更多的工作。由于不可执行的堆栈,新的孩子使用返回库机制,尝试返回调用,例如,一个exec /系统调用与堆栈中的数据。
答案 1 :(得分:0)
有了现代执行保护措施,让shellcode像这样运行有点棘手。请注意,您的代码不会尝试在堆栈上执行代码;相反,它将shellcode的地址存储在堆栈中,实际代码位于程序的数据段中。
你有几个选项可以让它发挥作用:
将shellcode放在实际的可执行部分中,因此它是可执行代码。您可以使用GCC和Clang __attribute__((section("name")))
执行此操作。在OS X上:
const char code[] __attribute__((section("__TEXT,__text"))) = "...";
后跟
((void (*)(void))code)();
效果很好。在Linux上,请改为使用部分名称".text"
。
使用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。