资讯动态

ZKar引用句柄系统揭秘:TCReference与Handler如何在序列化流中解析对象引用

发布时间:2026/8/24 17:19:04 来源:尧图企业网站定制
ZKar引用句柄系统揭秘TCReference与Handler如何在序列化流中解析对象引用【免费下载链接】zkarZKar is a Java serialization protocol analysis tool implement in Go.项目地址: https://gitcode.com/gh_mirrors/zk/zkarZKar 是一款用 Go 语言编写的 Java 序列化协议分析工具0xACED 0x0005字节流的解析器与重建器无需 JDK、无需 CGO。本文揭秘 ZKar 的引用句柄系统TCReference与 Handler 如何在序列化流中解析对象引用——这正是理解 Java 序列化对象只写一次、后面反复引用机制的核心。为什么 Java 序列化需要句柄Java 序列化流由ObjectOutputStream写出有一个重要特性共享与循环引用。当一个对象在流中出现第二次时JVM 不会再次完整写出它而是写一个 5 字节的回指0x71 00 7E 00 000x71是TC_REFERENCE标签常量定义见 serz/model.go 中的JAVA_TC_REFERENCE后面 4 字节是句柄Handler即该对象第一次被写出时领取的门牌号例如0x7E0000十进制 8257536因此解析器必须维护一张门牌号 → 对象的登记表这正是 ZKar 中TCReference Handler 系统要解决的问题。快速上手用 ZKar Dump 一个真实载荷克隆仓库后即可体验仓库地址https://gitcode.com/gh_mirrors/zk/zkargit clone https://gitcode.com/gh_mirrors/zk/zkar cd zkar go run main.go dump -f testcases/ysoserial/CommonsCollections6.serCLI 入口实现在 main.godump子命令会把字节流解析成一棵结构树打印出来。对象节点上都能看到Handler字段例如TC_OBJECT - 0x73 Handler - 8257538 ... TC_REFERENCE - 0x71 Handler - 8257538TC_REFERENCE后面跟的 Handler指回的就是之前Handler相同的那个对象——这就是 ZKar 帮你还原的对象引用关系图。TCReference一个 5 字节的回指指针解析TC_REFERENCE的入口是readTCReference位于 serz/tc_reference.go流程只有三步读标签消费 1 字节0x71读句柄按大端序读 4 字节得到 Handler 值查表回指调用stream.GetReference(handler)从登记表中取出对象并根据对象的具体类型TCObject、TCClass、TCClassDesc、TCProxyClassDesc、TCString、TCArray、TCEnum设置引用类型标记TCReference结构体本身并不内嵌对象的副本而是持有指向已有对象的指针。若 Handler 在表中查不到解析器会直接报错object reference %v is not found——这种前向引用在合法流中是不存在的报错能帮你快速定位畸形载荷。Handler流的身份证登记处句柄登记表的维护者是ObjectStreamserz/buffer.go它有三个关键成员成员作用handler当前可分配的句柄号初始为JAVA_BASE_WRITE_HANDLE 0x7E0000references map[uint32]Object句柄 → 对象的登记表AddReference(obj)给对象发放门牌号并登记然后句柄号 1GetReference(handler)按门牌号查表供TCReference回指使用ZKar 严格模拟 Java 写端的行为每个对象在第一次被写出时登记而不是被引用时。以普通对象为例readTCObject读完类指针后立即调用stream.AddReference(obj)serz/tc_object.go字符串serz/tc_string.go、数组、枚举、类描述符、代理类描述符同理。这种发放时机一旦错位回指就会全部对不上——这也是该项目测试套件坚持逐字节回环解析后再序列化必须与原文件完全一致的原因。另外Java 协议定义了TC_RESET0x79用于重置整个句柄上下文ZKar 将其作为一等内容类型处理见 serz/tc_content.go不会与相邻记录合并。引用的另一大场景类描述符复用对象引用并不只出现在流的顶层。Java 允许类描述符被引用多个对象如果是同一个类第二个对象后面只需要写TC_REFERENCE指回类描述符的句柄不必重复写出整个类元数据。这一场景由TCClassPointerserz/tc_classpointer.go承载——它表示一个类的指针可以是全新的类描述符也可以是一个TCReference。当 ZKar 需要读取对象字段时FindClassBag方法会先判断指针类型如果是指向句柄的引用就从登记表里取出真正的类描述符再沿SuperClassPointer递归收集完整类链。如何优雅遍历Walk 防环机制由于引用会造成对象图出现环A 引 B、B 又引 A朴素的递归遍历会死循环。ZKar 的解法很克制TCReference.Walk直接返回不再深入——它指向的对象在前面已经被访问过源码注释写得很明白We dont walk into TCReference, because its field are all the pointer that walked before通用访问器FindObject/FindClassDescserz/walker.go支持按条件搜索任意节点命中后返回哨兵错误StopWalkError短路退出这让你可以方便地回答流里有没有某个类描述符这类问题而不用担心引用成环。字节级回环引用也能被忠实重建ZKar 不只读也能写。TCReference.ToBytes()只做一件事输出0x71 4 字节大端句柄保证序列化载荷在解析 → 重建后逐字节还原。项目内置了大量真实 ysoserial 载荷作为基准testcases/ysoserial/测试断言重建结果与原始文件bytes.Equal——引用句柄系统的正确性正是靠这套黄金基准钉死的。相关源码地图文件职责serz/tc_reference.goTCReference定义与解析serz/buffer.goObjectStream句柄登记表与发放逻辑serz/model.goTC_*标签常量与起始句柄0x7E0000serz/tc_content.go顶层标签分发器引用也是其一serz/tc_classpointer.go类描述符引用与类链收集serz/walker.go防环遍历与节点搜索serz/parser.go流头部魔数版本与主解析循环小结 ZKar 的引用句柄系统用三个概念还原了 Java 序列化的对象引用机制ObjectStream按首次写出即登记的原则发放从0x7E0000起递增的 HandlerTCReference作为 5 字节的回指节点通过查表把 4 字节数字还原成真实的对象指针Walk 防环设计则让带引用的对象图可以安全遍历。理解这套机制后你阅读 ZKar 打印出的任何结构树时都能一眼看懂每个TC_REFERENCE到底指向谁。【免费下载链接】zkarZKar is a Java serialization protocol analysis tool implement in Go.项目地址: https://gitcode.com/gh_mirrors/zk/zkar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价