第 24 章 协议逆向(网络协议/私有协议)
协议逆向 = 搞清楚两台机器之间"按什么规矩说话"。公开协议有文档,私有/自定义协议要靠逆向。本章核心是协议还原的架构化流程,不是某个工具。
📍 知识点地图 | 主题:协议逆向 | 前置:第9章 | 后续:第67章 | 核心概念:帧结构、字段还原、序列化识别、重放
24.1 协议逆向的对象与动机
对象: TCP/UDP 应用层协议、WebSocket、gRPC/Protobuf、C2 通信、
自定义二进制协议、文件格式、设备与上位机通信
动机: 重放请求(API 风控)、理解 C2 行为、写互操作客户端、
挖掘协议层漏洞(校验/重放/越权)、还原数据格式
24.2 协议要素模型(先知道协议由什么组成)
任何协议都可分解为以下要素,逆向时逐项还原:
| 要素 | 含义 | 还原方法 |
|---|---|---|
| 帧结构 | 消息边界(定长/长度字段/分隔符) | 抓包看字段规律 |
| 字段布局 | 各字段偏移与语义 | 对比不同请求 |
| 类型系统 | 整型/字符串/嵌套结构/变长 | 长度字段+字段特征 |
| 编码 | 明文/二进制/序列化格式 | 特征识别 |
| 状态机 | 会话阶段(握手/请求/响应/心跳) | 多轮交互观察 |
| 加密/签名 | 载荷保护方式 | 识别算法+找 key |
| 校验 | checksum/序列号/时间戳 | 修改字段观察报错 |
24.3 协议逆向全流程(架构)
Phase 1: 采集(抓包)
├─ 工具: Wireshark / tcpdump / mitmproxy / Burp / 应用内 Hook
├─ 目标: 拿到多组不同类型的交互样本
└─ 原则: 样本多样性 > 样本数量(不同状态、不同参数)
↓
Phase 2: 结构还原
├─ 对齐帧边界(长度字段识别)
├─ 字段语义猜测(ID/长度/类型/载荷/校验)
├─ 序列化格式识别(JSON/XML/Protobuf/MSGPack/自定)
└─ 产出: 帧格式图(偏移表)
↓
Phase 3: 行为还原
├─ 状态机推导(多轮交互时序)
├─ 与客户端/服务端代码交叉验证(逆向 App/服务端)
└─ 产出: 会话流程图
↓
Phase 4: 重放验证
├─ 用还原的格式构造请求
├─ 修改字段观察响应(合法性校验在哪)
└─ 产出: 可复现的客户端/脚本
24.4 核心技能 1:抓包与流量获取
场景: 普通 App/Web
工具: Burp/mitmproxy(HTTPS 需装证书)+ Wireshark 落盘
场景: 移动 App(无法抓包/SSL Pinning)
工具: Frida Hook 发送函数 / 透明代理 + 绕过 Pinning(第 6 章)
场景: 加密流量(TLS)
思路: Hook SSL_write/SSL_read(数据解密前/后)或用 TLS 密钥日志
场景: 本地进程间通信(Unix Socket/共享内存)
工具: strace + /proc/PID/fd 观察 / 修改程序日志
场景: 设备硬件(串口/CAN/I2C)
工具: 逻辑分析仪 / CAN 分析工具(第 15 章)
TLS 密钥日志法(重点)
1. 设置环境变量: export SSLKEYLOGFILE=/tmp/keys.log
2. 抓包: tcpdump -w cap.pcap
3. Wireshark: 设置 → 协议 → TLS → (Pre)-Master-Secret log filename
4. 流量自动解密 → 看到明文应用层协议
适用: 自己可控运行环境的程序;App 用 Frida 同样能拿到 key
24.5 核心技能 2:序列化格式识别
| 格式特征 | 识别方法 | 逆向工具 |
|---|---|---|
| JSON | { "key": 明文 |
直接读 |
| XML | <tag> |
直接读 |
| Protobuf | 变长 varint + \n 等字段 tag |
protoc / blackboxprotobuf |
| MessagePack | 0x82 等 map/array 标记 |
msgpack 工具 |
| TLV | 每段 = 类型+长度+值 | 人工/脚本 |
| 自定义二进制 | 结构规整、长度字段 | 对比样本人工还原 |
| 加密载荷 | 高熵、无结构 | 先解密(第 9 章)再还原 |
Protobuf 快速还原
# 黑盒还原(无 .proto 文件)
pip install blackboxprotobuf
# 用 Wireshark 的 Protobuf 解析或 blackboxprotobuf 推断字段
# 有已知字段名时(App 内字符串),可以反推出 .proto 再 protoc 解析
24.6 核心技能 3:帧结构与字段还原方法
定长 vs 变长
定长: 每个字段偏移固定 → 直接对照两个样本的差异
变长: 找长度字段(常见位置: 头部 2/4 字节)→ 校验长度值
找长度字段的实证方法
1. 抓两个不同载荷大小的请求
2. 逐字节对齐(十六进制窗口双开)
3. 变化的字段里,值=数据长度的那个字节/字,就是长度字段
校验和/签名字段识别
证据: 修改载荷任意字节 → 响应报错"校验失败"
确认: 固定载荷字段,逐字节尝试恢复校验算法(CRC/XOR 累加/自定)
24.7 状态机还原(会话级理解)
方法: 按时间序排列样本 → 标注每条的"阶段"
常见阶段: 握手 → 认证 → 业务请求 → 心跳 → 断开
产出: 状态转移图(什么输入使状态转移)
用途: 重放工具的正确调用顺序;找认证绕过(跳过某状态)
24.8 重放与验证工程
# 还原后的重放客户端架构
# 发送: 帧封装(长度+类型+载荷+校验)
# 接收: 解析响应帧
# 状态: 会话状态机(token/seq 维护)
# 测试: 字段变异器(改每个字段看响应差异 = 黑盒协议 fuzz)
验证还原正确性的标准:构造的请求能被目标接受并返回预期响应;改动关键字段能复现服务端拒绝。
24.9 协议逆向决策树
拿到流量/通信对象
├─ 明文? → 直接还原(24.5 识别格式)
├─ TLS 加密? → 密钥日志 / Hook SSL(24.4)
├─ 自定义加密? → 逆向客户端加密函数(第 9 章)→ 拿 key 后解密再还原
├─ 有客户端二进制? → 逆向客户端代码是最快路径(函数名/字符串就是协议文档)
├─ 无客户端?(纯被动)→ 纯流量分析(帧结构→字段→状态机)
└─ 目标只是重放? → 不需要完全还原,复现"能通过的请求"即可
24.10 检查清单
□ 采集足够多样本(不同参数/不同阶段/错误路径)
□ 帧边界确定(长度字段确认)
□ 每个字段语义标注(哪怕只是"未知"也要记录位置)
□ 序列化格式识别
□ 加密/签名处理方案确定
□ 状态机画出来
□ 重放验证通过
□ 输出协议文档(字段偏移表 + 时序图)
动手练习
- 用 mitmproxy 抓一个普通网站 App 的 API 请求,画出每个请求的字段表(query/header/body)。
- 用 TLS 密钥日志法解密一个 HTTPS 会话的流量,观察应用层协议。
- 抓取某个游戏的本地网络包(或找一个协议逆向 CTF 题),还原其帧格式。
- 用 blackboxprotobuf 解析一段未知 Protobuf 流量,还原字段结构。
深入阅读
- 仓库:
skills/reverse-engineering/platforms.md(网络协议逆向、CAN 总线、序列化识别) - 仓库:
skills/reverse-engineering/patterns*.md(协议相关 CTF 案例:SPN、RC4 加密流、字节-at-a-time) - 仓库:
skills/reverse-engineering/field-notes.md(WebSocket/HTTP 协议案例) - 实战案例:
skills/field-journal/seed-011_pcap-protocol-reverse.md(PCAP 协议还原) - 仓库:
CTF-Sandbox-Orchestrator/competition-pcap-protocol/(PCAP 专项) - 仓库:
CTF-Sandbox-Orchestrator/competition-websocket-runtime/(WebSocket 专项)