⭐ 觉得好用?收藏备用,下次直接打开
Payload
📜 可选:粘贴 .proto 获得字段名 不贴也能解码,会显示 tag 编号
解码结果

Protobuf 解码 把二进制 protobuf 消息拆成可读的 JSON。底层基于 protobufjs 解析 .proto,并配合手写的 wire format reader 做 schemaless 解码——所有处理都在浏览器本地完成,payload 和 schema 不会上传 任何服务器。

两段式工作流

阶段你提供输出
无 schema 解码仅 payload(hex / base64 / 文件)field_<n>: value,每个 tag 给出 wire type + 多种类型解释
有 schema 解码payload + .proto + 选择 message 类型带字段名的 JSON;.proto 没覆盖的 tag 仍以 _unknown_<n> 保留

切到 编码 模式可以反向操作:JSON + .proto → hex / base64,方便构造测试用例。

Wire format 速查

每个字段以 varint tag 开头:(field_number << 3) | wire_type

wire type取值用于
0 varintint32/int64/uint32/uint64/sint32/sint64/bool/enum1–10 字节,最高位为 continuation bit
1 i64fixed64/sfixed64/double固定 8 字节
2 lenstring/bytes/embedded message/packed repeatedvarint 长度 + 原始字节
5 i32fixed32/sfixed32/float固定 4 字节
3/4sgroup / egroup(已废弃)proto2 早期遗留

常见坑

  • int32 vs sint32:编码不同,按错的解会得到完全不一样的数字(特别是负数)。一定要按 .proto 来
  • packed repeated:proto3 默认 packed,wire 上是 len 类型而不是多次重复同一 tag。无 schema 模式下会被识别为 bytes 或 message
  • gRPC 5 字节前缀:HTTP/2 抓包看到的 body 前 5 字节是 grpc framing,不是 protobuf 本身,要先剥掉
  • oneof:wire 上和普通字段没区别,只是 schema 上互斥;本工具有 schema 模式会按 oneof 选中的字段输出
  • 未知字段:proto3 之前默认丢弃;本工具会主动保留并标 _unknown_<n>,避免漏掉对方加的新字段

📍使用场景

  • 抓包看到二进制 bodygRPC / Protobuf-over-HTTP 抓到的 request/response body 是一坨字节,先无 schema 解出 tag 结构看个大概,再贴 .proto 升级到字段名。
  • 没拿到 .proto 也能调试第三方接口或对方还没给 schema,照样能解出每个 tag 的 wire type、数值、字符串候选——很多排错只需要看到结构就够了。
  • 验证 SDK 编出的 payload怀疑客户端 SDK 编错字段(比如把 sint32 写成 int32),把 wire 上抓到的字节贴进来对照 .proto 看有没有出入。
  • 反向构造测试用例切到 "编码" 模式,输入 .proto + JSON 直接生成 hex/base64 二进制,喂给被测服务做边界测试,不用写脚手架。

常见问题

为什么不需要 .proto 也能解?

Protobuf 的 wire format 自带 tag number 和 wire type——每个字段的字节流前都会编码 (field_number << 3) | wire_type,解码器据此就能拆出 "字段编号 + 大类(varint/i32/i64/length-delimited)"。看不到的只是 字段名精确类型(int32 / sint32 / enum / bool 都编为 varint,分不清)。这就是 protoc --decode_raw 的原理,本工具也实现了同样的能力。

解出来的 string / message / bytes 是怎么判断的?

Length-delimited(wire type 2)字段在 wire format 上完全没有线索说明它是 string、bytes 还是嵌套 message。本工具按 "先递归当成 message 解,能解通就显示为 message;否则尝试 UTF-8 解码,可读则当字符串;都不行再当 bytes 给出 hex/base64" 的优先级智能猜。结果会同时给出 _wire 标记,方便你判断猜得对不对。

int32sint32 解出来不一样?

是的。两者编码方式不同int32 直接用 varint 存(负数会占满 10 字节),sint32 用 ZigZag 编码(小绝对值的负数也很短)。无 schema 模式下工具把 varint 同时给出 uintint(二补码解读)、sint(ZigZag 解读)三种值,让你按需选。有 schema 模式下 protobufjs 会按 .proto 里声明的类型自动选对。

.proto 里有 import 怎么办?

暂不支持 import——浏览器拿不到外部文件。把所有依赖的 message 复制粘贴到同一份 .proto 即可。google.protobuf.Timestampgoogle.protobuf.Durationgoogle.protobuf.Empty 等 well-known types 已被 protobufjs 内置,可直接引用。

schema 里少声明的字段会丢吗?

不会。本工具会先按 .proto 解一遍,然后再无 schema 解一遍找出 schema 没覆盖的 tag,以 _unknown_<n> 为键合并到结果里。这点比直接 protoc --decode 强——后者会把未知字段静默丢弃。

为什么我的 long/int64 字段输出成字符串了?

JavaScript 的 Number 只能精确表达 ±2^53 范围内的整数。protobufjs 的 int64/uint64/sint64/fixed64 默认返回字符串(通过 toObject({ longs: String })),保证 18 位以内的 ID 不丢精度。你直接 JSON.parse 后还能拿到字符串原值,没问题。

gRPC 抓包里前面的 5 个字节怎么处理?

gRPC over HTTP/2 的 frame 前面有 5 字节前缀:1 字节 compression flag + 4 字节 big-endian length。把这 5 字节剥掉,剩下的才是真正的 protobuf payload,再贴进本工具解码。如果 compression flag = 1,还要先按 gRPC 头里声明的 grpc-encoding(gzip 等)解压。

能解 protobuf-text-format 吗?

不能——本工具只处理 wire format(二进制)。Text format(人类可读的 field: value)和 wire format 是两种不同的序列化格式,互转要用 protoc --encode / --decode