前言
在java反序列化时由于readObject会自动执行代码,在java类加载的时候也会执行代码,从这里可以扩宽攻击的知识面
Javac原理: javac是用于将源码文件.java编译成对应的字节码文件.class
类加载
先在方法区找class信息,有的话直接调用,没有的话则使用类加载器加载到方法区(静态成员放在静态区,非静态成功放在非静态区),静态代码块在类加载时自动执行代码,非静态的不执行;先父类后子类,先静态后非静态;静态方法和非静态方法都是被动调用,即不调用就不执行
)
给出示例代码
import java.io.Serializable; |
分为了四个部分静态代码块,静态方法,构造代码块,构造方法,下面给出不同情况的不同输出
new Person(); |
在 初始化时调用静态代码块,在 使用时才调用其他函数等
初始化:静态代码块
实例化:构造代码块,无参构造函数
双亲委派机制
Java虚拟机 - 双亲委派机制 - 掘金 (juejin.cn)
面试官:说说双亲委派模型? - 掘金 (juejin.cn)
简单说就是 Java虚拟机对class文件采用的是 按需加载 的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是 双亲委派模式
模型的原理就是
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归请求最终将到达顶层的启动类加载器。
如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载
就是想上请求加载,上面没法加载,那就自己加载
作用就是
保护程序安全,防止核心API被随意篡改。在java.lang包下,开发者自定义的类中的main方法不允许执行,防止恶意代码对程序产生破坏。
避免类的重复加载。一个类只会被加载一次。
- 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自
java.lang.ClassLoader
。 - 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()
来获取它。
动态类加载方法
Class.forName()
- 初始化/不初始化
Class.forName("Person"); |
跟进forName,
跟进forName0,四个参数,(类名,是否初始化,类加载器,不重要)
其实forName有两个最后都是调用forName0,这个就是自己可控是否初始化
判断一下,静态代码块是不是在初始化时调用
ClassLoader cl = ClassLoader.getSystemClassLoader(); |
确实是在类加载初始化时进行的调用
ClassLoader.loadClass
- 不进行初始化
- 存在利用方式
ClassLoader cl = ClassLoader.getSystemClassLoader(); |
也就是说在利用loadClass进行类加载时是不进行初始化的
跟进调试,根据双亲委派模型,首先进入的是application的AppClassLoader,因为jdk源码问题暂时不跟了
底层调用大概流程
继承流程: ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader
调用流程: loadClass->findClass(重写的方法)->defineClass(从字节码加载类)
利用方式一:URLClassLoader任意类加载
利用URLClassLoader
支持协议:http,https,ftp,mailto,file,jar,netdoc
根据URL的构造方法进行构造
将 calc.java 编译为class文件
import java.io.IOException; |
加载恶意类
import java.net.URL; |
方式二:ClassLoader.defineClass字节码加载任意类
- 利用defineClass
- 私有->反射
从字节码加载类,不出网情况下利用方式
import java.lang.reflect.Method; |
方式三:Unsafe.defineClass
- 利用defineClass
- public却无法直接生成
等解决源码问题,再跟进调试
import sun.misc.Unsafe; |
参考
Java类加载机制和对象创建过程 - SegmentFault 思否