【JavaScript 逆向】Protobuf 协议实战:从抓包到黑盒解析的完整链路

张开发
2026/4/20 19:08:33 15 分钟阅读

分享文章

【JavaScript 逆向】Protobuf 协议实战:从抓包到黑盒解析的完整链路
1. Protobuf协议基础与逆向工程概述第一次接触Protobuf协议是在分析某个学术平台的数据接口时发现返回的内容全是乱码。当时我还以为是加密数据后来通过抓包工具看到请求头里赫然写着content-type: application/grpc-webproto这才意识到遇到了传说中的Protobuf协议。对于习惯了JSON格式的前端开发者来说Protobuf就像个黑盒子——你知道里面有数据但就是看不清具体内容。ProtobufProtocol Buffers是Google开发的一种二进制序列化格式相比JSON有三大优势体积更小通常能减少30%-50%、传输更快、schema强类型约束。但正是这些优点给逆向工程带来了挑战没有.proto定义文件时我们面对的就是一堆天书般的二进制数据。在JavaScript逆向场景中常见于以下三种情况移动端APP与服务器的通信Web端使用gRPC协议的数据交互对传输体积敏感的大数据量接口2. 抓包识别Protobuf特征用Chrome开发者工具分析某文献检索平台时我发现了典型的Protobuf流量特征。打开Network面板后重点关注以下三个关键点请求头特征content-type: application/grpc-webproto accept: application/grpc-webproto x-grpc-web: 1响应体特征前5个字节通常是00 00 00 00 1A这样的长度前缀后续内容显示为乱码或十六进制数据在Hex视图下能看到有规律的字段分隔符实战技巧使用Wireshark时设置过滤条件http.content_type contains proto在Charles中启用Hex视图查看原始字节对可疑请求右键选择Save as HAR保留原始数据我曾遇到过一个坑某平台在Protobuf数据前后添加了自定义字节导致直接解析失败。后来发现需要先去掉头部的5个长度字节和尾部的20个校验字节才能正确解析。这种定制化处理在实际项目中很常见需要特别注意。3. 黑盒解析工具链搭建没有.proto文件时我们需要借助黑盒分析工具。经过多次实践我总结出以下工具组合方案必备工具清单BlackboxProtobuf核心反序列化工具protobuf-inspector可视化分析xxd十六进制查看Wireshark流量捕获环境配置步骤# 安装BlackboxProtobuf pip install blackboxprotobuf # 安装protobuf-inspector git clone https://github.com/mildsunrise/protobuf-inspector.git cd protobuf-inspector make # 使用示例 cat binary.proto | ./protobuf-inspector工具对比表工具名称优点缺点适用场景BlackboxProtobuf支持动态类型推断复杂嵌套结构处理较弱快速原型分析protobuf-inspector可视化展示字段层级需要编译安装深度结构解析protoc官方工具可靠性高必须要有.proto文件有定义文件的情况最近在分析某电商平台时发现他们使用了Protobuf的packed重复字段。这种数据在BlackboxProtobuf中会解析异常需要先用protobuf-inspector确定字段类型后再手动指定类型参数。4. 实战从抓包到数据提取以文献检索平台为例演示完整分析流程步骤1捕获原始请求// 使用浏览器插件捕获的示例请求 fetch(https://api.example.com/search, { method: POST, headers: { Content-Type: application/grpc-webproto }, body: new Uint8Array([0,0,0,0,23,10,5,8,1,16,2...]) // 二进制数据 })步骤2提取有效载荷# 用Python处理捕获的数据 import blackboxprotobuf with open(captured.bin, rb) as f: data f.read() # 去除gRPC包装头 payload data[5:] # 前5字节是长度前缀 # 首次解析尝试 try: decoded, typedef blackboxprotobuf.protobuf_to_json(payload) except Exception as e: print(f解析失败: {e}) # 可能需要尝试不同的偏移量步骤3类型推断与修正当自动推断失败时需要人工介入。通过以下特征判断字段类型变长整数Varint以0x08, 0x10, 0x18等开头长度限定字符串以0x12开头后跟长度固定32位以0x2d开头固定64位以0x31开头实战案例 某次解析遇到字段值为1a 03 08 96 01分析过程0x1a表示字段编号3类型为字符串/嵌套消息0x03表示后续3个字节是有效载荷内部0x08表示字段编号1类型Varint0x96 01是150的Varint编码最终手动定义的typedef应为{ 3: { type: message, message_typedef: { 1: {type: int} } } }5. JavaScript环境下的特殊处理浏览器环境中处理Protobuf需要特别注意以下几点跨域问题解决方案配置本地代理服务器使用浏览器插件绕过CORS通过Electron构建调试环境性能优化技巧// 使用ArrayBuffer代替Blob提高处理速度 const decoder new TextDecoder(); const buffer await response.arrayBuffer(); const uint8View new Uint8Array(buffer); // Web Worker中处理大型Protobuf数据 const worker new Worker(protobuf-decoder.js); worker.postMessage(uint8View, [uint8View.buffer]);常见问题排查字节序问题Protobuf默认使用小端序字段编号冲突确保没有重复使用字段号默认值处理Proto3会省略默认值字段最近帮朋友分析一个Vue项目时发现他们用WebSocket传输Protobuf数据。这种情况下需要先建立Socket连接再监听message事件处理二进制数据const socket new WebSocket(wss://example.com); socket.binaryType arraybuffer; socket.onmessage (event) { const data new Uint8Array(event.data); // 使用protobufjs等库解析数据 };6. 安全防护与反逆向策略随着Protobuf的普及越来越多的平台开始加入防护措施。我遇到过以下几种防护方案常见防护手段动态字段编号每次请求随机映射字段ID嵌套混淆在消息中嵌入无用字段自定义编码在标准Protobuf基础上添加额外编码层破解思路对同一接口多次抓包对比字段变化规律使用差分分析修改单个参数观察响应变化结合字符串搜索定位关键字段某次逆向某社交APP时他们采用了字段名混淆动态编号的双重防护。我的解决方案是固定操作流程录制多组数据包用Python脚本自动比对字段出现频率对高频字段重点分析其语义# 字段频率分析脚本示例 from collections import defaultdict field_stats defaultdict(int) for packet in captured_packets: _, typedef blackboxprotobuf.protobuf_to_json(packet) for field in typedef.keys(): field_stats[field] 1 print(sorted(field_stats.items(), keylambda x: x[1], reverseTrue))7. 自动化分析与持续集成对于需要长期监控的接口可以建立自动化分析流水线工具链配置# GitHub Actions 配置示例 name: Protobuf Monitor on: schedule: - cron: 0 12 * * * # 每天UTC时间12点运行 jobs: analyze: runs-on: ubuntu-latest steps: - uses: actions/checkoutv2 - name: Set up Python uses: actions/setup-pythonv2 with: python-version: 3.9 - run: pip install blackboxprotobuf requests - run: python monitor.py监控脚本核心逻辑# monitor.py import difflib from datetime import datetime def compare_protobuf(old, new): 比较两个Protobuf消息的结构变化 old_types get_field_types(old) new_types get_field_types(new) return difflib.unified_diff( old_types.splitlines(), new_types.splitlines(), fromfileold, tofilenew ) def alert_on_changes(diff): 检测到结构变化时发送通知 if any(line.startswith() or line.startswith(-) for line in diff): send_alert_email(fProtobuf结构变更检测 {datetime.now()})在实际项目中这种自动化监控帮我们及时发现过三次接口变更避免了数据采集中断。对于重要业务接口建议设置每日自动检测任务。

更多文章