如何通过JNI MethodID获取Java方法内存地址?有无非JVMTI原生Hook方案?
咱来拆解你遇到的问题——你已经在用ReClass.NET逆向JVM结构了,说明你愿意碰一些未公开的内部细节,那咱们就结合HotSpot JVM的实际机制来聊:
首先得明确:JNI的MethodID在HotSpot JVM(绝大多数OpenJDK/Oracle JDK的底层实现)里,本质是指向**Method结构体**的指针(少数版本是经过包装的Handle,但一般可以直接强转,注意32/64位平台的指针长度差异)。你用ReClass.NET看到的jvm.dll.53A14DE8 Method : Metadata : MetaspaceObj就是这个Method对象本身。
接下来要找到Method结构体里的代码入口字段:
- HotSpot的
Method结构体中,核心的代码入口是_entry_point字段(不同JVM版本可能叫_code或者偏移不同),但这个字段的值分两种情况:- 如果方法处于解释执行状态:这个入口是解释器的通用代码地址(比如模板解释器的dispatch逻辑),不是你要找的目标方法机器码——这就是你看到“3字节/30+指令”的原因,那是解释器的逻辑,不是方法本身的实现。
- 如果方法已经被JIT编译(C1/C2编译器生成了机器码):这个字段会指向JIT生成的机器码起始地址,也就是你要找的返回1.0的目标方法实际内存地址。
具体操作步骤:
- 把
MethodID强转为Method*:在C++里写Method* method = reinterpret_cast<Method*>(methodId);(注意:Method是HotSpot内部结构体,你需要自己根据逆向结果定义它的结构,或者直接用偏移访问)。 - 定位
_entry_point的偏移:用ReClass.NET打开jvm.dll,找到Method结构体的布局,确定代码入口字段的偏移量(比如HotSpot 8 64位版本中,这个偏移大概是0x78,具体看版本)。 - 判断JIT状态:读取
Method结构体的_flags字段,检查是否有_is_compiled类的标志;或者直接判断_entry_point是否指向jvm.dll外部(解释器入口一般在jvm.dll内部,JIT代码在专门的代码缓存区)。 - 触发JIT(如果需要):如果方法还没被编译,循环调用这个Java方法多次,JVM的JIT编译器会自动把热点方法编译成机器码,之后
_entry_point就会指向实际方法地址。
不用JVMTI的话,都是基于修改JVM内部结构或内存代码的“黑科技”,兼容性和稳定性不如官方接口,但确实可行:
Hook JIT编译后的方法入口
当方法被JIT编译后,直接修改其机器码:- 用
VirtualProtect(Windows)或mprotect(Linux)把代码页权限改成RWX(默认JIT代码页是只读的)。 - 把方法入口的前几个字节替换成跳转指令(比如x86的
jmp、x64的rip相对跳转),跳转到你的代理函数。 - 代理函数执行完你的逻辑后,再跳回原方法的剩余代码继续执行。
注意:JVM可能会对JIT代码做去优化(Deoptimization),把方法变回解释执行状态,这时候Hook会失效——你需要监听去优化事件(或定期检查入口地址),重新Hook。
- 用
修改Method结构体的入口点
直接替换Method结构体里的_entry_point字段,让它指向你的代理函数:- 先把
Method所在的Metaspace内存页权限改成可写(默认是只读)。 - 把
_entry_point的值改成你的代理函数地址,代理函数的签名要和原方法的JNI调用签名匹配(比如原方法是()F,代理函数要返回jfloat,参数为JNIEnv*、jobject)。
优势:不需要修改机器码,但要注意JVM版本的结构体偏移变化,以及去优化可能会重置_entry_point。
- 先把
解释器层面Hook
如果目标方法始终是解释执行,你可以Hook解释器的方法调用逻辑:
逆向jvm.dll找到模板解释器的方法dispatch入口,拦截这个点,检查当前要执行的Method对象是否是你的目标方法,如果是就直接返回1.0,否则走原逻辑。
难点:解释器结构在不同JVM版本差异极大,需要深入逆向,兼容性很差,不推荐除非你只针对特定版本。
最后提醒:这些方案都依赖HotSpot的内部实现,不同JDK发行版(Oracle JDK、OpenJDK、Azul Zulu)、不同版本的JVM结构体可能有差异,JVM更新后很可能失效。如果可以的话,优先用JVMTI——它是JVM提供的标准工具接口,稳定且兼容性好,专门用于这类Hook和方法操控场景。
内容的提问来源于stack exchange,提问作者yungcxn

