Java
  • About This Book
  • 🍖Prerequisites
    • 反射
      • 反射基本使用
      • 高版本JDK反射绕过
      • 反射调用命令执行
      • 反射构造HashMap
      • 方法句柄
    • 类加载
      • 动态加载字节码
      • 双亲委派模型
      • BCEL
      • SPI
    • RMI & JNDI
      • RPC Intro
      • RMI
      • JEP 290
      • JNDI
    • Misc
      • Unsafe
      • 代理模式
      • JMX
      • JDWP
      • JPDA
      • JVMTI
      • JNA
      • Java Security Manager
  • 👻Serial Journey
    • URLDNS
    • SerialVersionUID
    • Commons Collection 🥏
      • CC1-TransformedMap
      • CC1-LazyMap
      • CC6
      • CC3
      • CC2
    • FastJson 🪁
      • FastJson-Basic Usage
      • FastJson-TemplatesImpl
      • FastJson-JdbcRowSetImpl
      • FastJson-BasicDataSource
      • FastJson-ByPass
      • FastJson与原生反序列化(一)
      • FastJson与原生反序列化(二)
      • Jackson的原生反序列化利用
    • Other Components
      • SnakeYaml
      • C3P0
      • AspectJWeaver
      • Rome
      • Spring
      • Hessian
      • Hessian_Only_JDK
      • Kryo
      • Dubbo
  • 🌵RASP
    • JavaAgent
    • JVM
    • ByteCode
    • JNI
    • ASM 🪡
      • ASM Intro
      • Class Generation
      • Class Transformation
    • Rasp防御命令执行
    • OpenRASP
  • 🐎Memory Shell
    • Tomcat-Architecture
    • Servlet API
      • Listener
      • Filter
      • Servlet
    • Tomcat-Middlewares
      • Tomcat-Valve
      • Tomcat-Executor
      • Tomcat-Upgrade
    • Agent MemShell
    • WebSocket
    • 内存马查杀
    • IDEA本地调试Tomcat
  • ✂️JDBC Attack
    • MySQL JDBC Attack
    • H2 JDBC Attack
  • 🎨Templates
    • FreeMarker
    • Thymeleaf
    • Enjoy
  • 🎏MessageQueue
    • ActiveMQ CNVD-2023-69477
    • AMQP CVE-2023-34050
    • Spring-Kafka CVE-2023-34040
    • RocketMQ CVE-2023-33246
  • 🛡️Shiro
    • Shiro Intro
    • Request URI ByPass
    • Context Path ByPass
    • Remember Me反序列化 CC-Shiro
    • CB1与无CC依赖的反序列化链
  • 🍺Others
    • Deserialization Twice
    • A New Blazer 4 getter RCE
    • Apache Commons Jxpath
    • El Attack
    • Spel Attack
    • C3P0原生反序列化的JNDI打法
    • Log4j
    • Echo Tech
      • SpringBoot Under Tomcat
    • CTF 🚩
      • 长城杯-b4bycoffee (ROME反序列化)
      • MTCTF2022(CB+Shiro绕过)
      • CISCN 2023 西南赛区半决赛 (Hessian原生JDK+Kryo反序列化)
      • CISCN 2023 初赛 (高版本Commons Collections下其他依赖的利用)
      • CISCN 2021 总决赛 ezj4va (AspectJWeaver写字节码文件到classpath)
      • D^3CTF2023 (新的getter+高版本JNDI不出网+Hessian异常toString)
      • WMCTF2023(CC链花式玩法+盲读文件)
      • 第六届安洵杯网络安全挑战赛(CB PriorityQueue替代+Postgresql JDBC Attack+FreeMarker)
  • 🔍Code Inspector
    • CodeQL 🧶
      • Tutorial
        • Intro
        • Module
        • Predicate
        • Query
        • Type
      • CodeQL 4 Java
        • Basics
        • DFA
        • Example
    • SootUp ✨
      • Intro
      • Jimple
      • DFA
      • CG
    • Tabby 🔦
      • install
    • Theory
      • Static Analysis
        • Intro
        • IR & CFG
        • DFA
        • DFA-Foundation
        • Interprocedural Analysis
        • Pointer Analysis
        • Pointer Analysis Foundation
        • PTA-Context Sensitivity
        • Taint Anlysis
        • Datalog
Powered by GitBook
On this page
  • 0x01 类加载器
  • 0x02 双亲委派机制
  • 0x03 双亲委派模型优缺
  • 0x04 破坏双亲委派机制
  • 0x05 Res

Was this helpful?

  1. 🍖Prerequisites
  2. 类加载

双亲委派模型

Previous动态加载字节码NextBCEL

Last updated 1 year ago

Was this helpful?

0x01 类加载器

类加载器:将Java字节码加载为**java.lang.Class实例**

特点:

  • 动态加载。可在程序运行过程中动态按需加载字节码(字节码可以是jar、war、远程和本地.class文件)

  • 类加载器加载类时,该类所有的依赖类都由这个类加载器一并加载。

类的唯一性由加载它的类加载器和类本身决定,只有两个类是被同一类加载器加载的前提下,比较两个类是否相等才有意义(equals、isInstance、instanceof)。即使两个类源于同一Class文件,被同一JVM加载,只要加载它们的类加载器不同,这两个类就不同。

类加载器可分为两种:

  • 启动类加载器:C++实现,虚拟机自身的一部分

  • 继承自java.lang.ClassLoader的类加载器,包括扩展类加载器、应用程序类加载器、自定义类加载器

启动类加载器(Bootstrap ClassLoader):负责加载<JAVA_HOME>\lib目录。启动类加载器无法被Java程序直接引用

扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录。该类加载器由sun.misc.Launcher$ExtClassLoader实现。扩展类加载器由启动类加载器加载,其父类加载器为启动类加载器(parent=null)

应用程序类加载器(Application ClassLoader):负责加载用户类路径ClassPath上所指定的类库,由sun.misc.Launcher$App-ClassLoader实现。开发者可直接通过java.lang.ClassLoader中的getSystemClassLoader()方法获取应用程序类加载器,所以也可称它为系统类加载器。应用程序类加载器也是启动类加载器加载的,但是它的父类加载器是扩展类加载器。在一个应用程序中,系统类加载器一般是默认类加载器。

  • BootStrap ClassLoader

由于启动类加载器(Bootstrap ClassLoader)是由C++实现的,而C++实现的类加载器在Java中是没有与之对应的类的,所以拿到的结果是null。

  • Extension ClassLoader

  • Application ClassLoader

0x02 双亲委派机制

JVM并非在启动时就把加载了所有.class文件,而是在程序运行过程中动态按需加载。除了启动类加载器外,其他所有类加载器都需要继承抽象类ClassLoader

public abstract class ClassLoader {
    // The parent class loader for delegation
    private final ClassLoader parent;
    
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException{
        // First, check if the class has already been loaded
        Class<?> c = findLoadedClass(name);
        // if the class is not loaded
        if (c == null) {
          // 委派给父加载器去加载(此处递归调用)
          if (parent != null) {
             c = parent.loadClass(name, false);
           } else {
             // 若父加载器为null,检查BootStrap加载器是否加载过
             c = findBootstrapClassOrNull(name);
           }
        }
        if (c == null) {
            // 父加载器没加载成功,调用findClass自己去加载
            c = findClass(name);
        }
        return c;
    }
    
    protected Class<?> findClass(String name){
       //1. 根据传入的类名name,到在特定目录下去寻找类文件,把.class文件读入内存
       //2. 调用defineClass将字节数组转成Class对象
       return defineClass(buf, off, len);
    }
    
    // 将字节码数组解析成一个Class对象
    protected final Class<?> defineClass(byte[] b, int off, int len){
       ...
    }
}
  • 每个类加载器都持有一个 parent 字段,指向父加载器。(AppClassLoader的parent是ExtClassLoader,ExtClassLoader的parent是BootstrapClassLoader,但ExtClassLoader的parent=null。)

  • loadClass方法实现了双亲委派机制:先检查这个类是否被加载过,若未加载过委派给父加载器加载,递归调用,一层层向上委派,若到最顶层的类加载器(BootStrap ClassLoader)无法加载该类,再一层层向下委派给子类加载器加载

  • findClass:找到字节码文件并读入得到字节码数组,传入defineClass实现类加载

  • defineClass:将Java类的字节码解析为java.lang.Class实例

0x03 双亲委派模型优缺

优点:

  1. 安全:防止用户伪造的恶意类覆盖原系统类。例如类java.lang.Object,它存放在rt.jar之中,无论哪个类加载器要加载这个类,最终都是委派给启动类加载器加载

  2. 避免多个类加载器将同一类重复加载

缺点:

典型问题:加载 SPI 实现类的场景

比如 JNDI服务,它的代码由启动类加载器去加载(在 JDK 1.3 时放进 rt.jar),但 JNDI 的目的就是对资源进行集中管理和查找,它需要调用独立厂商实现的部署在应用程序的 classpath 下的 JNDI 接口提供者(SPI)的代码,上面说到类加载器加载类时,该类所有的依赖类都由这个类加载器一并加载。启动类加载器不可能去加载ClassPath下的类。此时就只能打破双亲委派机制。

0x04 破坏双亲委派机制

由上面知道实现双亲委派机制主要是loadClass方法,若想打破双亲委派机制,自定义类加载器继承ClassLoader,重写loadClass方法即可

java中所有类都继承了Object,而加载自定义类显然还会加载其父类,而最顶级的父类Object是java官方的类,只能由BootstrapClassLoader加载,java禁止用户用自定义的类加载器加载java.开头的官方类。所以就算破坏双亲委派机制,BootStrap ClassLoader还是绕不过的,但可以跳过AppClassLoader和ExtClassLoader

另一种破坏双亲委派机制的方法是利用线程上下文类加载器,其实是一种类加载器传递机制。可以通过java.lang.Thread#setContextClassLoader方法给一个线程设置上下文类加载器,在该线程后续执行过程中就能把这个类加载器取(java.lang.Thread#getContextClassLoader)出来使用。

如果创建线程时未设置上下文类加载器,将会从父线程(parent = currentThread())中获取,如果在应用程序的全局范围内都没有设置过,就默认是应用程序类加载器。

0x05 Res

Java双亲委派模型:为什么要双亲委派?如何打破它?破在哪里?_徐同学呀的博客-CSDN博客_java为什么用双亲委派
面试必问:什么是双亲委派模型?-51CTO.COM
深入理解Java类加载 - czwbig - 博客园 (cnblogs.com)
image-20230205112731576
image-20230205112644935
image-20230205113000126
image-20230205113101924