Java类加载机制在Java安全知识体系中有着非常重要的地位,早在之前分析Shiro反序列化漏洞利用,以及学习蚁剑Java马,都和Java ClassLoader打过交道。笔者打算从把ClassLoader的原理、使用场景、自定义方式等多个方面剖析类加载器在Java安全中设计到的知识。
0x01 ClassLoader 简介
Java语言需要把代码编译成class文件,才能用JVM加载执行,那么ClassLoader就是负责将class文件加载到内存,生成对应的类。
0x1 ClassLoader类加载流程
从上图中可以看出ClassLoader的主要职能,将字节流加载到内存,并使用defineClass加载到JVM生成可以被调用的类。Java源码编译之后生成对应的字节码,字节码的存储形式不只局限于文件,还可以使用访问数据库,URL请求的方式进行获取。存储的字节码还可以使用加密算法进行加密,提高存储安全性。
0x2 常见类加载方式
在之前的《Shiro 反序列化漏洞的一些思考》文章中有提到,两种常见的类加载方式。
1.Class.forName 不能加载原生类型,但其他类型都是支持的
2.Classloader.loadclass 不能加载原生类型和数组类型,其他类型都是支持的
这两种类加载方式虽然在实际过程中有着区别,但是其底层都是使用ClassLoader实现类加载机制的。ClassLoader的加载原理并不复杂,相对复杂的是ClassLoader逻辑父节点和真实父类概念上的理解。逻辑父节点是为了双亲委派机制设计的,这样在类加载的时候可以通过parent属性找到上级ClassLoader进行加载。
0x3 获取ClassLoader
ClassLoader loader = null; loader = Thread.currentThread().getContextClassLoader();//通过当前线程获取 loader = ClassLoader.getSystemClassLoader(); loader = this.getClass().getClassLoader();//通过已加载class获取
大多数时候采用第一种方式获取ClassLoader,有时第三种方式会获取的值为null
0x02 ClassLoader 类关系及类结构
笔者本小节主要介绍ClassLoader的类结构以及类关系,ClassLoader在代码实现上是抽象类。这就意味着在程序中ClassLoader应该有很多的子类,那么ClassLoader及其子类中有什么关键类方法和类属性,在这一小节中进行介绍。
0x1 类关系
类关系问题第一次遇到还是在分析Shiro反序列化的时候,当时使用Tomcat容器启动的Shiro应用,在具体调试过程中发现WebappClassLoaderBase这个Classloader子类继承了URLClassLoader,一开始对于这种想象是不能够理解的。于是对ClassLoader、URLClassLoader、AppClassLoader等概念产生了混淆,笔者根据之前师傅们的研究总结了他们之间的关系,如下图所示
URLClassLoader是类加载机制双亲委托机制中最靠近子类的父类。ExtClassLoader、AppClassLoader、WebappClassLoaderBase等都继承自URLClassLoader。
public class Mytest{ public static void main(String[] arg){ ClassLoader classloader = Mytest.class.getClassLoader(); System.out.println(classloader); ClassLoader classloader1 = classloader.getParent(); System.out.println(classloader1); ClassLoader classloader2 = classloader1.getParent(); System.out.println(classloader2); } }
运行结果如下所示,我们可以发现ExtClassLoader的父类加载器为null,原因是BootStrapClassLoader由C实现,无法载Java代码中体现其类之间的关系,但是在实际使用过程中ExtClassLoader确实会委托顶级类加载器BootStrapClassLoader进行类加载,代码实现如下