JIT编译代码是如何注入内存并执行的?

11
考虑一个典型的 Windows x86 或 AMD64 架构,内存被划分成可执行部分和数据部分。可执行部分不能被写入,数据部分可以被写入但不能被执行(DEP)。JIT 在内存中编译方法,并且通常不将任何内容存储到磁盘上,而是将其移动到下一个指令指针可以到达的位置,将当前指令指针(指向 JIT)更改为指向新生成的代码,然后执行它。这两段话虽然有点过于简单化,但基本解释了JIT和Windows的内存模型。我还知道,当我试图手动复制一些可执行代码到内存中并尝试执行它时,通常情况下无法执行(除非使用 DLL 注入)。JIT 设计者是如何克服这个障碍的?他们使用 ring-0 驱动程序还是在用户模式下完成所有操作呢?
1个回答

13

使用Windows VirtualProtect() API函数即可完成操作,它可以更改虚拟内存页属性,将其从PAGE_READWRITE变为PAGE_EXECUTE_READ,以便JIT编译器可以写入机器代码并执行。不需要特殊权限,因为该页是由运行JIT编译器的进程拥有的。


真的,那么简单吗?有道理。(OT)啊,我记得,上次我需要注入一些东西时它不在我的进程中(即,要删除当前正在运行的可执行文件,您需要先卸载可执行文件,这是典型的鸡/蛋问题)。 - Abel
@Hans:进一步了解后,我想知道这与这篇Social MSDN帖子有什么关系,该帖子解释了您需要管理员权限才能使用VirtualProtect更改标志,并声称它不能以这种方式工作。对于ActionScript,相同的原理适用于VirtualAlloc正如这位博主所示(他弄错了名称,请参见他的第三张图片的下标)。两篇文章都让我认为应该使用VirtualAlloc。你对此有什么想法? - Abel
@Hans:是的,我反应太快了。查看VirtualProtect ,也没有提到这一点。请注意,在SSCLI中,我没有在fjitcompiler.cpp中找到使用VirtualProtect,只有VirtualAlloc。VirtualProtect在hosting.cpp中经常被重新定义/未定义(变成Dont_Use_VirtualProtect哈哈)。最后,如果可用,则似乎归结为调用IHostMemoryManager->VirtualProtect,否则将调用Win32 API的::VirtualProtect(这就是你说的)。 - Abel
抱歉我拖延了这么久,但我需要一些保证。幸运的是,你给了足够的答案让我可以进一步研究这个主题。谢谢! - Abel
2
@Hans:你知道CLR JIT是否在编译完成后删除对包含生成代码的页面的写访问权限吗?还是它们保留了“PAGE_EXECUTE_READWRITE”访问权限? - Ben Voigt
显示剩余2条评论

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