本文目录导读:

在Java反序列化案例中还原对象,通常有几种不同的场景和对应方法,我来系统梳理一下:
基本还原方法
标准反序列化(有源码且类可用)
// 假设你有完整的类定义
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
// getters/setters
}
// 反序列化还原
public static Object deserialize(byte[] data) {
try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis)) {
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
只有序列化数据,没有源码
这是CTF和安全分析中的常见情况:
public static void analyzeObject(byte[] data) {
try {
// 先读取数据头
try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
ObjectInputStream ois = new ObjectInputStream(bis)) {
// 读取对象类描述
ObjectStreamClass desc = ois.readObject().getClass().getObjectStreamClass();
System.out.println("类名: " + desc.getName());
System.out.println("序列化UID: " + desc.getSerialVersionUID());
// 读取字段信息
ObjectStreamField[] fields = desc.getFields();
for (ObjectStreamField field : fields) {
System.out.println("字段: " + field.getName() + " 类型: " + field.getTypeCode());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
使用第三方工具还原
SerialKiller / Serianalyzer
<!-- Maven依赖 -->
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.12.9</version>
</dependency>
ysoserial的Deserializer工具
# 使用ysoserial分析序列化数据 java -cp ysoserial.jar ysoserial.Deserializer < serialized_data.bin
专业的序列化分析工具
SerializationDumper - 最常用的序列化数据分析工具:
# 下载并运行 java -jar SerializationDumper.jar -r serialized_data.bin
输出示例:
STREAM_MAGIC: 0xac ed
STREAM_VERSION: 5
TC_OBJECT: 0x73
TC_CLASSDESC: 0x72
className: "com.example.User"
serialVersionUID: 0x1234567890abcdef
...
动态生成类来还原
当没有原始类时,可以用字节码工具动态生成:
import javassist.*;
public class DynamicClassLoader {
public static Class<?> createDynamicClass(String className,
Map<String, Class<?>> fields) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass(className);
// 实现Serializable接口
ctClass.addInterface(pool.get("java.io.Serializable"));
// 添加字段
for (Map.Entry<String, Class<?>> entry : fields.entrySet()) {
CtField field = new CtField(pool.get(entry.getValue().getName()),
entry.getKey(), ctClass);
field.setModifiers(Modifier.PRIVATE);
ctClass.addField(field);
}
return ctClass.toClass();
}
}
实战案例:还原恶意序列化数据
场景:分析反弹Shell的序列化payload
public class PayloadAnalyzer {
public static void analyzePayload(byte[] payload) {
try {
// 1. 尝试安全反序列化(不执行恶意代码)
SafeObjectInputStream sois = new SafeObjectInputStream(
new ByteArrayInputStream(payload));
// 2. 记录对象图结构
ObjectGraphBuilder builder = new ObjectGraphBuilder();
Object obj = sois.readObject();
// 3. 分析对象链
analyzeObjectChain(obj);
} catch (Exception e) {
// 4. 如果失败,使用hex分析
analyzeHexPayload(payload);
}
}
private static void analyzeObjectChain(Object obj) {
// 递归分析对象引用链
System.out.println("根对象类: " + obj.getClass().getName());
// 使用反射查看字段
for (Field field : obj.getClass().getDeclaredFields()) {
field.setAccessible(true);
try {
Object value = field.get(obj);
System.out.println("字段: " + field.getName() +
" = " + (value != null ? value.getClass().getName() : "null"));
} catch (Exception e) {
System.out.println("无法访问字段: " + field.getName());
}
}
}
}
特殊场景处理
处理自定义readObject
// 如果类有自定义readObject,需要模拟执行
public class CustomDeserializer {
public static Object deserializeWithCustom(byte[] data) {
// 创建自定义ClassLoader
URLClassLoader classLoader = new URLClassLoader(new URL[]{});
try (ObjectInputStream ois = new ObjectInputStream(
new ByteArrayInputStream(data)) {
// 重写resolveClass来加载动态类
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws ClassNotFoundException {
// 可以在这里加载从网络下载的类
return classLoader.loadClass(desc.getName());
}
}) {
return ois.readObject();
}
}
}
处理代理对象
// 分析动态代理对象
public static void analyzeProxy(Object obj) {
if (Proxy.isProxyClass(obj.getClass())) {
InvocationHandler handler = Proxy.getInvocationHandler(obj);
System.out.println("代理接口: " +
Arrays.toString(obj.getClass().getInterfaces()));
// 获取InvocationHandler的内部状态
if (handler instanceof AnnotationInvocationHandler) {
// 特定处理
}
}
}
最佳实践建议
- 安全第一:在分析未知序列化数据时,使用隔离环境(沙箱或虚拟机)
- 逐步还原:从数据结构分析开始,到字段提取,最后才是对象重建
- 工具辅助:优先使用SerializationDumper等专业工具
- 记录日志:详细记录还原过程中的异常和特殊行为
常见问题:
- 序列化版本不一致:可以尝试修改serialVersionUID
- 类在classpath中不存在:使用动态生成类或自定义ClassLoader
- 嵌套对象复杂:使用递归分析,建立对象图
需要我详细解释某个具体场景吗?比如处理特定的CTF题目或安全分析案例?