Java反序列化基础篇-类加载器
字数 1745 2025-08-12 12:08:18
Java类加载机制与反序列化基础教学文档
1. 类加载器概述
类加载器(ClassLoader)是Java虚拟机(JVM)的重要组成部分,负责将Class文件加载到JVM中,使其能够被程序使用。
1.1 类加载器的主要功能
- 加载Class文件
- 将抽象类实例化(如
Student student = new Student();)
1.2 类加载器层次结构
Java中有四种主要的类加载器:
-
引导类加载器(BootstrapClassLoader)
- 底层由C++编写,是JVM的一部分
- 不继承java.lang.ClassLoader类
- 没有父加载器
- 负责加载核心Java库(JVM本身)
- 加载路径:
/jre/lib/rt.jar - 只加载包名为java、javax、sun等开头的类
-
扩展类加载器(ExtensionsClassLoader)
- 由
sun.misc.Launcher$ExtClassLoader实现 - 加载路径:
/jre/lib/ext或java.ext.dirs指定的目录 - 用于加载Java扩展库
- 由
-
应用程序类加载器(AppClassLoader)
- 由
sun.misc.Launcher$AppClassLoader实现 - 通过
java.class.path或Classpath环境变量加载类 - 通常用于加载应用程序类
- 可通过
ClassLoader.getSystemClassLoader()获取
- 由
2. 双亲委派机制
双亲委派机制是Java类加载的安全机制,确保核心类库的安全性。
2.1 双亲委派工作原理
类加载器在被调用时(new class),按以下顺序查找类:
BOOT(引导类加载器) → EXC(扩展类加载器) → APP(应用程序类加载器)
2.2 双亲委派示例
错误示例:
package java.lang;
public class String {
public String toString(){
return "hello";
}
public static void main(String[] args) {
String s = new String();
s.toString();
}
}
- 会报错,因为与BOOT加载器中的String冲突
正确示例:
package src.DynamicClassLoader;
public class Student {
public String toString(){
return "Hello";
}
public static void main(String[] args) {
Student student = new Student();
System.out.println(student.getClass().getClassLoader());
System.out.println(student.toString());
}
}
- 可以正常加载,因为BOOT和EXC加载器中都没有Student类
3. 代码块加载顺序
Java中有四种主要代码块:
- 静态代码块:
static{} - 构造代码块:
{} - 无参构造器:
ClassName() - 有参构造器:
ClassName(String name)
3.1 不同场景下的加载顺序
场景一:实例化对象
Person person = new Person();
加载顺序:
- 静态代码块
- 构造代码块
- 构造器(根据实例化方式调用无参或有参构造器)
场景二:调用静态方法
Person.staticAction();
加载顺序:
- 静态代码块
- 静态方法
场景三:对静态成员变量赋值
Person.staticVar = 1;
加载顺序:
- 静态代码块
场景四:使用class获取类
Class c = Person.class;
结果:
- 不会加载类,无输出
场景五:使用forName获取类
三种形式:
Class.forName("src.DynamicClassLoader.Person")- 会调用静态代码块
Class.forName("src.DynamicClassLoader.Person", true, ClassLoader.getSystemClassLoader())- 会调用静态代码块
Class.forName("src.DynamicClassLoader.Person", false, ClassLoader.getSystemClassLoader())- 不会调用静态代码块
场景六:使用ClassLoader.loadClass()
ClassLoader.getSystemClassLoader().loadClass("com.xiinnn.i.test.Person");
结果:
- 不会进行类的初始化,无输出
- 如果后续使用
newInstance()初始化,则与场景一相同
4. 类加载在反序列化中的应用
在Java反序列化中,理解类加载机制至关重要:
- 动态加载类:攻击者可能利用类加载机制加载恶意类
- 绕过安全限制:理解双亲委派机制有助于理解某些安全限制的绕过方式
- 代码执行:通过控制类加载过程可能实现任意代码执行
4.1 关键点
- 类加载器决定了类的可见性和安全性
- 双亲委派机制是Java安全模型的重要组成部分
- 不同加载方式(如forName与loadClass)的行为差异可能被利用
- 静态代码块在类初始化时执行,是反序列化攻击的常见切入点
5. 防御措施
- 严格限制反序列化的类白名单
- 使用SecurityManager限制类加载
- 及时更新Java运行环境
- 避免使用不安全的反序列化方法
理解这些类加载机制对于Java安全开发和反序列化漏洞分析至关重要。