Proxyman 与 mitmproxy 对比:解码 Protobuf WebSocket 消息
你已经抓取到了 WebSocket 连接,但它的消息看起来像一堆随机的二进制数据。你知道应用收到的是有用的信息,却无法在调试工具里读出其中的值。
这些消息可能使用了 Protocol Buffers,通常简称为 Protobuf。Protobuf 速度快、体积小,但它并不是为了像 JSON 那样便于阅读而设计的。
mitmproxy 和 Proxyman 都能帮你解码 Protobuf WebSocket 消息。mitmproxy 提供了 Protobuf 内容视图,并支持读取 .proto 定义文件。Proxyman 则在 macOS 应用中提供了可视化的消息列表、快速的 Raw Mode,以及基于 descriptor 的解码功能。
在本指南中,你将了解:
- WebSocket 和 Protobuf 是什么
- 为什么 Protobuf 消息难以阅读
- 目前 mitmproxy 的解决方案如何工作
- 如何在 Proxyman 中解码消息,无论是否有 schema
- 如何解决常见的解码问题
什么是 WebSocket?
普通的 HTTP 通常遵循一个简单的模式:
- 应用发送请求。
- 服务器返回响应。
- 本次通信结束。
WebSocket 会保持一条连接处于打开状态,应用和服务器可以随时互相发送消息。
WebSocket 适用于以下场景:
- 聊天消息
- 实时价格
- 游戏事件
- 配送或行程更新
- 通知推送
- 协同编辑
- 实时仪表盘
调试 WebSocket 时,你通常需要先看到这条连接,然后按顺序逐条查看每条发送和接收的消息。
什么是 Protobuf?
Protobuf 是 Google 创建的二进制数据格式。与 JSON 等普通文本格式相比,它用更少的字节存储结构化数据。
一条 JSON 消息可能是这样的:
{
"userId": 123,
"name": "Taylor",
"online": true
}
同样的数据用 Protobuf 编码后就是二进制。如果以文本形式打开,你可能会看到方框、奇怪的符号,或一长串字节。
这并不代表消息损坏了。你的调试工具需要按照 Protobuf 的规则对它进行解码。
为什么 Protobuf 难以阅读?
Protobuf 消息存储的是字段编号和对应的值。便于理解的字段名通常保存在单独的 schema 中。
例如,schema 可能如下定义:
message UserStatus {
int64 user_id = 1;
string name = 2;
bool online = 3;
}
二进制消息中可能包含字段 1、2 和 3,但每次都无需重复 user_id、name 和 online 这些字段名。
这种设计让消息变得很小,但也带来了调试上的难题:如果没有正确的 schema,工具通常只能显示字段编号和可能的值类型,无法可靠地还原原始字段名。
mitmproxy 的现有解决方案
mitmproxy 支持 WebSocket 流量。它还内置了 Protobuf 内容视图,可以格式化二进制数据。
要显示字段名,mitmproxy 提供了 protobuf_definitions 选项。你需要将它设置为 .proto 文件的路径:
mitmweb --set protobuf_definitions=/path/to/messages.proto
抓取到流量后,找到对应的 WebSocket 会话,打开一条消息;如果没有自动选中,就手动选择 Protobuf 内容视图。
这是 mitmproxy 真正内置的功能。查看普通的 Protobuf 数据无需编写自定义的 Python 插件。
以下情况适合使用 mitmproxy:
- 你已经在使用 mitmproxy 抓取流量
- 你习惯使用终端命令行选项
- 你的开发项目中保存了
.proto文件 - 你希望将更多流程自动化或脚本化
- 你需要免费的开源代理工具
mitmproxy 工作流中可能遇到哪些难点?
对新手来说,主要难点在于把各个环节串联起来。
1. WebSocket 消息与其 schema 是分离的
抓取到字节只是第一步。如果你想看到真实的字段名,还需要正确的 .proto 定义。
大型项目可能有很多 .proto 文件,以及大量的 import、package 和消息类型。当旧版本的应用仍在发送旧格式时,schema 也可能已经发生了变化。
2. 配置需要使用选项和内容视图
你需要知道选项名称、提供正确的文件路径、找到对应的 WebSocket 消息,并选择正确的内容视图。
熟悉之后,这种方式效率很高。但对于刚接触网络调试的开发者来说,他们可能更喜欢直接从当前查看的消息出发、可见的菜单操作。
3. 找到某一条消息可能很费时间
繁忙的 WebSocket 可能会发送成百上千条消息,有用的数据则夹杂在心跳消息、状态更新和其他事件类型之中。
清晰的收发消息时间线,能帮你把应用中的一次操作和对应的一条二进制消息关联起来。
4. 可能缺少 schema
有时你被允许调试该应用,但手边没有它的 .proto 源文件。这种情况下,你仍然需要一种安全的方式来查看基本的字段编号和值。
原始解码无法还原原始字段名,但可以判断载荷是否很可能是 Protobuf,并帮助你定位所需的值。
Proxyman 的可视化解决方案
Proxyman macOS 6.5.0 或更高版本可以解码 WebSocket 消息中的 Protobuf 载荷。
它提供两种实用的模式:
- Raw Mode 无需 descriptor 文件即可使用,会显示字段编号和推断出的值。
- Message Type 使用导入的
.descdescriptor 文件,可以显示 schema 中定义的字段名和类型。
这样你可以先从抓取到的消息入手,再根据手头掌握的 schema 信息选择合适的模式。
第 1 步:抓取 WebSocket 连接
下载并设置 适用于 macOS 的 Proxyman。如果需要解密安全的 wss:// 连接,请安装并信任证书。
对于由 Google Chrome 打开的 WebSocket,先启动 Proxyman,然后再打开网站。Proxyman 可以自动抓取 Chrome 中正常的生产环境 WebSocket 流量。
执行会发送消息的操作,例如发送一条聊天消息,或修改一项实时设置。测试范围越小,越容易找到对应的帧。

选中 WebSocket 会话。在 WebSocket 标签页中,你可以看到客户端发送的消息和服务器返回的消息。
请只检查你有权限测试的 WebSocket 流量。消息中可能包含账户信息、会话令牌、个人数据或私密的商业信息。
第 2 步:在没有 schema 的情况下尝试 Raw Mode
如果没有 Protobuf descriptor,Raw Mode 是检查消息最快的方式。
- 选中该 WebSocket 消息。
- 右键点击该消息。
- 选择
Protobuf→Decode。 - 选择
Raw Mode。

Proxyman 会读取二进制载荷,并显示它能推断出的字段。

Raw Mode 可以帮你看到:
- 字段编号
- 数字
- 文本值
- 类布尔值
- 嵌套的二进制值
Raw Mode 无法得知某个字段原本叫 user_id 还是 message_text。要获取这些名称,你需要 schema。
第 3 步:准备 descriptor 以显示完整字段名
Proxyman 使用扩展名为 .desc 的 Protobuf File Descriptor。descriptor 中包含解码器所需的编译后 schema 信息。
最简单的方法是向后端或应用团队索取当前的 descriptor 文件。
如果你有 .proto 源文件,可以使用 protoc 生成一个 descriptor。下面的示例包含了被导入的定义:
protoc \
--descriptor_set_out=output.desc \
--include_imports \
-I=/path/to/proto/files \
/path/to/proto/files/*.proto
如果 macOS 上尚未安装 protoc,请先安装 Protobuf 命令行工具:
brew install protobuf
请将 descriptor 保存在安全的开发目录中。schema 可能会暴露内部的消息名称和数据结构,因此除非团队允许,否则不要公开发布。
第 4 步:将 descriptor 导入 Proxyman
打开:
Tools → Protobuf Schema
点击 + 按钮,然后选择你的 .desc 文件。Proxyman 会读取其中可用的包名和消息类型。
你也可以从 WebSocket 消息开始,选择 Protobuf → Decode → Message Type,如果列表中没有所需的类型,可以使用导入按钮。

第 5 步:选择正确的消息类型
选择该载荷所使用的根消息类型。如果 schema 中定义了包名,请一并选择其包名。
例如:
chat.events.NewMessage
选择正确的类型后,Proxyman 会使用字段名解码载荷。

现在,你可以把这些二进制值与应用的行为对应起来了。你可能会看到原始字节中隐藏的 ID、时间戳、枚举值、嵌套消息和文本字段。
Proxyman 与 mitmproxy 解码 Protobuf WebSocket 消息的对比
| 任务 | mitmproxy | Proxyman |
|---|---|---|
| 抓取 WebSocket 流量 | 支持 | 支持 |
| 内置 Protobuf 视图 | 支持 | 支持 |
| 无 schema 时查看内容 | Protobuf 内容视图可以显示原始字段 | 消息菜单中的 Raw Mode |
| Schema 格式 | .proto 文件 | 编译后的 .desc descriptor |
| 配置方式 | 选项与内容视图工作流 | 可视化菜单和 schema 管理器 |
| 消息查看 | 控制台或 mitmweb 界面 | 原生的收发消息列表 |
| 最适合 | 终端、开源和自动化工作流 | macOS 上的交互式调试 |
关键不在于哪个工具支持 Protobuf、哪个不支持,两者都支持。区别在于工作流程。
对于习惯在终端或 Web 界面中工作的开发者来说,mitmproxy 是一个不错的选择。如果你希望点击一个已抓取的帧、尝试 Raw Mode,然后在不离开消息时间线的情况下添加 descriptor,那么 Proxyman 会更实用。
常见问题及解决方法
能看到连接,但消息无法读取
首先,确认已为 WebSocket 主机启用 SSL Proxying。启用后需要重新连接,因为已有的安全连接可能仍是加密状态。
然后确认载荷确实使用的是 Protobuf。二进制 WebSocket 数据也可能经过压缩、加密,或使用 MessagePack 等其他格式。
Raw Mode 只显示数字,没有字段名
这是正常现象。Protobuf 的每条二进制消息中都不包含原始字段名。请导入当前的 .desc 文件,并选择正确的 Message Type。
解码后的值看起来不对
所选的根消息类型可能不正确。请尝试该事件所使用的类型。客户端发往服务器的消息与服务器发往客户端的消息,可能使用不同的类型。
载荷在 Protobuf 字节之前还可能包含自定义封装。如果开头的字节描述的是消息长度、事件类型或压缩方式,请向应用团队确认。
部分字段缺失
你的 descriptor 可能比应用或服务器的版本更旧。请移除旧的 schema,根据当前的 .proto 文件重新生成 .desc 文件,然后再次导入。
请记住,Protobuf 不会发送值为默认值的字段。缺失的可选字段可能是正常的。
无法抓取 iOS 应用中的 WebSocket
部分 iOS WebSocket API 不遵循常规的 HTTP 代理设置。如果你的应用使用 URLSessionWebSocketTask,请参考 Proxyman SOCKS Proxy 指南,或者对于你自己开发的应用,使用仅用于调试的 Atlantis 框架。
消息列表太杂乱
清空会话,然后重复执行一次操作。记下操作的时间,再检查附近收发的消息。小范围的测试远比录制几分钟的实时流量更容易分析。
总结
WebSocket 让消息能够实时传输,Protobuf 则让消息保持小巧。二者结合,非常适合应用使用,但抓取到的二进制数据可能很难理解。
mitmproxy 可以借助内容视图和 .proto 定义解码 Protobuf。对于喜欢终端选项和自动化工作流的开发者来说,它功能强大。
Proxyman 为 macOS 开发者提供了一条可视化路径,从实时的 WebSocket 消息直达解码后的值。如果没有 schema,可以先从 Raw Mode 开始;如果需要完整的字段名,则导入 .desc descriptor 并选择正确的 Message Type。
延伸阅读

