1. 如何获取Unsafe
Unsafe unsafe = Unsafe.getUnsafe();
通过调用静态方法Unsafe.getUnsafe()就可以获取一个Unsafe的实例,但是在我们自己的类中执行同样的代码却会抛出SecurityException异常。
Exception in thread "main" java.lang.SecurityException: Unsafe
那么为什么会这样呢?其实点进去看源码就知道了。
@CallerSensitive
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
}
如果调用该方法的类不是被系统类加载器加载的就会抛出异常,通常情况下开发者所开发的Java类都会被应用类加载器进行加载。
在Unsafe类中存在一个Unsafe的实例theUnsafe,该实例是类私有成员,并且在Unsafe类的静态代码块中已经被初始化了,因此我们可以通过反射的方式尝试获取该成员的属性:
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
2. Unsafe实现的功能
2.1 绕过类构造函数完成对象创建
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
AtomicFieldUpdaterDemo.Alex alex = (AtomicFieldUpdaterDemo.Alex) unsafe.allocateInstance(AtomicFieldUpdaterDemo.Alex.class);
System.out.println(alex.getSalary()==0);
}
2.2 直接修改内存数据
public class UnsafeDemo {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
A a = new A();
System.out.println(a.canAccess(10));
System.out.println(a.canAccess(1));
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
Field accessNo = a.getClass().getDeclaredField("accessNo");
unsafe.putInt(a,unsafe.objectFieldOffset(accessNo),20);
System.out.println(a.canAccess(20));
}
static class A {
private int accessNo = 1;
public boolean canAccess(int num) {
return this.accessNo == num;
}
}
}
2.3 类的加载
public static void main(String[] args) throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
byte[] content = getClassContent();
Class<?> aClass = unsafe.defineClass(null, content, 0, content.length, null, null);
Object geta = aClass.getMethod("geta").invoke(aClass.newInstance(), null);
System.out.println(geta);
}
public static byte[] getClassContent() throws Exception {
File file = new File("C:\\Users\\Administrator\\Documents\\Tencent Files\\1466425779\\FileRecv\\LoadDemo.class");
try (FileInputStream inputStream = new FileInputStream(file)) {
byte[] content = new byte[(int) file.length()];
inputStream.read(content);
return content;
}
}