Unsafe class of Java Security

Unsafe class of Java Security

0x00 Preface

In fact, the use of some JNI programming and javaagent technologies in security is very interesting and subtle. This has been said many times. Some interesting technologies will be found later, such as ASM and unsafe. Now let's explain the use of unsafe and some actual application scenarios.

0x01 unsafe overview

Unsafe is located in sun A class under misc package mainly provides some methods for performing low-level and unsafe operations, such as direct access to system memory resources, self-management of memory resources, etc. these methods play a great role in improving Java operation efficiency and enhancing the operation ability of Java language underlying resources. You can use this class to gain control of the underlying layer. This class is in sun Misc package, which is loaded by bootstrap classloader by default.

Take a look at the following two pictures

Unsafe class is a class that cannot be inherited, and unsafe class instances cannot be created directly through new.

You can see that the constructor is private, so you can't directly new the object. There is an instance of getunsafe () that will return unsafe.

@CallerSensitive
public static Unsafe getUnsafe() {
    // ----- 这里去获取当前类的ClassLoader加载器
    Class var0 = Reflection.getCallerClass();
    // ----- 判断var0是不是BootstrapClassLoader
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        // ----- 否:抛出SecurityException异常
        throw new SecurityException("Unsafe");
    } else {
        // ----- 是:返回unsafe对象
        return theUnsafe;
    }
}

The issystemdomain loader is called here to determine whether it is a bootstrap class loader. If so, the unsafe instance can be obtained normally, otherwise a security exception will be thrown.

public static boolean isSystemDomainLoader(ClassLoader var0) {
    // ----- 重点是在这里:
    // --- 当结果为true时:说明var0是Bootstrap类加载器,
    // -- 当结果为false时:说明var0是Extension || App || Custom 等类加载器
    // ----- 所以回到getUnsafe()函数,当这个函数返回false时,会直接抛异常,不允许加载Unsafe
    return var0 == null;
}

You can test it

package com.UNsafe;

import sun.misc.Unsafe;

public class test {
    public static void main(String[] args) {
        Unsafe unsafe = Unsafe.getUnsafe();
        int i = unsafe.addressSize();
    }
}

0x02 unsafe call

As mentioned earlier, unsafe functions are called directly, so we will think of our reflection mechanism at this time. Reflection can be used to call directly. There are also two ways to make reflection calls.

Call method 1:

Because this class defines its instantiation in theunsafe member variable, you can use reflection to directly obtain the value of the variable.

package com.UNsafe;

import sun.misc.Unsafe;

import java.lang.reflect.Field;

public class test {
    public static void main(String[] args) throws ClassNotFoundException,NoSuchFieldException,illegalaccessexception {
        Class<?> aClass = Class.forName("sun.misc.Unsafe");
        Field theUnsafe = aClass.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe o = (Unsafe)theUnsafe.get(null);
        int i = o.addressSize();
        System.out.println(i);

    }
}

result:

8

Call mode 2:

Another way is to call the getunsafe () method by reflection, which will directly return the unsafe instance object. Then you can reflect the instance that gets the construction method, and then call the method.

package com.UNsafe;

import sun.misc.Unsafe;

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

public class test {
    public static void main(String[] args) throws ClassNotFoundException,illegalaccessexception,NoSuchMethodException,InvocationTargetException,InstantiationException {
        Class<?> aClass = Class.forName("sun.misc.Unsafe");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Unsafe o = (Unsafe)declaredConstructor.newInstance();
        int i = o.addressSize();
        System.out.println(i);


    }
}

result:

8

0x03 unsafe function

Operational memory

public native long allocateMemory(long bytes);

	//分配内存,相当于C++的malloc函数

public native long reallocateMemory(long address,long bytes);

	//扩充内存
public native void freeMemory(long address);

	//释放内存
public native void setMemory(Object o,long offset,long bytes,byte value);

	//在给定的内存块中设置值
public native void copyMemory(Object srcBase,long srcOffset,Object destBase,long destOffset,long bytes);

	//内存拷贝
public native Object getObject(Object o,long offset);

	//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native void putObject(Object o,Object x);

	//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native byte getByte(long address);

//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native void putByte(long address,byte x);

	//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)

Get system information

public native int addressSize();  
//返回系统指针的大小。返回值为4(32位系统)或 8(64位系统)。
public native int pageSize();
//内存页的大小,此值为2的幂次方。

Thread scheduling

public native void unpark(Object thread);
// 终止挂起的线程,恢复正常.java.util.concurrent包中挂起操作都是在LockSupport类实现的,其底层正是使用这两个方法

public native void park(boolean isAbsolute,long time);

// 线程调用该方法,线程将一直阻塞直到超时,或者是中断条件出现。

@Deprecated
public native void monitorEnter(Object o);
//获得对象锁(可重入锁)

@Deprecated
public native void monitorExit(Object o);
//释放对象锁

@Deprecated
public native boolean tryMonitorEnter(Object o);
//尝试获取对象锁

Operation object

// 传入一个Class对象并创建该实例对象,但不会调用构造方法
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

// 获取字段f在实例对象中的偏移量
public native long objectFieldOffset(Field f);

// 返回值就是f.getDeclaringClass()
public native Object staticFieldBase(Field f);
// 静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);

// 获得给定对象偏移量上的int值,所谓的偏移量可以简单理解为指针指向该变量;的内存地址,
// 通过偏移量便可得到该对象的变量,进行各种操作
public native int getInt(Object o,long offset);
// 设置给定对象上偏移量的int值
public native void putInt(Object o,int x);

// 获得给定对象偏移量上的引用类型的值
public native Object getObject(Object o,long offset);
// 设置给定对象偏移量上的引用类型的值
public native void putObject(Object o,Object x););

// 设置给定对象的int值,使用volatile语义,即设置后立马更新到内存对其他线程可见
public native void putIntVolatile(Object o,int x);
// 获得给定对象的指定偏移量offset的int值,使用volatile语义,总能获取到最新的int值。
public native int getIntVolatile(Object o,long offset);

// 与putIntVolatile一样,但要求被操作字段必须有volatile修饰
public native void putOrderedInt(Object o,int x);

Here, the allocateinstance method is very interesting. You can get an instance of an incoming object without calling the construction method.

So how do you use this method in security? Suppose that in a scenario, the constructor of a class is hook, and the constructor is private modification, and it cannot directly new the object. If you can't use the reflection mechanism to make a call at this time, you can use this method to bypass it at this time.

Small case:

Define a person class, and the constructor is private.

package com.demo2;

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private  int age;

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ",age=" + age +
                '}';
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    private Person() {

    }

    private Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
}

Write calling test code:

package com.UNsafe;

import com.demo2.Person;
import sun.misc.Unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class test {
    public static void main(String[] args) throws ClassNotFoundException,InstantiationException {
        Class<?> aClass = Class.forName("sun.misc.Unsafe");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Unsafe unsafe = (Unsafe)declaredConstructor.newInstance();
        Person person = (Person)unsafe.allocateInstance(Person.class);
        person.setAge(20);
        person.setName("nice0e3");
        System.out.println(person);
    }
}

Execution results:

Person{name='nice0e3',age=20}

The constructor is called without reflection and new reflection.

Class related operations

//静态属性的偏移量,用于在对应的Class对象中读写静态属性
public native long staticFieldOffset(Field f);
//获取一个静态字段的对象指针
public native Object staticFieldBase(Field f);
//判断是否需要初始化一个类,通常在获取一个类的静态属性的时候(因为一个类如果没初始化,它的静态属性也不会初始化)使用。 当且仅当ensureClassInitialized方法不生效时返回false
public native boolean shouldBeInitialized(Class<?> c);
//确保类被初始化
public native void ensureClassInitialized(Class<?> c);
//定义一个类,可用于动态创建类,此方法会跳过JVM的所有安全检查,默认情况下,ClassLoader(类加载器)和ProtectionDomain(保护域)实例来源于调用者
public native Class<?> defineClass(String name,byte[] b,int off,int len,ClassLoader loader,ProtectionDomain protectionDomain);

//定义一个匿名类,可用于动态创建类
public native Class<?> defineAnonymousClass(Class<?> hostClass,byte[] data,Object[] cpPatches);

The defineclass method is also very interesting. In the previous study, you should be deeply impressed with the defineclass method. For example, the implementation of the command execution Java webshell tool and some free killing of JSPS will use the defineclass method of classloader to restore the bytecode to a class. The role of this defineclass here is also explained above. You can also define an anonymous class and create it dynamically. Suppose a scenario classloader When defineclass is unavailable, you can use unsafe defineClass。

Dynamically loading class cases

package com.UNsafe;

import com.demo2.Person;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.NotFoundException;
import sun.misc.Unsafe;

import javax.xml.soap.saajResult;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.security.cert.Certificate;

public class test {
    public static void main(String[] args) throws ClassNotFoundException,InstantiationException,CannotCompileException,IOException,NotFoundException {
        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String Classname ="com.nice0e3.Commandtest";
        ClassPool classPool= ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload=classPool.makeClass("com.nice0e3.Commandtest");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");

        byte[] bytes=payload.toBytecode();
        //获取系统加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        //创建默认保护域
        ProtectionDomain protectionDomain = new ProtectionDomain(new CodeSource(null,(Certificate[]) null),null,systemClassLoader,null);
        Class<?> aClass = Class.forName("sun.misc.Unsafe");
        Constructor<?> declaredConstructor = aClass.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);
        Unsafe unsafe = (Unsafe)declaredConstructor.newInstance();
        Class<?> aClass1 = unsafe.defineClass(Classname,bytes,bytes.length,protectionDomain);
        Object o = aClass1.newInstance();
        

    }
}

This method has been removed since JDK 11. However, the defineanonymousclass method mentioned earlier still exists and can be used.

Reference articles

https://www.cnblogs.com/rickiyang/p/11334887.html
https://javasec.org/javase/Unsafe/

0x04 end

It can be seen from the above case that javassist dynamically generates classes when analyzing and using the chain, and then converts them into byte code unsafe for loading. In fact, we can also think of some interesting utilization scenarios.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>