首页 > 解决方案 > 通过 Java ASM 调用私有方法

问题描述

我想在不使用反射的情况下调用私有方法:是否可以使用 ASM 挂钩私有本机方法并调用它?

标签: javamethodsinvokejava-bytecode-asm

解决方案


挑战不是private通过 ASM 或类似的字节码生成工具生成调用该方法的类。挑战是在不被验证者拒绝的情况下将字节码放入 JVM。

对于 OpenJDK/HotSpot,sun.misc.Unsafe它具有defineAnonymousClass(Class<?>, byte[], Object[])用于生成访问器类的方法,例如实现功能接口和调用private包含 lambda 表达式主体的合成方法的运行时类。

您可以使用它来加载您的字节码,使用private方法的声明类作为第一个参数,以使该方法可访问。另一方面,JRE 自己的用例意味着您甚至不需要自己生成这样的字节码:

MethodHandles.Lookup l = MethodHandles.privateLookupIn(Class.class, MethodHandles.lookup());
MethodHandle target = l.findSpecial(Class.class, "getDeclaredFields0",
    MethodType.methodType(Field[].class, boolean.class), Class.class);
BiFunction<Class<?>,Boolean,Field[]> a = (BiFunction)LambdaMetafactory.metafactory(
    l, "apply", MethodType.methodType(BiFunction.class), target.type().generic(),
    target, target.type().changeParameterType(1, Boolean.class))
    .getTarget().invokeExact();

然后,您可以调用,例如a.apply(Class.class, false),它将getDeclaredFields0在没有反射的情况下调用。

privateLookupIn是一种 Java 9 方法,如果您使用非模块化代码或--add-opens在 JVM 启动时提供必要的选项以打开java.base您的模块,则该方法将起作用。会有一个关于反射访问的警告,好吧,注意到这种访问“将在未来的版本中被拒绝”是正确的。无论如何,如何在没有 JVM 参数的情况下在 java 9 中隐藏警告“非法反射访问”?可能很有趣。

对于 Java 8,您需要侵入Lookup该类以获取适当的查找实例。这个答案显示了一个解决方案,是另一种方法。


推荐阅读