前言
Unsafe类是一个位于sun.misc
包下的类,它提供了一些相对底层方法,能够让我们接触到一些更接近操作系统底层的资源,如系统的内存资源、cpu指令等。而通过这些方法,我们能够完成一些普通方法无法实现的功能,例如直接使用偏移地址操作对象、数组等等。但是在使用这些方法提供的便利的同时,也存在一些潜在的安全因素,例如对内存的错误操作可能会引起内存泄漏,严重时甚至可能引起jvm
崩溃。因此在使用Unsafe
前,我们必须要了解它的工作原理与各方法的应用场景,并且在此基础上仍需要非常谨慎的操作,下面我们正式开始对Unsafe
的学习。
Unsafe功能
Unsafe的功能大致如下:
查看Unsfe
源码,可以看到该类被final
关键字修饰,代表不能被其他类继承
构造方法被private
修饰,就表明不能通过new
的方式创建Unsafe
类的实例,下面的getUnsafe()
方法可以返回Unsafe的实例。
查看下isSystemDomainLoader()
方法,可以看到如果var0
为Bootstrap
类加载器,那么就会等于”null”,也就是返回true
编写测试方法
Unsafe调用
方式一
该类的成员变量theUnsafe
定义为它的实例化,因此可以利用反射获取该变量的值
编写测试代码
1 2 3 4 5 6 7 8 9
| 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); System.out.println(o); } }
|
运行结果
方式二
因为Unsafe类里面存在getUnsafe方法,该方法的返回值是Unsafe的实例对象,所以可以通过反射调用该方法。
编写测试方法
1 2 3 4 5 6 7 8 9
| public class Test1 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { Class<?> aClass = Class.forName("sun.misc.Unsafe"); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(); declaredConstructor.setAccessible(true); Unsafe o = (Unsafe)declaredConstructor.newInstance(); System.out.println(o); } }
|
运行结果
Class相关操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public native long staticFieldOffset(Field f);
public native Object staticFieldBase(Field f);
public native boolean shouldBeInitialized(Class<?> c);
public native void ensureClassInitialized(Class<?> c);
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);
|
学习下Unsafe.defineClass()
的运用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package org.agent;
import javassist.CannotCompileException; import javassist.ClassPool; import javassist.CtClass; import javassist.NotFoundException; import sun.misc.Unsafe;
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 Test2 { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException, CannotCompileException, IOException, NotFoundException, NotFoundException, CannotCompileException, IOException, InvocationTargetException { String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet"; String Classname ="org.agent.Commandtest"; ClassPool classPool= ClassPool.getDefault(); classPool.appendClassPath(AbstractTranslet); CtClass payload=classPool.makeClass("org.agent.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, 0, bytes.length, systemClassLoader, protectionDomain); Object o = aClass1.newInstance();
} }
|
在JDK 11版本以后就移除了该方法。但还可以利用defineAnonymousClass
方法。
参考
https://www.cnblogs.com/nice0e3/p/14102892.html