[英]how do you intercept the address of an instruction that is writing to a segment of memory?
[英]How can you mark a segment of memory as executable in C?
我最近在研究一些 JIT 编译器。 据我所知,JIT 是一种将一些脚本语言代码即时(在执行之前)编译为本机代码的技术。 当我想象这样一个编译器的内部时,我发现生成的本机代码所在的地方一定有一段动态分配的缓冲区。 但是我们需要一种方法来从保存数据的缓冲区中开始运行代码。 我的意思是,您不能只是将一些代码放入char[]
,然后因为安全隐患而跳入执行,为此操作系统必须阻止您这样做。 必须有某种方式将缓冲区标记为可执行文件。 考虑以下幼稚的方法:
#include <stdlib.h>
void *jit_some_native_code(void) {
void *code_segment = malloc(1024);
/*
* bla bla bla...
* Generate code into this code_segment.
*/
return code_segment;
}
int main(void) {
void *code = jit_some_native_code();
/*
* How can I start executing instruction in code?
*/
typedef void (*func_ptr_t)(void);
/*
* This won't work. OS bans you doing so.
*/
((func_ptr_t)code)();
}
在 Ubuntu 上,代码将运行,但会以状态代码 26 退出。鉴于 C 的类型不安全性质,代码可以编译,但对于 C++,编译器只会阻止您。 这是否意味着 JIT 必须绕过编译器,同时设置可执行标志?
编辑:除了mprotect
,如果您使用mmap
,您还可以指定要映射的页面的权限:
PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
因此,该页面将具有可执行权限。
如果要在堆可执行文件中创建一个区域,可以使用mprotect 。
int main() {
typedef void (*func_t)(void);
void *code = &some_jit_func;
int pagesize = getpagesize();
mprotect(code, pagesize,PROT_EXEC);
((func_t)code)();
}
您还可以使用 PROT_READ/PROT_WRITE 或标志
在您的代码中,您正在获取现有函数的地址。 这自然会指向一个已经可以执行的内存区域。 但是同一个区域在任何现代系统上都是不可写的。
另一方面,如果您 malloc() 一些内存,它将是可写但不可执行的。 因此,您在 jit 编译器中构建的任何代码都将无法执行,并且尝试调用您在那里构建的函数将失败。 您必须首先使用mprotect使内存可执行。
出于安全原因,您应该遵循W^X
原则。 这意味着任何页面都只能是可写或可执行的,但不能两者兼而有之。 当您使用 mprotect 使代码可执行时,也会使其不可写,反之亦然。 永远不要将可写和可执行结合起来。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.