百趣云 百趣云的博客

抓包抓到 protobuf:从二进制 wire 格式还原 proto 文件

认出 protobuf:先看特征再动手

抓包看到请求体是一堆二进制,先确认是不是 protobuf。特征很明显:

  • 开头几个字节是 08 xx 12 xx 1a xx 这种模式(field tag + wire type)
  • 能解出可读字符串,但字段之间没有分隔符
  • Content-Type 常是 application/x-protobufapplication/octet-stream

protobuf 的 wire 格式核心规则:每个字段是 tag + value,tag = (field_number << 3) | wire_type。wire type 就五种:varint(0)、64位(1)、长度分隔(2)、32位(5)、嵌套消息走 2。

手工解码:protoc --decode_raw 是第一神器

不需要 proto 文件,protoc 自带裸解码:

# 把抓包的 body 存成 bin 文件
protoc --decode_raw < request.bin

输出:

1: 1787968800
2: "keyword"
3 {
  1: "device_id_xxx"
  2: 20
}
4: 0x9e3779b9

字段号、嵌套结构、值全出来了。缺的是字段名和类型语义——1 是 timestamp 还是 user_id?3.2 是 int32 还是 enum?这需要结合业务猜,但骨架已经立起来了。

从解码结果反推 proto 文件

syntax = "proto3";

message SearchRequest {
  int64 timestamp = 1;        // 1: 1787968800 看着像时间戳
  string keyword = 2;         // 2: "keyword"
  DeviceInfo device = 3;      // 3: 嵌套消息
  fixed32 sign_seed = 4;      // 4: 固定32位,像种子值
}

message DeviceInfo {
  string device_id = 1;
  int32 page_size = 2;
}

反推的类型判断规则:

  • wire type 0(varint):可能是 int32/int64/bool/enum,看值的范围猜
  • wire type 2(长度分隔):能按 UTF-8 解出可读文本的是 string,解不出的是 bytes 或嵌套 message(递归 decode_raw 验证)
  • wire type 5/1(32/64 位定长):fixed32/fixed64/float/double,看值像不像浮点

验证:encode 回去对拍

proto 文件写好后必须验证——用它能重新编码出和抓包字节级一致的数据:

protoc --encode=SearchRequest request.proto < decoded.txt > reencoded.bin
diff <(xxd request.bin) <(xxd reencoded.bin)

字节一致 = 字段号和类型全对。不一致就看 diff 位置:tag 不对是字段号错,长度不对是类型错(比如把 int64 写成了 int32 导致 varint 截断)。

精髓:四个实战坑

  1. 字段号比字段名重要。protobuf 序列化只带字段号不带名,你 proto 里的字段名随便起(field_1 都行),号对了就能编解码。别纠结"猜不出字段含义",先保证结构对。
  2. repeated 字段的两种编码。packed(所有值连在一个 length-delimited 块里)和非 packed(每个值一个 tag)编码不同,decode_raw 看不出来——同一个字段号出现多次就是非 packed repeated;一个字段号后面跟一串紧密排列的 varint 就是 packed。
  3. map 是语法糖map<string, int32> 实际是 repeated MapEntry,每个 entry 是 {1: key, 2: value} 的嵌套消息。decode_raw 看到大量同构的两字段嵌套,就是 map。
  4. oneof 和 unknown fields。新版 protoc 的 decode_raw 对 unknown fields 也能解。遇到同一个字段号出现不同类型,可能是 oneof——proto 里用 oneof 声明。

进阶:从 App 里直接抠 proto 定义

手工猜 proto 是兜底方案。如果 App 没混淆 protobuf 代码,更快捷径:

  • Java 层:jadx 搜 extends GeneratedMessageLitecom.google.protobuf,类名和字段直接给出完整 message 定义
  • descriptors:很多 App 编译时把 FileDescriptor 嵌在代码里,protobuf-inspector 工具可以直接从 class 提取 .proto

总结

  1. protoc --decode_raw 无需 proto 文件裸解码,先拿结构
  2. 反推 proto 后必须 encode 对拍,字节一致才算对
  3. 字段号对就能用,字段名可以后补
  4. 优先从 App 的 protobuf 生成类里抠定义,手工猜测是兜底

交流微信:run1255

By 百趣云 阅读量:2 On