文章总结: 文档系统解析Java类加载机制,涵盖静态与动态加载区别、类加载时机及完整生命周期(加载、验证、准备、解析、初始化)。重点分析双亲委派模型的安全作用,指出动态加载(如反射)可能引发RCE风险,并强调漏洞分析中需关注类来源与自定义加载器控制问题。 综合评分: 78 文章分类: 漏洞分析,安全开发,应用安全,WEB安全,安全培训
加载(Loading)【从哪里来?–> 怎么处理? –>到哪里去?】
在该阶段 JVM 将字节码从不同的数据源(Class文件、Jar文件、网络)转为二进制字节流加载到内存中,并生成为一个代表类的 java.lang.Class 对象。
验证(Verification)【主要有什么规范?】
该阶段 JVM 对二进制字节流进行校验,只有符合 JVM 字节码规范的才能被 JVM 执行,主要包含以下几个部分:
- 1. 确保二进制字节流格式符合规范。s
- 2. 所有方法需遵守访问控制关键字限定。
- 3. 方法调用的参数个数和类型是否正确
- 4. 确保变量在使用之前被正确初始化
- 5. 检查变量是否被赋予恰当类型的值
- 6. … 该阶段是保证 JVM 安全的屏障。
准备(Preparation)
该阶段 JVM 对类变量/静态变量(static)分配内存并初始化(给定对应数据类型的默认初始值: 0、0L、null…)
例如:
public String var1 = "test";
public static String var2 = "test2";
public static final String var3 = "test3";
上述代码在准备阶段:
- 1.
var1不会分配内存 - 2.
var2会被分配内存初始值为null - 3.
var3是(static final)常量会被初始化为test3。
解析(Resolution)【处理哪里的数据?完整怎样的转化?】
该阶段将常量池中的符号引用解析为直接引用,简单来说,将”名字”转换为”真实位置”。
例如,代码中编写 com.example.vuln,在编译时只是一个字符串(‘名字’),JVM 并不知道它来自于哪里。等程序运行时,JVM会根据该名字找到类在内存的位置,该结果即为直接引用。
-
• 举例:
Class.forName(userInput)当userInput可控,其作为符号引用,JVM在该阶段会解析并寻找加载对应的类。 -
• 没加载 → 触发类加载(Load → Link → Init)
-
• 已加载 → 直接拿已有的Class对象
初始化(Initialization)【做了什么?什么会触发?】
该阶段为类加载的最后一步,类变量将被赋予代码定义的值。换句话说,初始化阶段是执行类构造器方法,执行类里的 static 代码(也就是 <clinit>())。
比如:
class A {
static int x = 10;
static {
System.out.println("init A");
}
}
初始化时做的就是:
x = 10
执行 static 代码块
- • 初始化的触发需要满足以下某项:
- 1. 创建类的实例(
new xxx()) - 2. 访问 static 变量/方法(非
final) - 3. 反射(
Class.forName()) - 4. 初始化子类(会先初始化父类)
- 5. 程序的入口(
Main方法对应类) 那么,初始化是会执行类里的 static 代码,而这一步可能导致RCE。
类加载器【 加载器类型,对于不同标准怎么统一处理?加载地址 】
JVM 判断类是否相同,不仅看类名,还看是”谁加载的”。
![[类加载器.png]]
类加载器决定从哪里加载类,通过 “双亲委派” 机制
例如:new Test()
JVM 视角: 1.使用ClassLoader –> 找到 Test.class –> 加载 –> 运行
主要的类加载器为以下三种:
- 1.
Bootstrap
- • 加载: Java核心类(
Java.lang.*) - • 特点: 通过 C++ 实现,不是 Java 对象。
String.class.getClassLoader()返回null。
- 2.
Extension
- • 加载:
jre/lib/ext中的拓展库
- 3.
Application
- • 加载: 用户编写的代码(
classpath中) - • 特点:平常接触的多。
自定义类加载器属于第四种,类的加载问题可被控制。
由于 类加载默认是”从上往下找” 双亲委派: 加载 Test 类 –> 想问
Bootstrap… 避免核心类被覆盖的问题并且不同加载器可加载 “同名不同类”。
在漏洞分析过程中,需要关注类从哪里来,是否是自定义的类加载器,关键在于触发类加载的方式。
双亲委派【加载规则】
类加载先交给父加载器,只有父加载器加载不了才自己加载,从而保证类的唯一性和核心类的安全性。
Bootstrap ClassLoader
↑
│
Extension ClassLoader
↑
│
System/Application ClassLoader
↑
│
Custom ClassLoader
这种层次关系被称作为双亲委派模型: 加载一个类 → 先给父加载器 → 一直往上问(直到 Bootstrap) → 都加载不了 → 才自己加载。 这种设计存在两个核心作用:
- 1. 防止核心类被覆盖
- • 例如:
java.lang.String
- 2. 避免重复加载
总结
加载阶段是从不同来源(文件、网络、jar包等)找到二进制字节流,将其转换为方法区中的运行时数据结构,并在堆中生成对应的 Class 对象作为访问入口。 在此过程中 JVM 提供了三种内置类加载器:Bootstrap ClassLoader(负责加载核心类库)、Extension ClassLoader(负责加载扩展类)、Application ClassLoader(负责加载用户 classpath 下的类),此外还支持自定义类加载器。为保证加载的安全性,设计了双亲委派机制,防止核心类被覆盖和重复加载。 验证阶段,验证 Class 对象是否符合 JVM 规范。 准备阶段,将定义的 static 变量进行内存分配并赋默认值(如 int 默认为 0,引用类型默认为 null)。 解析阶段,将常量池中的符号引用替换为直接引用(即内存中的实际地址),使 JVM 能够真正找到对应的类、方法和字段。 初始化阶段,执行类的初始化逻辑,触发条件包括:使用 new 创建对象、调用静态方法或访问静态字段、使用 Class.forName() 加载类、子类初始化时父类尚未初始化、以及 JVM 启动时的主类(含 main 方法的类)。
参考文章
一文彻底搞懂 Java 类加载机制 类加载过程详解 Java 类加载篇(一)ClassLoader 类加载机制总结
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Gh0xE9 me7eorite me7eorite《Java安全必学习类加载机制》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论