Java基础:sun.misc.Unsafe
最后更新时间:
文章总字数:
预计阅读时间:
sun.misc.Unsafe
是Java底层API提供的一个神奇的Java类,Unsafe
提供了非常底层的内存、CAS、线程调度、类、对象等操作、Unsafe
正如它的名字一样它提供的几乎所有的方法都是不安全的,今天只分析如何使用Unsafe定义Java类和创建类实例,并不涉及攻击手法。
如何获取Unsafe对象
Unsafe是Java内部API,外部是禁止调用的,在编译Java类时如果检测到引用了Unsafe
类也会有禁止使用的警告:Unsafe是内部专用 API, 可能会在未来发行版中删除。
看到源码,可以发现这个Unsafe并不允许直接使用new的方式来创建对象,只能调用getUnsafe,同时对classLoader进行了严格的限制,只能使用SystemDomainLoader(事实上就是不允许你在外部调用)。
既然无法直接通过Unsafe.getUnsafe()
方式调用,那么可以采用反射来获取Unsafe实例。
Unsafe里面有这么一个成员变量:private static final Unsafe theUnsafe = new Unsafe();
那么只需要getDeclaredField即可,Unsafe.class.getDeclaredField("theUnsafe")
,但是这里需要注意我们只是获取到了字段的信息,要实际获取到字段的值还要使用get方法,完整反射代码如下:
1 | public static void main(String[] args) throws Exception{ |
当然我们也可以用反射创建Unsafe
类实例的方式去获取Unsafe
对象(获取默认构造函数):
1 | public static void main(String[] args) throws Exception{ |
获取到了unsafe对象之后就可以调用里面的危险方法了。下面分析两个危险方法。
allocateInstance无视构造方法创建类实例
传入java.lang.class对象调用Unsafe的allocateInstance方法,可以直接跳过构造方法得到类实例。当我们因为某些原因无法使用反射获取类实例时可以考虑这种方法。
1 | package com.test.classloader; |
1 | public class Test { |
defineClass直接调用JVM创建类对象
Unsafe里面有我们最喜欢的defineClass,直接使用字节码在JVM注册类。要注意的是这个defineClass有六个参数,需要我们自行传入类加载器和保护域。
1 | // 获取系统的类加载器 |
Unsafe
还可以通过defineAnonymousClass
方法创建内部类,这里不再多做测试。
注意:Java 11
开始Unsafe
类已经把defineClass
方法移除了(defineAnonymousClass
方法还在)