Unsafe
sun.misc.Unsafe
sun.misc.Unsafe提供了一些底层操作,如内存、CAS、类、对象
Unsafe是Java内部API,不允许外部调用
/*
A collection of methods for performing low-level, unsafe operations. Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.
*/
public final class Unsafe {
private Unsafe() {}
private static final Unsafe theUnsafe = new Unsafe();
@CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
}Unsafe采用了单例模式,其getUnsafe()方法会先判断调用者(Reflection.getCallerClass();)的类加载器classLoader是否为Bootstrap Classloader
可以使用反射来获取Unsafe对象
allocateInstance
若RASP限制了某些类的构造方法(比如TrAXFilter(加载字节码)、ProcessImpl(Windows命令执行)、UnixProcess(Linux命令执行))
可以用Unsafe的allocateInstance方法绕过这个限制
Google的GSON库在JSON反序列化的时候就使用这个方式来创建类实例

绕过命令执行限制
Windows版本:
Linux版本:
注意这个方法并不会执行任何构造方法
有时候使用反射去创建实例时,会遇到各种复杂的类依赖关系,此时也可以考虑用这个去实例化对象
内存层面修改值
在JVM中,对实例的Field进行了有规律的存储,通过一个偏移量可以从内存中找到相应的Field值
Unsafe提供两个方法来获取Field的偏移量
staticFieldOffset(Field var1)和objectFieldOffset(Field var1)
获取偏移量后可以调用putObject、putInt等方法来修改对象的成员值
当Field的set方法被限制时,可以考虑这种方法绕过
但对final修饰的字段貌似改不了
defineClass
Unsafe提供了一个defineClass方法,传入类名、类字节码可以在JVM中注册一个类

注意Unsafe#defineClass只能在JVM中define一个类,不会加载这个类,所以最后通过Class.forName来触发其静态代码块
defineAnonymousClass
defineAnonymousClass可以创建一个内部类
这个类的名字设置时甚至可以是已存在的类名,由于java动态编译特性会在内存中生成新的类名
无法通过Class.forName获取这个类的(do not make it known to the class loader)
这个类的classloader为null

打印出类名为java.lang.String/1645995473,并弹出计算器
Java 11 把
Unsafe的defineClass方法移除了,但defineAnonymousClass还在
Close RASP
一旦攻击者拿到了一个代码执行权限,那么他便可以通过反射的方式取得RASP运行在内存中的开关变量(多为boolean或者AtomicBoolean类型),并把它由true修改为false,就可以使RASP得的防护完全失效。开关变量只是其中一个思路,当然有更多的方法去破坏RASP的运行模式,如置空检测逻辑代码(如果RASP使用了js、lua等别的引擎),置空黑名单、添加白名单
Summary
内存层面修改对象的字段
绕反射限制
关闭RASP开关
自定义类
更加隐蔽的内存马
defineAnonymousClass生成的类无法通过反射获取其内部信息,且类加载器为Bootstrap ClassLoader,会被认为jdk自带的类
Reference
Last updated
Was this helpful?