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动态加载机制
类加载方式
类名.classClass.forName("类名")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.Serializable或java.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();
反序列化要求
- 被反序列化的类必须存在
serialVersionUID值必须一致- 不会调用类的构造方法
安全注意事项
反序列化漏洞
- 攻击者可能构造恶意序列化数据,在反序列化时执行危险操作
- 防御措施:
- 不要反序列化不可信数据
- 使用白名单验证反序列化的类
- 使用
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反序列化安全涉及多个核心技术点:
- 类加载机制和双亲委派模型
- 反射API的动态调用能力
- 序列化/反序列化流程
- 命令执行和文件操作的基础方法
理解这些基础概念和机制是分析和防御Java反序列化漏洞的前提。在实际开发中,应特别注意这些功能的安全使用方式,避免引入安全风险。