Java魔法类:Unsafe应用解析

引用文章

基本介绍

可以通过getUnsafe获取,但是源码这边会出现问题

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
public final class Unsafe {  

static {
Reflection.registerMethodsToFilter(Unsafe.class, Set.of("getUnsafe"));
}

private Unsafe() {}

private static final Unsafe theUnsafe = new Unsafe();
private static final jdk.internal.misc.Unsafe theInternalUnsafe = jdk.internal.misc.Unsafe.getUnsafe();

/**
* Provides the caller with the capability of performing unsafe * operations. * * <p>The returned {@code Unsafe} object should be carefully guarded
* by the caller, since it can be used to read and write data at arbitrary * memory addresses. It must never be passed to untrusted code. * * <p>Most methods in this class are very low-level, and correspond to a
* small number of hardware instructions (on typical machines). Compilers * are encouraged to optimize these methods accordingly. * * <p>Here is a suggested idiom for using unsafe operations:
* * <pre> {@code
* class MyTrustedClass {
* private static final Unsafe unsafe = Unsafe.getUnsafe(); * ... * private long myCountAddress = ...; * public int getCount() { return unsafe.getByte(myCountAddress); } * }}</pre>
*
* (It may assist compilers to make the local variable {@code final}.)
* * @throws SecurityException if the class loader of the caller
* class is not in the system domain in which all permissions * are granted. */ @CallerSensitive
public static Unsafe getUnsafe() {
Class<?> caller = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(caller.getClassLoader()))
throw new SecurityException("Unsafe");
return theUnsafe;
}
......

我们可以通过反射获取unsafe

1
2
3
4
Class<?> name = Class.forName("sun.misc.Unsafe");
Field theUnsafe = name.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);

内存操作

这部分主要包含堆外内存的分配、拷贝、释放、给定地址值操作等方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//分配内存, 相当于C++的malloc函数
public native long allocateMemory(long bytes);
//扩充内存
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);
//获取给定地址值,忽略修饰限定符的访问限制。与此类似操作还有: getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//为给定地址设置值,忽略修饰限定符的访问限制,与此类似操作还有: putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//获取给定地址的byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果为确定的)
public native byte getByte(long address);
//为给定地址设置byte类型的值(当且仅当该内存地址为allocateMemory分配时,此方法结果才是确定的)
public native void putByte(long address, byte x);

Class相关

此部分主要提供Class和它的静态字段的操作相关方法,包含静态字段内存定位、定义类、定义匿名类、检验&确保初始化等。

1
2
3
4
5
6
7
8
9
10
11
12
//获取给定静态字段的内存地址偏移量,这个值对于给定的字段是唯一且固定不变的
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);

对象操作

此部分主要包含对象成员属性相关操作及非常规的对象实例化方式等相关方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//返回对象成员属性在内存地址相对于此对象的内存地址的偏移量
public native long objectFieldOffset(Field f);
//获得给定对象的指定地址偏移量的值,与此类似操作还有:getInt,getDouble,getLong,getChar等
public native Object getObject(Object o, long offset);
//给定对象的指定地址偏移量设值,与此类似操作还有:putInt,putDouble,putLong,putChar等
public native void putObject(Object o, long offset, Object x);
//从对象的指定偏移量处获取变量的引用,使用volatile的加载语义
public native Object getObjectVolatile(Object o, long offset);
//存储变量的引用到对象的指定的偏移量处,使用volatile的存储语义
public native void putObjectVolatile(Object o, long offset, Object x);
//有序、延迟版本的putObjectVolatile方法,不保证值的改变被其他线程立即看到。只有在field被volatile修饰符修饰时有效
public native void putOrderedObject(Object o, long offset, Object x);
//绕过构造方法、初始化代码来创建对象
public native Object allocateInstance(Class<?> cls) throws InstantiationException;

Demo

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
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;  
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import sun.misc.Unsafe;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Scanner;

public class Test {
public static void main(String[] args) throws Exception {
Class<?> name = Class.forName("sun.misc.Unsafe");
Field theUnsafe = name.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
TemplatesImpl templates= (TemplatesImpl) unsafe.allocateInstance(TemplatesImpl.class);
Field name1 = templates.getClass().getDeclaredField("_name");
unsafe.putObject(templates,unsafe.objectFieldOffset(name1),"n1ght");
Field tfactory = templates.getClass().getDeclaredField("_tfactory");
unsafe.putObject(templates,unsafe.objectFieldOffset(tfactory),new TransformerFactoryImpl());
Field bytecodes = templates.getClass().getDeclaredField("_bytecodes");
unsafe.putObject(templates,unsafe.objectFieldOffset(bytecodes),new byte[][]{getPayload()});
templates.getOutputProperties();
}
public static byte[] getPayload() throws NotFoundException, CannotCompileException, IOException {
String s="public MyClassLoader(){Runtime.getRuntime().exec(\"calc\");}";
ClassPool classPool = ClassPool.getDefault();
classPool.importPackage(Scanner.class.getName());
CtClass ctClass = classPool.get(AbstractTranslet.class.getName());
CtClass calc = classPool.makeClass("MyClassLoader");
calc.setSuperclass(ctClass);
CtConstructor ctConstructor = CtNewConstructor.make(s, calc);
calc.addConstructor(ctConstructor);
return calc.toBytecode();
}
}

利用UnSafe绕过jdk17对反射的限制

https://pankas.top/2023/12/05/jdk17-%E5%8F%8D%E5%B0%84%E9%99%90%E5%88%B6%E7%BB%95%E8%BF%87/#%E5%88%A9%E7%94%A8Unsafe%E7%BB%95%E8%BF%87JDK17-%E5%AF%B9%E5%8F%8D%E5%B0%84%E7%9A%84%E9%99%90%E5%88%B6

1
2
3
4
5
6
7
8
Class unsafeClass = Class.forName("sun.misc.Unsafe");
Field field = unsafeClass.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
Module baseModule = Object.class.getModule();
Class currentClass = Main.class;
long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
unsafe.getAndSetObject(currentClass, addr, baseModule);