Java代码审计学习笔记之JAVA基础系列(三、Unsafe)

0.Unsafe类概述

Unsafe类存在于sun.misc.Unsafe,网上说这个类半天使半魔鬼,并且使用的时候虽然能再很大程度上提高效率。但是很有可能导致程序发生崩溃。Unsafe类为java提供了非常强大的提高运行效率能力和与底层进行交互的能力。Unsafe类中通过其八十多个native方法及内部对于这些native方法组合使用生成的方法,提供了对于内存对象CAS线程等重要目标的操作能力,使得用户可以通过Unsafe类进行底层操作。

1.如何使用Unsafe类

Unsafe类的部分代码如下:

public final class Unsafe {
    private static final Unsafe theUnsafe;
    ......省略......
    private Unsafe() {
    }
        @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }
    ......省略......

首先Unsafe类有final修饰符,因此该类无法继承。并且,其默认的构造函数为private,因此无法再外部通过new创建实例。其内部提供了一个static修饰的getUnsafe方法可以返回实例化的对象,但是该方法中检测了加载该类的Loader是否为系统Loader,因此我们无法直接使用ClassLoader去加载这个类。当然我们可以通过修改系统Bootstrap Classloaderbootclasspath来加载这个类,但更简单的方法是直接使用反射机制加载。代码如下:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestProject {
    public static void main(String argvs[]) throws IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, NoSuchMethodException {
        Class myUnsafe = Class.forName("sun.misc.Unsafe");
        Constructor myUnsafeConstructor = myUnsafe.getDeclaredConstructor();
        myUnsafeConstructor.setAccessible(true);
        sun.misc.Unsafe myUnsafeInstance = (sun.misc.Unsafe) myUnsafeConstructor.newInstance();
    }
}

2.Unsafe类的用途

Unsafe类的用户非常广泛,主要包括:

  • 类的操作:类的非常规实例化、基于偏移地址获取或者设置变量的值、基于偏移地址获取或者设置数组元素的值等。
  • 内存管理:内存申请、读写
  • 线程同步:监视器锁定、解锁以及CAS相关、线程恢复和挂起等

这里教程中给了两个例子,一个是调用allocateInstance无视构造方法创建类实例,还有一个是直接使用DefineClass方法直接调用JVM创建类对象不过这个方法具有版本限制。

  • 1.调用allocateInstance无视构造方法的检查创建类实例
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestProject {
    public class UnSafeTest {

        private UnSafeTest() {
            // 假设RASP在这个构造方法中插入了Hook代码,我们可以利用Unsafe来创建类实例
            System.out.println("init...");
        }
        private void Hi() {
            System.out.println("Hi!!!");
        }

    }
    public static void main(String argvs[]) throws IllegalAccessException, InvocationTargetException, InstantiationException, ClassNotFoundException, NoSuchMethodException {
        Class myUnsafe = Class.forName("sun.misc.Unsafe");
        Constructor myUnsafeConstructor = myUnsafe.getDeclaredConstructor();
        myUnsafeConstructor.setAccessible(true);
        sun.misc.Unsafe myUnsafeInstance = (sun.misc.Unsafe) myUnsafeConstructor.newInstance();
        UnSafeTest unSafeTest = (UnSafeTest)myUnsafeInstance.allocateInstance(UnSafeTest.class);
        unSafeTest.Hi();
    }
}

参考