调试自己的应用时抓到一个请求或响应,body 是二进制的:application/x-protobuf、application/grpc 或 application/grpc-web+proto。DevTools 里只能看到一串乱码,描述它的 .proto 又在另一个仓库里,或者在你手头没有的那个构建版本里。装了 protoc 的话,把字节存成文件跑一下 protoc --decode_raw 就能看结构。这个页面把同样的事搬进浏览器:贴上字节,直接看字段。
能做什么
输入。 把 body 以十六进制(空格、冒号、逗号以及 0x、\x 前缀都会被忽略)或 base64(标准或 URL 安全字母表,有没有补位的 = 都行)粘贴进来,也可以直接打开原始字节文件,最大 16 MiB。输入格式自动识别:只含十六进制字符的文本按十六进制读,其余按 base64 读。如果你的 base64 恰好全由 0-9、a-f 组成,手动选 Base64 即可。
gRPC 分帧。 gRPC 和 gRPC-Web 的 body 并不是裸的 protobuf:每条消息前面有 1 字节标志位和 4 字节大端长度,流式响应里会连着好几帧。字节能被完整切成这样的帧时,每一帧单独展示:
- 标志
0x00:普通消息,直接解码; - 标志
0x01:压缩过的消息。开头像 gzip 或 zlib 的(grpc-encoding: gzip或deflate)用浏览器自带的DecompressionStream解压后再解码;snappy、zstd 等其他编码只做提示,不去猜; - 标志
0x80:gRPC-Web 的 trailers 帧,按文本显示(grpc-status、grpc-message)。
gRPC-Web-text(application/grpc-web-text)的 body 就是这些帧的 base64,有时是好几段各自带补位的 base64 首尾相接,原样粘贴即可。如果想把字节当成单条消息解,取消勾选 检测 gRPC / gRPC-Web 分帧。整个 body 是一个 gzip 流时,会先解压。
文本视图。 默认视图和 protoc --decode_raw 的输出逐字节相同:每个字段一行 字段号: 值,varint 按无符号十进制,fixed32 和 fixed64 按补零的十六进制,嵌套消息用花括号,其余按 C 风格转义的字符串输出。可以复制或下载成 .txt,两次抓包的结果拿任何文本 diff 工具就能比。
树形视图。 wire format 只记录值是怎么编码的,不记录它是什么意思,所以树形视图把讲得通的解读都列出来:
- varint:无符号、有符号
int64(最高位为 1 时)、zigzag 的sint,值为 0 或 1 时还有bool; - fixed32 和 fixed64:无符号、有符号,以及
float或double; - length-delimited:嵌套消息、UTF-8 字符串、packed varint、packed fixed32 或 fixed64,以及十六进制字节。每个字段都有按钮切换解读,默认显示哪一种和 protoc 的优先顺序一致(先当消息);
- 已废弃的 group(wire type 3 和 4)和 protoc 一样按嵌套字段显示。
每个字段还标出字节偏移,方便在十六进制编辑器里对照。
字段类型是怎么判断的
protoc 对每个非空的 length-delimited 值都先试着当嵌套消息解析:能完整解析成字段就按消息输出,否则按字符串输出。所以两个字节的字符串 "hi" 会被打印成 1 { 13: 105 }:0x68 恰好是合法的 tag(字段 13,varint),0x69 是合法的值。文本视图保留 protoc 的这个选择,树形视图里可以把这个字段切到 字符串。
解码器照着 protoc 的两套解析逻辑实现。顶层按 C++ ParseContext 的规则读(tag 和长度最多 5 字节,长度小于 2 GiB,group 最多嵌套 100 层);嵌套的值按 TextFormat::Printer 的方式重新解析,预算从 10 开始、每深一层减 1,所以嵌套超过 10 层的消息在 protoc 和这里都会显示成字符串。
我们在 macOS 上用 libprotoc 36.2 做了对照。为本页写了 10 个测试消息(全部标量类型、packed 与非 packed 的 repeated 字段、sint32 和 sint64、含中文和 emoji 的字符串、map、oneof、空消息、最大到 536,870,911 的字段号、proto2 group、一棵 12 层的树),用 protoc --encode 编码后,文本输出与 protoc --decode_raw 逐字节一致。另外 1,000 个随机生成并随机篡改过的输入(包括 protoc 两套解析逻辑表现不同的超长 varint),凡是 protoc 能解的,输出文本都相同;protoc 拒绝的,这里也都报错。
字节不是合法 protobuf 时
protoc 会直接报 Failed to parse input.,什么都不输出。这个页面会把出错位置之前的字段解出来显示,并给出出错的字节偏移和原因:值被截断、本该是 tag 的位置出现 0x00(常见于填充字节或末尾的校验和)、字段号为 0、wire type 6 或 7、没有起始的 end-group tag、超过 10 字节的 varint。如果一个字段都解不出来,先确认粘贴的是 body 而不是整段 HTTP 报文,再试试打开或关闭分帧检测。
数据如何保持私密
- 解码在你设备上的 Web Worker 里完成,粘贴或打开的内容都不会上传。
- 字段值一律按纯文本显示,payload 里的任何内容都不会被当成 HTML 解析或执行。
- 统计事件只有粗粒度信息:成功、
malformed或错误码,字节的来源(hex、base64或file),是否识别出 gRPC 分帧,大小区间和耗时区间。绝不包含字节内容、字段号或字段值。完整列表见隐私政策。 - 工具页不展示广告。
如何自己验证
- 先打开浏览器开发者工具的 Network 面板,再解码。能看到的请求只有页面资源和 worker 脚本
/_astro/protobuf-worker-*.js,没有一个带着你的输入。 - 或者先加载页面,再断网(关掉 Wi-Fi,或在 Network 面板勾选 Offline),然后解码,照样能用。
- worker 运行在
default-src 'none'; script-src 'self'; connect-src 'none'的内容安全策略下,浏览器不允许它发出任何网络请求。
局限
- 没有字段名和类型。 没有 schema,字段 2 就只是字段 2,
string和bytes也分不出来。暂不支持粘贴.proto按名字解码:常用的 protobuf.js 用new Function动态生成解码代码,而本页的内容安全策略禁止这样做。 - 有歧义的值依然有歧义。 短字符串可能被解析成消息,一串小整数的 packed 列表也可能被当成字符串。树形视图把可能的解读都列出来,哪个对得上你的接口,由你来判断。
- 压缩只支持浏览器能解的 gzip 和 deflate 帧。snappy 和 zstd 帧会标出来,但不解码。
- 大小。 最多 16 MiB 字节。树形视图只显示前 200,000 个字段,文本视图和下载的文件始终完整。
字节从哪里来
要先抓到自己 Web 应用的流量,可以在 DevTools 里录制并导出 HAR;HAR / SAZ 接口归纳会按接口归纳请求并列出内容类型,protobuf 接口一眼就能找到,HAR 文件分析指南讲了怎么读抓包文件、怎么安全地分享。从抓到的请求一路追到发请求的前端代码,可以看追踪网站 API 调用。只抓取和解码你自己拥有或已获授权测试的应用与服务的流量。
相关教程
- 没有 .proto 怎么解 protobuf:wire type、varint 与 gRPC 帧:用 protoc 编码一条消息,再假装丢了 schema,逐行对照
protoc --decode_raw读回来。