在C中执行数据作为代码

时间:2015-01-03 17:44:33

标签: c undefined-behavior

使用this answer(和this follow-up)作为灵感我正在研究如何在C中进行一些函数式编程(对此网站已经有很多有趣的讨论)。我想知道的是,如何以及何时可以使用链接代码中采用的方法,将字符串转换为函数指针并执行它。例如在我的机器上(OSX 10.10,Darwin 14.0.0,GCC 4.8.3)我可以编译并运行

int eax = ((int(*)())("\xc3 <- This returns the value of the EAX register"))();

(总是返回0,这是我所期望的,如果程序什么也不做)但是

#include <stdio.h>

int main() {
  const char* lol = "\x8b\x5c\x24\x4\x3d\xe8\x3\x0\x0\x7e\x2\x31\xc0\x83\xf8\x64\x7d\x6\x40\x53\xff\xd3\x5b\xc3\xc3 <- Recursively calls the function at address lol.";
  int i = ((int(*)())(lol))(lol);
  printf("i: %d\n",i);
  return 0;
}

段错误。另一方面,键盘成功运行第二个示例giving the correct answer i: 100

什么时候可以从字符串执行?有没有办法让它(相对)一致?

(我可以合理地猜测这是不确定的行为,我知道我会通过使用它来增加全球失业率。)

2 个答案:

答案 0 :(得分:4)

当然(合法地)undefined behavior,实际上它是特定于实现的。

你需要做好几件事才能成功执行。

  • 首先,您需要文字字符串中的机器代码才是正确的。这显然是处理器和ABI特定的。但我相信你。
  • 然后,您依赖于用于调用函数指针的协议,即根据ABI规范。
  • 最后,在几个处理器(特别是x86-64)上,您需要将机器代码放在某个可执行段中。我想通常情况并非如此(但可能是特定于操作系统的)。详细了解NX bitASLR(以及PIC)。有时可以规避这种情况,例如:适当地mmap - 使用执行权限的某个段并在那里复制机器代码。

BTW,您可能会对JIT compilation技术和库感兴趣(libjitlightningasmjitLLVM ...)

DCoder所述,请详细了解shellcode&amp;更一般地说code injection

更可移植的方法可能(就像我在MELT中所做的那样)动态生成一些C(或C ++)代码,将代码编译成共享对象,然后dlopen - 共享对象(&amp; dlsym - 适当)。

答案 1 :(得分:0)

通常来说,Linux和OSX中字符串文字的内容存储在一个只读段中,该段也恰好是可执行的(在Windows或其他平台上不一定是这种情况)。这就是为什么您可以做类似的事情

(L"\xfeeb")();
在x86和x86_64 Linux和OSX上

并没有出现编译器错误。但是,如果您在字符串文字中输入的机器语言指令不符合应根据操作系统和硬件平台构造功能的方式的要求,则可能会遇到段错误。在Linux Aarch64上运行的可执行字符串文字可能无法在x86_64的OSX上运行,反之亦然。

如果您想探索可执行计算机代码的编程生成方式,则可以(在POSIX上)使用mmap()函数分配可执行内存区域,将代码放在此处并进行实验以你内心的满足感。

有时,您可能会在disassemble <addr>,+<range>中找到gdb,在disassemble --start-address <addr> --end-address <addr>中找到lldb