zoukankan      html  css  js  c++  java
  • 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());
    

  • 相关阅读:
    如何判断栈的增长方向
    时间复杂度
    shell基础part3
    shell基础part2
    shell基础part2
    linux基础part5
    linux基础part4
    linux基础part3
    linux基础part2
    shell基础part1
  • 原文地址:https://www.cnblogs.com/zpchcbd/p/13479662.html
Copyright © 2011-2022 走看看