Post

实战:使用 eCapture 捕获 gRPC 明文并在 Wireshark 中解密

实战:使用 eCapture 捕获 gRPC 明文并在 Wireshark 中解密

在微服务架构中,gRPC(基于 HTTP/2 + Protobuf)通常运行在 TLS 加密之上,且其负载(Payload)为二进制格式。这给抓包调试带来了巨大的挑战:传统的 tcpdump 只能看到加密后的乱码数据。

本文介绍一种非侵入式的“魔法”方案:使用 eBPF 工具 eCapture,直接在内核层面 Hook TLS 函数,导出带有解密密钥的 pcapng 文件,或者直接导出明文,从而在 Wireshark 中完美还原 gRPC 调用细节。

核心原理

传统的 TLS 解密方法(如设置 SSLKEYLOGFILE 环境变量)通常需要重启服务,并且对静态编译的 Go 程序(常见于 gRPC 服务)支持较差。

eCapture 基于 eBPF 技术,其工作原理如下:

  1. uprobe Hook: 动态追踪用户态的 OpenSSL SSL_write/SSL_read 或 Go crypto/tls 相关函数。
  2. 获取明文/密钥: 直接在加密前(发送)或解密后(接收)截获明文数据,或者在 TLS 握手阶段截获 Master Secret
  3. Pcapng 导出: 将捕获的数据或密钥封装成 Wireshark 可识别的 pcapng 格式(支持 Decryption Secrets Block, DSB)。

前置条件

  • Linux Kernel: 需要支持 eBPF(建议内核 >= 4.18,理想情况下 >= 5.5 以支持 BPF CO-RE)。
  • 权限: Root 权限。
  • 工具:
    • eCapture
    • Wireshark (建议 v3.0+ 以支持 HTTP/2 和 Protobuf)

实战步骤

场景 1:分析 Go 语言编写的 gRPC 服务

Go 编写的程序通常是静态链接的,不依赖系统的 libssl.so。因此,必须使用专门的 gotls 模块。

1. 找到目标进程和二进制文件

假设你的 gRPC 服务进程 ID 为 12345,二进制路径为 /app/grpc-server

1
2
ps -ef | grep grpc-server
# 输出: root 12345 ... /app/grpc-server

2. 使用 eCapture 捕获并导出 pcapng

使用 gotls 子命令,并指定 --elf 参数为目标二进制文件。

1
2
3
4
# -e: 目标二进制文件路径
# --pcapfile: 输出的 pcapng 文件名
# --pid: (可选) 仅捕获特定进程;如果省略,则捕获所有使用该二进制的进程
sudo ecapture gotls -e /app/grpc-server -m pcap --pcapfile=grpc_traffic.pcapng --pid=12345 -i eth0 host xxx and port xxx

运行后,eCapture 会在控制台打印一些捕获到的明文摘要。保持其运行并触发一些 gRPC 请求。完成后按 Ctrl+C 停止。

场景 2:分析使用 OpenSSL 的服务(Python/C++/Nginx 等)

对于动态链接 OpenSSL 库的程序(如 Python gRPC 客户端或 C++ gRPC 服务),eCapture 通过 tls 子命令 Hook libssl.so 来工作。

1
2
3
# 自动 Hook 系统默认的 libssl
# 如果库位置非标准,使用 --libssl /path/to/libssl.so
sudo ecapture tls -m pcap --pcapfile=openssl_traffic.pcapng -i eth0

如何验证 pcapng 是否包含解密密钥 (DSB)?

在打开 Wireshark 之前,你可以通过以下方法确认 eCapture 是否成功将 TLS 密钥嵌入到了 pcapng 文件中。

1. 使用 capinfos 命令行工具

capinfos 是 Wireshark 自带的一个命令行工具,用于快速查看文件元数据。

1
capinfos grpc_traffic.pcapng

如果包含 DSB,你会看到类似以下的行:

  • Number of Decryption Secrets: 1 (or more)
  • Type of Decryption Secrets: TLS Key Log

2. 在 Wireshark 界面中查看

打开文件后:

  1. 前往 统计 (Statistics) -> 捕获文件属性 (Capture File Properties)
  2. 滚动到窗口最底部。
  3. 如果存在密钥,你会看到一个名为 Decryption Secrets 的面板,列出了嵌入的信息,如 CLIENT_RANDOM

Wireshark 分析流程

1. 打开 Pcapng 文件

直接使用 Wireshark 打开生成的 grpc_traffic.pcapng

关键点:eCapture 生成的 pcapng 文件使用了 DSB (Decryption Secrets Block) 标准,将捕获的 TLS Master Secret 直接嵌入到 pcapng 文件头中。当 Wireshark 读取该文件时,它会自动加载密钥并解密流量,无需手动配置 Key Log File。

2. 验证解密状态

  • 检查数据包列表;你应该能看到绿色的 HTTP2 协议数据包,而不是纯粹的 TCP/TLS 数据包。
  • 如果仍然显示为 TLS Application Data,请检查你的 Wireshark 版本是否过旧。

3. 解码 gRPC (Protobuf)

即使流量已解密,HTTP/2 DATA 帧中的负载仍然是二进制 Protobuf 数据。默认情况下,Wireshark 可能仅将其显示为十六进制转储 (Hex dump)。

要查看字段名称和值(例如 {"name": "Alice", "age": 18}),有两种方法:

方法 A:手动分析(无 Proto 文件)

  • 在 Wireshark 中选中一个 HTTP2 DATA 帧。
  • 右键点击 -> 协议首选项 (Protocol Preferences) -> ProtoBuf -> 确保启用了 “Dissect Protobuf fields”(解析 Protobuf 字段)。
  • 你只能看到 Field 1, Field 2 这样的标签;你需要根据代码猜测它们的含义。

方法 B:加载 Proto 文件(推荐)

  1. 准备好你的 .proto 定义文件。
  2. 在 Wireshark 中:编辑 (Edit) -> 首选项 (Preferences) -> Protocols -> Protobuf
  3. 将包含 .proto 文件的目录添加到 “Protobuf Search Paths”(Protobuf 搜索路径)。
  4. 确保勾选了 “Load .proto files”(加载 .proto 文件)。
  5. 应用设置后,Wireshark 将尝试根据 gRPC 路径(例如 /helloworld.Greeter/SayHello)匹配 Proto 定义,并显示可读的 JSON 树状结构。

常见问题排查

  1. 无法捕获 Go 程序?
    • 确保 Go 程序编译时没有完全去除符号表 (stripped)。eCapture 依赖符号表来定位 crypto/tls 函数地址。
    • 检查 Go 版本;版本太旧 (< 1.17) 或太新(eCapture 尚未适配)可能会有偏移量差异。
  2. Wireshark 显示 “Encrypted Handshake Message”?
    • 这是正常的。TLS 1.3 的握手部分也是加密的。只要后续的 “Application Data” 被解析为 HTTP2,就说明解密成功了。
  3. 无法捕获 localhost 流量?
    • eCapture 通过 uProbe 捕获用户态函数调用数据,因此它独立于网络接口。无论流量是走 lo 还是 eth0,只要发生了 TLS 加密/解密函数调用,就能被捕获。这是相对于 tcpdump 的一大优势。
This post is licensed under CC BY 4.0 by the author.