类加载
初始化:静态代码块
实例化:构造代码块,无参构造函数
类加载过程
先在方法区找class信息,有则直接调用,没有则使用类加载器加载到方法区(静态区/非静态区)。
静态代码块在类加载时执行,非静态成员不执行
先父类后子类,先静态后非静态
静态方法和非静态方法都是被动调用(不调用就不执行)
双亲委派机制
原理:
类加载器会根据全限定类名判断类是否被加载,若已被加载则直接返回已加载类;如果没有加载,类加载器会先委托父类加载器来加载此类,以此类推直到BooyStrapClassLoader。如果父类无法加载就会交给子类,以此类推…
1
| BootStrapClassLoader -> ExtClassLoader -> AppClassLoader -> 自定义类加载器
|
自定义类加载器
想要实现一个自定义的类加载器,需要继承ClassLoader类,并实现findClass()方法。
ClassLoader中有三个关键方法:loadClass()、findClass()、defineClass()
loadClass()是入口,它会先查找当前的ClassLoader以及它的双亲里面是否已经加载了目标类,如果没有
找到就会让双亲尝试加载,如果双亲加载不了,就会调用findClass()让自定义加载器来加载目标类。
ClassLoader的findClass()需要子类来覆盖,不同的加载器有不同的逻辑来获取目标类的字节码
有了字节码后就可以调用defineClass()将字节码转换成Class对象。
动态加载类方法
Class.forname可以选择进行初始化或不初始化
ClassLoader.loadClass不进行初始化
继承关系:
ClassLoader -> SecureClassLoader -> URLClassLoader -> AppClassLoader
调用关系:
LoadClass -> findClass(重写的方法) -> defineClass(从字节码加载类)
java 9 及以上会报错但是也可弹计算器
URLClassLoader:
URLClassLoader任意类加载:file/http/jar
Test.java:
1 2 3 4 5 6 7 8 9 10 11
| import java.io.IOException;
public class Test { static { try { Runtime.getRuntime().exec("calc"); } catch (IOException e) { throw new RuntimeException(e); } } }
|
file:
LoadClassTest.java:
1 2 3 4 5 6 7 8 9 10
| import java.net.URL; import java.net.URLClassLoader;
public class LoadClassTest { public static void main(String[] args) throws Exception { URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///E:\\WebSecurity\\JavaSecurity\\unserialize\\out\\production\\unserialize\\Load\\")}); Class<?> test = urlClassLoader.loadClass("Load.Test"); test.newInstance(); } }
|
编译好Test.class并移动位置,将Test.java删除后仍可弹出计算器(我的删之前可以弹出,删了无法弹出布吉岛为什莫)
http:
1 2 3 4 5 6 7 8 9 10
| import java.net.URL; import java.net.URLClassLoader;
public class LoadClassTest { public static void main(String[] args) throws Exception { URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://localhost:9999")}); Class<?> test = urlClassLoader.loadClass("Load.Test"); test.newInstance(); } }
|
1
| python -m http.server 9999
|

jar
1
| jar:file:///D:\\xxxx\\Test.jar!/
|
http更常用一点
ClassLoader
ClassLoader.defineClass 字节码加载任意类 私有
LoadClassTest.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import java.lang.reflect.Method; import java.nio.file.Files; import java.nio.file.Paths;
public class LoadClassTest { public static void main(String[] args) throws Exception { ClassLoader cl = ClassLoader.getSystemClassLoader(); Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", byte[].class, int.class, int.class); defineClassMethod.setAccessible(true); byte[] code = Files.readAllBytes(Path.of("E:\\WebSecurity\\JavaSecurity\\unserialize\\out\\production\\unserialize\\Load\\Test.class")); Class df = (Class)defineClassMethod.invoke(cl, code, 0, code.length); df.newInstance(); } }
|

unsafe
unsafe.defineClass 字节码加载 public类不能直接生成(Spring 里面可以直接生成)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import sun.misc.Unsafe;
import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Paths;
public class LoadClassTest { public static void main(String[] args) throws Exception { ClassLoader cl = ClassLoader.getSystemClassLoader(); byte[] code = Files.readAllBytes(Paths.get("E:\\WebSecurity\\JavaSecurity\\unserialize\\out\\production\\unserialize\\Load\\Test.class")); Class unsafeClass = Unsafe.class; Field theUnsafeField = unsafeClass.getDeclaredField("theUnsafe"); theUnsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafeField.get(null); Class c2 = (Class)unsafe.defineClass("Load.Test",code, 0 , code.length,cl,null); c2.newInstance(); } }
|
