首页 > 解决方案 > (JNI) 使用 GetMethodID 获取 Java 方法的内存地址

问题描述

如何获取我拥有 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 方法: 这就是 ReClass.NET 中的 jvm.dll.xxx 方法

或者有没有其他方法可以在没有 JVMTI 的情况下本地挂钩/操作 Java 方法?

标签: javac++jvmjava-native-interfacereverse-engineering

解决方案


您不能像使用本地方法那样挂钩 Java 方法,即直接在内存中替换机器代码。

jmethodID是对 Java 方法的不透明引用。它可能在不同的 JVM 中实现不同,甚至在同一个 JVM 的不同版本中实现。例如,jmethodID随着 Metaspace 的出现,JDK 7 和 JDK 8 之间的内部表示发生了变化。

现在,在 HotSpot JVM 中,jmethodID是指向MethodMetaspace 中的结构的指针。它不是Java方法的代码,而是代表JVM内部方法的内部结构。

请注意,Java 方法最初根本没有任何机器代码 - 相反,JVM 解释其字节码。由于 JIT 编译、重新编译或反优化,方法的机器代码可能会出现、更改或完全消失。此外,一个方法可能同时具有多个 JIT 编译版本。这就是为什么传统的挂钩技术不能应用于 Java 方法的原因。此外,一个方法可能被内联到其他 JIT 编译的方法中,在这种情况下jmethodID将毫无用处。

但是,有一种操作 Java 方法的标准技术——字节码检测。它可通过标准API 获得,即JVM TI 的RetransformClassesRedefineClasses函数。

如果您使用 JNI,您也可以使用 JVM TI 函数。即使没有代理或特殊的 JVM 参数,JVM TI 也能正常工作;它可以从任何 JNI 上下文中获得。例如如何jvmtiEnv*JNIEnv*

JavaVM* vm;
(*env)->GetJavaVM(env, &vm);

jvmtiEnv* jvmti;
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);

推荐阅读