sun.misc.Unsafe
sun.misc.Unsafe是Java底层API(仅限Java内部使用,反射可调用)提供的一个神奇的Java类,Unsafe提供了非常底层的内存、CAS、线程调度、类、对象等操作。Unsafe正如它的名字一样,它提供的几乎所有方法都是不安全的。
如何使用Unsafe定义Java类、创建类实例。
如何获取Unsafe对象
Unsafe是Java内部API,外部是禁止调用的,在编译Java类时如果检查到引用Unsafe类也会有禁止使用的警告:Unsafe是内部专用API,可能会在未来的发行版中删除。sun.misc.Unsafe代码片段:
import sun.reflect.CallerSensitive;import sun.reflect.Reflection;public final class Unsafe {private static final Unsafe thUnsafe;static {theunsafe = new Unsafe();其他代码......}private Unsafe() {}@CllerSensitivepublic static Unsafe getInsafe() {Class var0 = Reflection.getCallerClass();if (var0.getClassLoader() != null) {throw new SecurityException("Unsafe");} else {return theUnsafe;}}省去其他代码。。。。。。。。}
由以上代码片段可以看到,Unsafe类时一个不能被继承的类且不能通过new的反射创建Unsafe类实例,如果通过getUnsafe方法获取Unsafe实例还会检测类加载器,默认只允许Bootstrap classloader调用。
无法通过Unsafe.getUnsafe()的方式调用,那么就可以使用反射的方式去获取Unsafe类实例
// 获取Unsafe无参构造方法Constructor constructor = Unsafe.class.getDeclaredConstructor();// 修改构造方法访问权限constructor.setaccessible(true);// 反射创建构造Unsafe类实例,等价与 Unsafe unsafe1 = new Unsafe();Unsafe unsafe1 = (Unsafe) constructor.newInstance();
allocatelnstance无视构造方法创建类实例
假如有一个com.anbai.sec.unsafe.UnSafeTest的类,因为某种原因我们不能直接通过反射的方法去创建UnsafeTest类实例,那么这个时候使用Unsafe的allocateInstance方法就可以绕过这个限制了。
UnsafeTest代码片段:
public class UnsafeTest {priveate UnSafeTest() {// 假设RASP在这个构造方法中插入了Hook代码,可以使用Unsafe来创建实例System.out.println("init....");}}
使用Unsafe创建UnsafeTest对象:
// 使用Unsafe创建UnsafeTest类实例UnSafeTest test= (UnSafeTest) unsafe1.allocateInstance(UnSafeTest.class);
Google的GSON库在JSON反序列化的时候使用了这个方式来创建类实例。在渗透测试中也会经常遇到这样的限制,比如RASP限制了java.io.FileInputStream类的构造方法导致无法读取文件或者限制了UNIXProcess/ProcessImpl类的构造方法导致无法执行本地命令等。
defineClass直接调用JVM创建类对象
如果ClassLoader被限制的情况下还可以通过Unsafe的defineClass方法来实现同样的功能。
Unsafe提供了一个通过传入类名。类字节码的方式就可以定义类的defindeClass方法:
public native Class defineClass(String var1, byte[] var2, int var 3, int var4);
public native Class<?> defineClass(String var1, date[] var2, int var3, int var4,ClassLoader var5, protectionDomain var6);
使用Unsafe创建TestHelloWorld对象:
// 使用Unsafe想JVM中注册com.anbai.sec.classloader.TestHelloWorld类Class helloWorldClass = unsafe1.defineClass(TEST_CLASS_NAME, TEST_CLASS_BYTES, 0, TEST_CLASS_BYTES.length);
或调用需要传入类加载器和保护域的方法:
// 获取系统的类加载器ClassLoader classLoader = ClassLoader.getSystemClassLoader();// 创建默认的保护域ProtectionDomain domain = new ProtectionDomain(new CodeSource(null, (Certificate[]) null), null, classLoader, null);// 使用Unsafe向JVM中注册com.anbai.sec.classloader.TestHelloWorld类class helloWorldClass = unsafe1.defineClass(TEST_CLASS_NAME, TEST_CLASS_BYTES, 0, TEST_CLASS_BETES.length, classLoader, domain);
Unsafe还可以通过defineAnonymousClass方法创建内部类
注:
这个实例仅适用于Java 8以前的版本如果在Java 8中应该使用应该调用需要传类加载器和保护域的那个方法。Java 11开始Unsafe类已经把defineClass方法移除了(defineAnonymousClass方法还在),虽然可以使用java.lang.invoke.MethodHandles.Lookup.defineClass来代替,但是MethodHandles只是间接的调用了ClassLoader的defineClass,所以一切也就回到了ClassLoader。
