You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CreateRemoteThread执行Mono JIT原生代码远程注入访问违例问题

结论

你当前这套直接把mono_compile_method返回的JIT代码拷贝到远程进程执行的方案完全不可行,崩溃原因和你的推测方向一致,但实际存在的问题比你预想的更多:

  • Mono JIT生成的代码不是位置无关代码,内部硬编码了大量注入器进程的绝对地址:包括Mono内部函数地址、托管堆对象指针、方法元数据地址、字符串常量地址,这些地址拷贝到目标进程后全是野指针,执行时必然触发0xC0000005访问违例。
  • 你在注入器进程中调用mono_jit_init创建的是独立的全新Mono环境,和目标进程内正在运行的Mono实例完全隔离,内存空间没有任何关联,你在注入器内加载的程序集、编译的方法对目标进程完全不可见。
  • 你固定拷贝8KB代码的逻辑存在严重错误:mono_compile_method返回的方法代码长度不是固定值,要么越界读取了不属于目标方法的内存数据,要么拷贝长度不足截断了方法代码。
  • Mono JIT生成的方法有严格的执行前置条件:要求当前线程必须已经附着到对应Mono域、线程上下文符合Mono运行时要求,你直接通过CreateRemoteThread跳转到代码地址执行,线程根本没有在Mono环境中注册,必然崩溃。
可行方案

给Mono进程做托管代码注入根本不需要你自己拷贝JIT代码,标准流程走通了稳定性很高:

  • 先写个轻量的C++原生引导DLL,别在注入器EXE里折腾Mono逻辑。用最经典的CreateRemoteThread + LoadLibrary方案,把这个引导DLL注入到目标进程里,这步是Windows原生DLL注入的成熟流程,兼容性很好。
  • 引导DLL进入目标进程之后,先通过GetModuleHandle拿到目标进程自己已经加载的mono.dll(部分版本叫mono-2.0.dll,以目标进程实际加载的模块名为准)基址,再用GetProcAddress把需要用到的Mono API地址全解析出来:包括mono_get_root_domain、mono_thread_attach、mono_domain_assembly_open、mono_assembly_get_image、mono_class_from_name、mono_class_get_method_from_name、mono_runtime_invoke,不要静态链接Mono库,否则会出现地址不匹配的问题。
  • 引导DLL的执行线程中,第一步必须先调用mono_thread_attach(mono_get_root_domain()),把当前远程线程附着到目标进程的Mono根域上,这步缺失的话所有Mono相关调用都会直接崩溃,没有例外。
  • 剩余逻辑和你本地调用Mono API执行方法的流程完全一致:在目标进程地址空间内加载要执行的C# DLL,获取对应类、对应方法的MonoMethod指针,直接调用mono_runtime_invoke执行目标方法即可,全程不需要手动处理JIT生成的原生代码。
常见踩坑点
  • 你测试用的PrintText是静态无参方法,调用mono_runtime_invoke时第二个实例对象参数直接传NULL,参数数组、异常指针也传NULL即可正常执行。
  • 注入器和目标进程的CPU架构必须完全匹配,x64架构只能注入x64进程,x86架构只能注入x86进程,架构混用必然失败。
  • 你使用的Mono API定义必须和目标进程自带的Mono运行时版本匹配,否则结构体偏移、函数参数约定不匹配,会出现各种不可预期的崩溃。
  • 不要在注入器进程中提前加载目标C# DLL、提前JIT编译方法,所有Mono相关操作必须在目标进程内部、当前线程附着到Mono域之后执行,跨进程传递的本地指针对目标进程全是无效地址。
  • 这套方案仅适用于使用Mono运行时的.NET程序,如果目标是.NET Core/.NET 5+采用CoreCLR运行时的程序,需要改用CoreCLR对应的注入接口,Mono API完全不兼容。

内容的提问来源于stack exchange,提问作者epicMan123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 13:24:28