JAVA Unsafe类

参考文章:https://javasec.org/javase/Unsafe/

Unsafe类源码自我认知

这个Unsafe类位于package sun.misc包中

Unsafe使用了1个接口,4个类

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import sun.reflect.CallerSensitive; //该为接口
import sun.reflect.Reflection;

从如下定义方式可知,该类是final类型的变量,那么也就是无法继承的!

public final class Unsafe

Unsafe定义了一堆如下属性,需要注意的是theUnsafe

private static final Unsafe theUnsafe; //这个属性保存了一个Unsafe对象,为什么呢?继续看后面
public static final int INVALID_FIELD_OFFSET = -1;
public static final int ARRAY_BOOLEAN_BASE_OFFSET;
public static final int ARRAY_BYTE_BASE_OFFSET;
public static final int ARRAY_SHORT_BASE_OFFSET;
public static final int ARRAY_CHAR_BASE_OFFSET;
public static final int ARRAY_INT_BASE_OFFSET;
public static final int ARRAY_LONG_BASE_OFFSET;
public static final int ARRAY_FLOAT_BASE_OFFSET;
public static final int ARRAY_DOUBLE_BASE_OFFSET;
public static final int ARRAY_OBJECT_BASE_OFFSET;
public static final int ARRAY_BOOLEAN_INDEX_SCALE;
public static final int ARRAY_BYTE_INDEX_SCALE;
public static final int ARRAY_SHORT_INDEX_SCALE;
public static final int ARRAY_CHAR_INDEX_SCALE;
public static final int ARRAY_INT_INDEX_SCALE;
public static final int ARRAY_LONG_INDEX_SCALE;
public static final int ARRAY_FLOAT_INDEX_SCALE;
public static final int ARRAY_DOUBLE_INDEX_SCALE;
public static final int ARRAY_OBJECT_INDEX_SCALE;
public static final int ADDRESS_SIZE;

它的构造函数是私有的,这就导致了它无法进行共有的实例化

它有一个getUnsafe方法来进行获取本身

public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

它定义了一大堆的getter setter属性的方法,不止如下图所示的数量

我还看到了它关于操控内存的方法,如下方法



public native long allocateMemory(long var1); //分配内存
public native long reallocateMemory(long var1, long var3); //申请内存
public native void setMemory(Object var1, long var2, long var4, byte var6); //设置内存
public void setMemory(long var1, long var3, byte var5) { //设置内存
    this.setMemory((Object)null, var1, var3, var5);
}
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);//复制内存
public void copyMemory(long var1, long var3, long var5) { //复制内存
    this.copyMemory((Object)null, var1, (Object)null, var3, var5);
}
public native void freeMemory(long var1); //释放内存

还有三个特殊的如下方法:

public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); //定义类
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3); //定义匿名类
public native Object allocateInstance(Class<?> var1) throws InstantiationException; //分配实例

静态代码块,这里就要讲为什么theUnsafe会保存一个Unsafe对象了

static {
        registerNatives();
        Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"});
        theUnsafe = new Unsafe(); //这里就说明了为什么theUnsafe会保存一个Unsafe对象
        ARRAY_BOOLEAN_BASE_OFFSET = theUnsafe.arrayBaseOffset(boolean[].class);
        ARRAY_BYTE_BASE_OFFSET = theUnsafe.arrayBaseOffset(byte[].class);
        ARRAY_SHORT_BASE_OFFSET = theUnsafe.arrayBaseOffset(short[].class);
        ARRAY_CHAR_BASE_OFFSET = theUnsafe.arrayBaseOffset(char[].class);
        ARRAY_INT_BASE_OFFSET = theUnsafe.arrayBaseOffset(int[].class);
        ARRAY_LONG_BASE_OFFSET = theUnsafe.arrayBaseOffset(long[].class);
        ARRAY_FLOAT_BASE_OFFSET = theUnsafe.arrayBaseOffset(float[].class);
        ARRAY_DOUBLE_BASE_OFFSET = theUnsafe.arrayBaseOffset(double[].class);
        ARRAY_OBJECT_BASE_OFFSET = theUnsafe.arrayBaseOffset(Object[].class);
        ARRAY_BOOLEAN_INDEX_SCALE = theUnsafe.arrayIndexScale(boolean[].class);
        ARRAY_BYTE_INDEX_SCALE = theUnsafe.arrayIndexScale(byte[].class);
        ARRAY_SHORT_INDEX_SCALE = theUnsafe.arrayIndexScale(short[].class);
        ARRAY_CHAR_INDEX_SCALE = theUnsafe.arrayIndexScale(char[].class);
        ARRAY_INT_INDEX_SCALE = theUnsafe.arrayIndexScale(int[].class);
        ARRAY_LONG_INDEX_SCALE = theUnsafe.arrayIndexScale(long[].class);
        ARRAY_FLOAT_INDEX_SCALE = theUnsafe.arrayIndexScale(float[].class);
        ARRAY_DOUBLE_INDEX_SCALE = theUnsafe.arrayIndexScale(double[].class);
        ARRAY_OBJECT_INDEX_SCALE = theUnsafe.arrayIndexScale(Object[].class);
        ADDRESS_SIZE = theUnsafe.addressSize();
    }

我查了下静态代码块的概念:static代码块指的是static{}包裹的代码块,且静态代码只执行一次,可以通过Class.forName("classPath")的方式唤醒代码的static代码块,但是也执行一次

我等下需要做个试验来证明是不是只有Class.forName("classPath")来唤醒static代码块还是咋的!

Unsafe类的方法学习

现在在对该类的方法进行学习:

因为上面说了,不能直接实例化,因为默认的构造方法是私有的,所以现在有两种获取该类的对象的方法

第一种:通过反射来获取该类的无参构造方法,然后进行实例化!

// 获取Unsafe无参构造方法
Constructor constructor = Unsafe.class.getDeclaredConstructor();

// 修改构造方法访问权限
constructor.setAccessible(true);

// 反射创建Unsafe类实例,等价于 Unsafe unsafe1 = new Unsafe();
Unsafe unsafe1 = (Unsafe) constructor.newInstance();

第二种:通过反射来获取该类中的属性theUnsafe,然后通过Unsafe实例化对象来接受

上面自己说做的试验的其实就是这里的问题,因为自己发现笔记中做的代码是如下代码,并且运行了是可以获取到该对象的!

// 反射获取Unsafe的theUnsafe成员变量
Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");


// 反射设置theUnsafe访问权限
theUnsafeField.setAccessible(true);


// 反射获取theUnsafe成员变量值
Unsafe unsafe = (Unsafe) theUnsafeField.get(null);

System.out.println(unsafe.toString());

那么是不是也说明Unsafe.class操作也会触发static代码块???

结果如下:

关于第一个强大功能:allocateInstance,该方法可以绕过构造方法创建类实例

什么时候用到该方法呢?利用场景:开头被Hook,可以通过allocateInstace来绕过

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

那么只需要做的事情就是如下的一句话

UnSafeTest unsafetest = Unsafe对象.allocateInstace(UnSafeTest);

defineClass的妙用

什么时候用到Unsafe类中的defineClass呢?

利用场景:当ClassLoader类被限制的情况下,如果能够向JVM虚拟机注册一个类呢?通过Unsafe中的defineClass方法也是一种方法,这里自己想了下是不是不要局限于Unsafe类中,其实其他类中如果有defineClass方法的话也是可以利用的,当然自己没实战过,现在只需要记住就行了!

        //这里自己通过Unsafe类的defineClass来获取MyClassLoader包中的TestHelloWorld类,这个类就是javasec第一篇中创建的类

        //首先先获取Unsafe的类的实例,然后获取该构造器
        Constructor SafeConstructor = Unsafe.class.getDeclaredConstructor();

        //因为Unsafe构造器默认是私有的,所以还要设置权限
        SafeConstructor.setAccessible(true);

        //然后调用newIntance生成一个实例返回
        Unsafe unsafe = (Unsafe)SafeConstructor.newInstance();

        // 默认ClassLoader
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        //默认保护域
        ProtectionDomain domain = new ProtectionDomain(
                new CodeSource(null, (Certificate[]) null), null, classLoader, null
        );

        //通过Unsafe类的defineClass来绕过ClassLoader来创建TestHelloWorld类的对象
        Class helloWorld = unsafe.defineClass(TEST_CLASS_NAME, TEST_CLASS_BYTES, 0, TEST_CLASS_BYTES.length, classLoader, domain);

        Constructor WorldConstructor = helloWorld.getDeclaredConstructor();

        TestHelloWorld testHelloWorld = (TestHelloWorld) WorldConstructor.newInstance();
        System.out.println(testHelloWorld.hello());

posted @ 2020-08-11 19:56  zpchcbd  阅读(275)  评论(0编辑  收藏  举报