unsafe是jdk里面非常有意思的函数,不仅仅是名字。具体功能大家可以搜一下看看,简单来说就是,这个类提供了直接操作硬件的方法。
我这边简单利用unsafe的putObject方法,实现一次类混淆,有点儿意思。
代码如下:
Dingo.java : (作用是跑一个计算器)
package ding.yang.controller;
import java.io.IOException;
import java.io.Serializable;
public class Dingo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public void fuck() {
ProcessBuilder pb = new ProcessBuilder("calc");
try {
pb.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Yang.java: (作用是跑一个记事本)
package ding.yang.controller;
import java.io.IOException;
import java.io.Serializable;
public class Yang implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public void fuck() {
ProcessBuilder pb = new ProcessBuilder("notepad");
try {
pb.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Hello.java:
package ding.yang.controller;
public class Hello {
public Dingo dingo;
public Hello(Dingo dingo){
this.dingo = dingo;
}
}
接着利用putObject,将hello.java类中的dingo这个field,替换为yang.java
package ding.yang.controller;
import java.io.IOException;
import java.lang.reflect.Field;
import java.security.AllPermission;
import java.security.ProtectionDomain;
import sun.misc.Unsafe;
public class TestUnsafe {
public static void main(String[] avgs) throws NoSuchFieldException,
SecurityException, IllegalArgumentException,
IllegalAccessException, IOException {
// 获取unsafe 的实例
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Hello hello = new Hello(new Dingo());
Yang yang = new Yang();
Field f = hello.getClass().getDeclaredField("dingo");
unsafe.putObject(hello, unsafe.objectFieldOffset(f), yang);
hello.dingo.fuck(); // 这里是弹计算器呢还是记事本呢?
}
}
总结:
1、上述代码最后是弹出了记事本,因为我们采用直接操作内存对象的方式,将hello类中的dingo对象,替换成了yang对象。
2、还有一个需要注意的点是,unsafe实例并不能直接获取,因为在unsafe中,判断了当前的classloader如果不是null,也就是root的classloader,会抛出一个securityexception异常,所以我们利用反射的方式,将private置为public,然后获取
3、上述代码当然只能在trust code环境里面运行了。