JDK17+反射限制绕过
JDK17+反射限制绕过
测试环境
jdk-17.0.5
测试代码
List<Integer> list = new ArrayList<>();
Field field2 = list.getClass().getDeclaredField("elementData"); //private static final Object[]
field2.setAccessible(true);
for (int i = 0; i < 100; i++) {
Object[] elementData = (Object[]) field2.get(list);
System.out.println(i + "," + "list size:" + list.size() + ", element array length:" + elementData.length);
list.add(i);
}
在JDK17下运行报错:
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field transient java.lang.Object[] java.util.ArrayList.elementData accessible: module java.base does not "opens java.util" to unnamed module @3b07d329
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at Main.main(Main.java:22)
调试分析
从field2.setAccessible(true);断点调试,跟进checkCanSetAccessible()
一直跟进到checkCanSetAccessible()
,下面是分块解析这个方法
caller即在哪个类进行了反射操作,首先判断调用类是不是MethodHandle,如果是报错了
往下,分别获取caller和declaringClass(被反射的类)的module,如果callerModule与declaringModule或者Object.class.getModule()相等就 return true (即能够允许被反射修改)
declaringModule.isNamed()如下,如果name为null则返回true
继续往下
这个部分是检测被反射对象是不是Public 或者protected-static
这段判断declaringModule的package是否对caller开放,是就能返回true,
如果前面的条件都没达到,就报错,return false
思路
最简单的,通过修改当前类(caller)的Module 与 Object.class.getModule()
相等
如何修改Module?
可以通过Unsafe#getAndSetObject来修改module进行反射修改
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。但由于Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力,这无疑也增加了程序发生相关指针问题的风险。在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。
获取Unsafe实例
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
这个反射满足
下一步就是通过objectFieldOffset获取module偏移,然后用getAndSetObject修改当前类的module为Object.class.getModule()的值
Module baseModule = Object.class.getModule();
Class currentClass = Main.class;
long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass, addr, baseModule);
修改后再进行需要的反射
import java.lang.reflect.Field;
import sun.misc.Unsafe;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
Module baseModule = Object.class.getModule();
Class currentClass = Main.class;
long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass, addr, baseModule);
List<Integer> list = new ArrayList<>();
Field field2 = list.getClass().getDeclaredField("elementData");
field2.setAccessible(true);
for (int i = 0; i < 100; i++) {
Object[] elementData = (Object[]) field2.get(list);
System.out.println(i + "," + "list size:" + list.size() + ", element array length:" + elementData.length);
list.add(i);
}
}
}
总结
先修改当前类的module,再反射
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
Module baseModule = Object.class.getModule();
Class currentClass = Main.class; //注意修改
long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass, addr, baseModule);