(JNI) 使用 GetMethodID 获取 Java 方法的内存地址
Posted
技术标签:
【中文标题】(JNI) 使用 GetMethodID 获取 Java 方法的内存地址【英文标题】:(JNI) Get Java method's memory address with GetMethodID 【发布时间】:2020-04-19 04:16:35 【问题描述】:如何获取我拥有 JNI MethodID 的方法的内存位置?
我想通过使用 JNI 来挂钩或操作 Java 方法,因为众所周知,JVM 会重新定位此类方法,因此无法使用指向方法的静态指针。
所以我使用 C++ 和 JNI 从 JVM 中获取 MethodID。
MethodID 可以转换为整数,即十六进制内存地址。
我已经发现,在 MethodID 的内存位置,有一个指向 HEAP 地址的指针。这个堆地址指向一个
“jvm.dll.53A14DE8 方法:元数据:MetaspaceObj”
(这就是我的反向工具“ReClass.NET”所说的)
所以 jvm.dll.xxx 方法得到了一些函数指针,但这些不能是方法,因为它们由 3 个字节组成(太小)或者真的太大(30 条指令+)。 我想找到的方法只返回 1.0 的浮点数
这就是 ReClass.NET 中的 jvm.dll.xxx 方法:
或者有没有其他方法可以在没有 JVMTI 的情况下本地挂钩/操作 Java 方法?
【问题讨论】:
您可以查看 JNA (github.com/java-native-access/jna) 如何获取回调地址 为什么不是 JVM TI?这是操作 Java 方法的正确方式(我会说,唯一方式)。 我试图通过获取方法的地址来找出另一种操作方式。 JNA 和 JVM TI 已打补丁,会导致我想要操作的内容崩溃。 “JVM TI 已打补丁,会崩溃”是什么意思?这是标准 JVM 的基本部分,应该像 JNI 一样可用。 再说一遍:没有“方法地址”这样的东西。 Java 方法可能有一个字节码的地址,以及多个(可能为零)已编译的条目。即使您将它们全部修补,这仍然不能保证行为发生变化,因为该方法可能已经内联到其他一些编译方法中。后者尤其适用于像“return 1.0”这样的简单方法。在这种情况下,您必须找到该方法的所有调用站点,递归地修补它们等等......理论上这是可能的,但这是一项远远超出您最初问题的巨大研究。 【参考方案1】:您不能像使用本地方法那样挂钩 Java 方法,即直接在内存中替换机器代码。
jmethodID
是对 Java 方法的不透明引用。它可能在不同的 JVM 中实现不同,甚至在同一个 JVM 的不同版本中实现。例如,随着 Metaspace 的出现,jmethodID
的内部表示在 JDK 7 和 JDK 8 之间发生了变化。
现在,在 HotSpot JVM 中,jmethodID
是指向元空间中的 Method
结构的指针。它不是Java方法的代码,而是代表JVM内部方法的内部结构。
请注意,Java 方法最初根本没有任何机器代码 - 相反,JVM 解释其字节码。由于 JIT 编译、重新编译或反优化,方法的机器代码可能会出现、更改或完全消失。此外,一个方法可能同时具有多个 JIT 编译版本。这就是为什么传统的挂钩技术不能应用于 Java 方法的原因。此外,一个方法可以内联到其他 JIT 编译的方法中,在这种情况下,jmethodID
将毫无用处。
然而,有一种标准技术可用于操作 Java 方法 - bytecode instrumentation。可通过标准 API 获取,即JVM TI 的RetransformClasses 和RedefineClasses 函数。
如果您使用 JNI,您也可以使用 JVM TI 函数。即使没有代理或特殊的 JVM 参数,JVM TI 也能正常工作;它可以从任何 JNI 上下文中获得。例如。如何从JNIEnv*
获取jvmtiEnv*
:
JavaVM* vm;
(*env)->GetJavaVM(env, &vm);
jvmtiEnv* jvmti;
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
【讨论】:
我不知道 JNI 中的 JVM TI,谢谢。但这不是我需要的,因为这是可能的。这个视频里的人是这么干的:youtu.be/4Futai_P5gw 问题是视频显示了我想hook方法的方式的结果,但是并没有说明什么。 @yungcxn 东西是not that simple。如果你想深入研究编译 Java 方法的二进制代码,我建议从-XX:+PrintAssembly
JVM 选项开始 - 它会转储所有 JIT 编译代码以及方法名称、地址等。
好的...我明白了。我会像你说的那样做,但是你将如何继续在内存中找到那些方法实例?我不介意这有多难,我有时间:)
你有jvm ti和jni的资源吗?以上是关于(JNI) 使用 GetMethodID 获取 Java 方法的内存地址的主要内容,如果未能解决你的问题,请参考以下文章
Android jni GetFieldID 和 GetMethodID 函数的说明
使用挂起异常java.lang.ClassNotFoundException调用JNI GetMethodID