Turker
发布于 2025-07-13 / 13 阅读
0
0

Java安全——ClassLoader与双亲委派模型

Java安全——ClassLoader与双亲委派模型

#Java

一、ClassLoader

1.1 什么是ClassLoader?

整个Java代码执行过程中很关键的一步是从Java字节码到JVM虚拟机,这个过程就称为类加载过程,简称ClassLoader。任何一个Java类必须经过ClassLoader加载之后,才能被调用和执行。

对于一般的Java开发人员来说,他们并不太关心ClassLoader类加载机制,但是ClassLoader是学习Java安全中的一个极重要的概念,ClassLoader为攻击者提供了一种执行任意Java代码的途径,有点类似于PHP中的eval。当然ClassLoader的用法要比eval复杂很多

1.2 有哪些ClassLoader?

JDK自带的ClassLoader有三个,分别是 BootstrapClassLoaderExtClassLoaderAppClassLoader。查看类加载器可以通过 Class对象的 getClassLoader函数实现。

三者之间存在父子关系,BootstrapClassLoader加载器是 ExtClassLoader的父加载器,ExtClassLoader加载器是 AppClassLoader加载器的父加载器。

1.2.1 BootstrapClassLoader

BootstrapClassLoader:引导类加载器,属于最顶层的类加载器,主要用于加载java的核心库,包括 rt.jarresources.jar等。引导类加载器加载的都是jdk原生携带的核心库,通过C/C++语言实现,引导类加载器的实现逻辑是JVM的一部分,不能通过java代码控制引导类加载器的行为。

一般而言,以java、javax和sun开头的类对应的类加载器是 BootstrapClassLoader。例如我们经常说的JNDI注入时用到的ldap协议对应的实现类 javax.naming.ldap.LdapName,查看此类对应的类加载器,如图所示。这里需要说明的是,如果获取到的类加载器为null,则表示类加载器是引导类加载器 BootstrapClassLoaderJava安全-10.png
究竟有哪些类的加载器是属于引导类加载器呢?通过 System.getProperty("sun.boot.class.path")查看全局属性可以获取引导类加载器加载的类对应的路径,如图所示。Java安全-11.png

1.2.2 ExtClassLoader

ExtClassLoader:扩展类加载器,一般属于JDK自带的一些非核心功能实现类。ExtClassLoader是由java代码实现的,可以被其他java程序调用。以类 jdk.internal.dynalink.beans.BeansLinker为例来查看对应的加载器,如图所示。
Java安全-12.png
与引导类加载器类似,扩展类加载器加载的类路径也保存在系统属性中,可以直接通过 System.getProperty("java.ext.dirs")查看对应属性的方式查看扩展类加载器加载的类路径。
Java安全-13.png

1.2.3 AppClassLoader

AppClassLoader:应用类加载器。应用类加载器是java应用中最常见的加载器,在java项目中自己编写的java类和引入的第三方类都由应用类加载器加载到JVM中。以类 com.sun.deploy.uitoolkit.PluginUIToolKit类为例查看对应的加载器,如图所示。
Java安全-14.png应用类加载器会加载当前应用classpath中的所有类,也可以通过 System.getProperty("java.class.path")读取系统属性值来查看应用类加载器对应的加载路径。
Java安全-15.png

1.3 ClassLoader原理——双亲委派模型

我们发现有部分类既被引导类加载器加载,又被应用类加载器加载。那么这种被两个类都加载的类怎么算呢?以哪个类加载器为标准,还是会在内存加载两次?

1.3.1 双亲委派模型

以一句话来总结双亲委派模型就是“总是优先把加载类的任务交给父加载器”。例如,如果要加载一个类 com.util.xxx,那么加载顺序应该是这样的:

  1. 首先看自定义的加载器(如果没有自定义加载器,则直接到步骤2)中是否已经加载了类com.util.xxx,如果已经加载过,就直接返回,否则交给 AppClassLoader
  2. 查看 AppClassLoader是否已经加载了 com.util.xxx,如果已经加载过,就直接返回,否则交给 ExtClassLoader
  3. 查找 ExtClassLoader是否已经加载了 com.util.xxx,如果已经加载过,就直接返回,否则交给 BootstrapClassLoader
  4. 查找 BootstrapClassLoader是否已经加载了 com.util.xxx,如果已经加载过,就直接返回,否则从 BootstrapClassLoader的加载路径中查找是否存在目标类。如果 BootstrapClassLoader没有找到目标类,则交给 ExtClassLoader
  5. ExtClassLoader的加载路径中查找是否存在目标类,如果 ExtClassLoader没有找到目标类,则交给 AppClassLoader
  6. AppClassLoader的加载路径中查找是否存在目标类,如果 AppClassLoader没有找到目标类,则交给自定义ClassLoader。
  7. 从自定义ClassLoader对应的路径查找是否存在目标类,如果自定义ClassLoader没有找到目标类,则抛出异常。
    从上面的过程可以看出,整个类的加载过程中总是优先使用父类加载器进行加载,如果父类加载器找到了目标类,就直接返回结果。那么我们再来回答一下本小节开头提出的问题,如果AppClassLoader加载器和BootstrapClassLoader加载器都可以加载某个类,JVM会优先选择通过BootstrapClassLoader加载器来加载目标类,内存中也不会保留两份目标类的加载对象。

1.3.2 源码解析

待施工

1.4 冰蝎Webshell分析

待施工


评论