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

C++调用jvm.dll未导出GetPhase函数访问异常排查

问题排查与修复方案

你的代码触发访问异常是三个核心问题叠加导致的:

1. 地址计算存在指针运算逻辑错误

当前地址计算代码:

func* f = (func*) (DWORD_PTR) hMod + 0x086059b8;

存在C指针运算的典型优先级错误:强转操作优先级高于加法,代码实际是先把模块基址hMod转为func*类型指针,再执行加法。C中对类型化指针加整数时,偏移量会自动乘以指针指向类型的大小:64位环境下函数指针占8字节,最终计算出的地址实际为hMod + 0x086059b8 * 8,完全跳离了目标函数所在的内存段,直接执行非法内存的指令触发异常。

正确的地址计算需要先按字节粒度计算偏移,再转为函数指针:

// 先转uint8_t*(单字节指针)做字节级偏移,再转对应函数类型
using GetPhaseFn = jvmtiError(*)(void*, jvmtiPhase*);
GetPhaseFn get_phase = (GetPhaseFn)((uint8_t*)hMod + 0x086059b8);

2. 函数调用约定、参数传递完全不匹配

从OpenJDK源码可见,你要调用的GetPhase是JvmtiEnv类的成员函数,不是全局函数:

  • Windows x64环境下C++类成员函数默认使用__thiscall约定,第一个隐式参数为JvmtiEnv实例的this指针(存在RCX寄存器),第二个参数才是你要传入的jvmtiPhase*输出指针
  • 你当前代码仅传入一个参数,且把空指针phase_ptr错当this指针传入,函数内部访问实例成员时会直接访问非法地址
  • 你定义的phase_ptr初始值为nullptr,即使参数顺序正确,函数向空地址写入阶段值也会触发内存写违例

正确的调用逻辑需要先拿到当前环境对应的JvmtiEnv实例地址作为this参数传入,同时为phase值分配有效内存:

jvmtiPhase current_phase;
// 第一个参数传JvmtiEnv实例指针,第二个参数传phase值的存储地址
jvmtiError err = get_phase(jvmti_env_instance, &current_phase);

3. 硬编码偏移存在有效性问题

你从Ghidra拿到的0x086059b8地址不能直接用:

  • Ghidra加载PE文件时默认使用PE头记录的默认镜像基址计算绝对地址,你需要先减去jvm.dll的默认镜像基址,得到相对加载基址的RVA偏移,才能和运行时拿到的hMod相加得到正确的函数地址
  • 该偏移仅和你当前分析的特定JDK版本、特定构建(32/64位、release/debug)绑定,换JDK版本、甚至同版本不同更新包偏移都会发生变化,硬编码的兼容性极差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:18:16