用途
本技能面向未公开的二进制网络协议逆向工程。它从两个方向同时推进:一是线上流量(wire),展示实际传输的字节;二是客户端(client),展示生成这些字节的代码。两者缺一不可——抓包告诉你什么在变化,客户端告诉你为什么变化。最终交付物是一个足够精确的解析器,能够解码、重放,并最终对协议进行模糊测试。
何时使用
- 专有或自定义的 TCP/UDP 协议,没有公开规范。
- Wireshark 因没有匹配的解析器而将流量显示为原始 Data 字节。
- 必须从抓包加客户端二进制中解码的游戏、物联网、工业或 C2 协议。
- 构建 Wireshark 解析器、Kaitai Struct 规范或 scapy 层,以解析和重放协议。
何时不使用
- 已知/结构化协议——Protobuf 或 gRPC 应使用 attacking-grpc-protobuf;标准协议已有解析器。
- 防御性 PCAP 调查——威胁狩猎、IOC 提取、事件分类——应使用 analyzing-network-traffic。
- 逆向客户端二进制本身(反汇编、反编译)属于 analyzing-binaries;应将其服务于协议分析,然后在此处进行结构化。
- 一旦发现协议已加密,识别或破解加密属于 reviewing-cryptography。
两侧同时入手
从抓包中,通过比较大量消息来建立结构:
- 分帧(Framing)——消息如何知道自身在哪里结束:长度前缀(一个跨消息跟踪载荷大小的字段)、分隔符或固定大小记录。找到长度字段通常是第一个突破口。
- 常量与魔数(Constants and magic)——每条消息中都相同的字节,标记头部、版本字段或类型标签。
- 操作码/消息类型(Opcode / message type)——一个与不同消息形态相关的小字段;按它分组抓包。
- 计数器与序列号(Counters and sequence numbers)——单调递增的字段。
- 校验和(Checksums)——随载荷不可预测变化的尾部字段;针对消息体测试 CRC 变体。
- TLV——许多自定义协议一旦看出模式,就是类型-长度-值三元组。
- 字节序(Endianness)——从已知长度确认:一条 260 字节的消息携带的是 04 01 还是 01 04?
从客户端中,解决抓包无法确定的问题:
- 通过逆向二进制(analyzing-binaries)找到序列化/解析代码,阅读它如何构建和消费消息。
- 用 Frida 挂钩 send/recv(或应用的 socket 封装),在加密前和解密后捕获缓冲区——这是在不破解加密的情况下读取加密协议的方法。
- 如果协议已加密,挂钩明文侧;只有在必须脱离客户端操作时,才用 reviewing-cryptography 识别密码和密钥处理。
构建解析器
将恢复的结构转化为可执行的东西,因为解析器既是理解协议的证明,也是后续一切工作的工具:
- Wireshark 解析器(Lua),逐字段解码实时抓包——针对更多流量验证假设的最快方式。
- Kaitai Struct,以声明式描述二进制格式,并生成多种语言的解析器。
- scapy 自定义层,既能解析也能构造消息,用于重放和模糊测试。
迭代:解码一批,找到无法解析的消息,细化规范。格式错误的消息是你的模型出错的地方,而不是噪声。
然后做什么
一个可用的解析器能够支撑安全相关工作:重放以测试服务器是否验证序列、会话和认证;对单个字段(长度、类型标签、计数)进行模糊测试以发现解析器缺陷;并推理协议级认证——nonce、会话令牌或 MAC 是否真正防止重放和篡改,还是仅作装饰。
应拒绝的合理化借口
- “Wireshark 显示为原始数据,所以没什么可看的。”没有解析器匹配——这是起点,不是死胡同。对比消息以找到分帧和字段,或编写解析器。
- “它加密了,所以我无法逆向。”挂钩客户端的 send/recv,在加密前读取明文。你很少需要破解密码来理解协议。
- “我直接肉眼看看十六进制就行。”结构来自对大量消息的比较——跟踪长度的字段、选择类型的字节。孤立的一条消息会隐藏这一切。
- “解析器基本能用,只有一条消息失败——差不多了。”失败的消息是纠正模型的反而例证。追查它;真正的结构就在那里。
- “重放它无害。”针对在线服务的重放会对该服务产生作用。将其视为需要授权的测试,并在发送前推理重放消息会做什么。
参考资料
- analyzing-binaries——逆向客户端以阅读其序列化代码
- attacking-grpc-protobuf——当协议是 Protobuf/gRPC 而非自定义时
- analyzing-network-traffic——防御性 PCAP 调查,而非协议逆向
- reviewing-cryptography——识别和评估协议的加密