类加载

初始化:静态代码块
实例化:构造代码块,无参构造函数

类加载过程

先在方法区找class信息,有则直接调用,没有则使用类加载器加载到方法区(静态区/非静态区)。
静态代码块在类加载时执行,非静态成员不执行
先父类后子类,先静态后非静态
静态方法和非静态方法都是被动调用(不调用就不执行)

双亲委派机制

原理:
类加载器会根据全限定类名判断类是否被加载,若已被加载则直接返回已加载类;如果没有加载,类加载器会先委托父类加载器来加载此类,以此类推直到BooyStrapClassLoader。如果父类无法加载就会交给子类,以此类推…

1
BootStrapClassLoader -> ExtClassLoader -> AppClassLoader -> 自定义类加载器

自定义类加载器

想要实现一个自定义的类加载器,需要继承ClassLoader类,并实现findClass()方法。
ClassLoader中有三个关键方法:loadClass()findClass()defineClass()
loadClass()是入口,它会先查找当前的ClassLoader以及它的双亲里面是否已经加载了目标类,如果没有
找到就会让双亲尝试加载,如果双亲加载不了,就会调用findClass()让自定义加载器来加载目标类。
ClassLoaderfindClass()需要子类来覆盖,不同的加载器有不同的逻辑来获取目标类的字节码
有了字节码后就可以调用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"); //package Load
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();
}
}