跳转到数据段

5

我正在测试一个汇编器,该汇编器生成X86指令。我想做类似于这样的事情来测试指令是否起作用。

#include<stdio.h>

unsigned char code[2] = {0xc9, 0xc3};

int main() {
  void (*foo)();
  foo = &code;
  foo();
  return 0;
}

然而,由于DEP的原因,OS X似乎在阻止这种操作。是否有办法(a)为该程序禁用DEP或(b)以其他格式输入字节,以便我可以跳转到它们。


在Linux上,您可以使用mprotect(2)来启用PROT_EXEC。看起来这也是在[OSX](http://developer.apple.com/library/mac/#documentation/darwin/reference/manpages/man2/mprotect.2.html)上的正确方法,但我不是专家。 - user786653
关于这个问题,有几个库可能对你有用。GNU lightning、libjit和http://code.google.com/p/asmjit/都是不错的选择。 - Basile Starynkevitch
就像@user786653所说的,mprotect可能会起作用,但是记得阅读手册页面,因为对于一些平台,页面需要使用'mmap'进行分配。 - Some programmer dude
2个回答

6
如果您只需要测试,请尝试使用这个,它是“魔法”...
const unsigned char code[2] = {0xc9, 0xc3};
^^^^^

const关键字会导致编译器将其放置在const段中(警告!这是一个实现细节!),该段与text段在同一段中。整个段应该是可执行的。使用以下方式可能更加可移植:

__attribute__((section("text"))
const unsigned char code[2] = {0xc9, 0xc3};

您可以始终在汇编文件中完成它,

    .text
    .globl code
code:
    .byte 0xc9
    .byte 0xc3

然而:如果你想在运行时修改代码,你需要使用mprotect。默认情况下,内存中没有同时具有写入和执行权限的映射。

下面是一个例子:

#include <stdlib.h>
#include <sys/mman.h>
#include <err.h>
#include <stdint.h>

int main(int argc, char *argv[])
{
    unsigned char *p = malloc(4);
    int r;
    // This is x86_64 code
    p[0] = 0x8d;
    p[1] = 0x47;
    p[2] = 0x01;
    p[3] = 0xc3;
    // This is hackish, and in production you should do better.
    // Casting 4095 to uintptr_t is actually necessary on 64-bit.
    r = mprotect((void *) ((uintptr_t) p & ~(uintptr_t) 4095), 4096,
                 PROT_READ | PROT_WRITE | PROT_EXEC);
    if (r)
        err(1, "mprotect");
    // f(x) = x + 1
    int (*f)(int) = (int (*)(int)) p;
    return f(1);
}

mprotect 规范规定,如果内存最初没有使用 mmap 进行映射,则其行为是未定义的,但是您正在进行测试而不是发布,所以请知道在 OS X 上它可以正常工作,因为 OS X 的 malloc 在幕后使用 mmap(我认为是排他性的)。


0

不知道你在OSX上的DEP情况,但另一件事情是你可以使用malloc()分配内存来写代码,然后跳转到这个malloc分配的区域。至少在Linux上,这段内存不会被执行保护(实际上这就是JIT通常使用的技巧)。


不,malloc返回的内存在大多数Linux系统上是不可执行的。 - Dietrich Epp
1
你的处理器是否支持NX位(grep /proc/cpuinfo nx)? 上一次我使用的是没有NX的系统。 - Dietrich Epp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接