二进制文件中protobuf结构分析
字数 1750 2025-08-30 06:50:35
Protocol Buffers (Protobuf) 结构分析与逆向工程指南
1. Protobuf 简介
Protocol Buffers (Protobuf) 是 Google 开发的一种数据序列化协议,具有以下特点:
- 类似于 XML 但更高效
- 用于结构化数据序列化
- 适用于数据存储和通信协议
- 跨平台和异构系统 RPC 调用
- 序列化/反序列化效率高
- 体积比 XML 和 JSON 小得多,适合网络传输
2. Protobuf 开发环境搭建
2.1 安装 Protobuf 编译器
# 下载 Protobuf
git clone https://github.com/protocolbuffers/protobuf
# 编译安装
cd protobuf
./configure && make
sudo make install
2.2 安装 C 语言支持库
# 下载 protobuf-c
git clone https://github.com/protobuf-c/protobuf-c
# 编译安装
cd protobuf-c
./configure && make
sudo make install
3. Protobuf 基本使用示例
3.1 定义 .proto 文件
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
3.2 生成 C 语言代码
protoc --c_out=. message.proto
这将生成两个文件:
message.pb-c.c- 实现文件message.pb-c.h- 头文件
3.3 关键数据结构分析
3.3.1 ProtobufCMessage 基础结构
struct ProtobufCMessage {
const ProtobufCMessageDescriptor *descriptor; // 消息描述符
unsigned n_unknown_fields; // 未知字段数量
ProtobufCMessageUnknownField *unknown_fields; // 未知字段
};
3.3.2 字段描述符 ProtobufCFieldDescriptor
struct ProtobufCFieldDescriptor {
const char *name; // 字段名称
uint32_t id; // 字段标签号
ProtobufCLabel label; // 字段标签类型
ProtobufCType type; // 字段数据类型
unsigned quantifier_offset; // 量词偏移
unsigned offset; // 字段偏移
// ... 其他成员
};
3.3.3 消息描述符 ProtobufCMessageDescriptor
const ProtobufCMessageDescriptor example__person__descriptor = {
PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
"example.Person", // 完整消息名称
"Person", // 简短消息名称
"Example__Person", // C结构体名称
"example", // 包名
sizeof(Example__Person), // 消息大小
3, // 字段数量
example__person__field_descriptors, // 字段描述符数组
// ... 其他成员
};
3.4 字段类型和标签枚举
3.4.1 ProtobufCType 字段类型
typedef enum {
PROTOBUF_C_TYPE_INT32, // int32
PROTOBUF_C_TYPE_SINT32, // signed int32
PROTOBUF_C_TYPE_SFIXED32, // signed int32 (4 bytes)
PROTOBUF_C_TYPE_INT64, // int64
PROTOBUF_C_TYPE_SINT64, // signed int64
PROTOBUF_C_TYPE_SFIXED64, // signed int64 (8 bytes)
PROTOBUF_C_TYPE_UINT32, // unsigned int32
PROTOBUF_C_TYPE_FIXED32, // unsigned int32 (4 bytes)
PROTOBUF_C_TYPE_UINT64, // unsigned int64
PROTOBUF_C_TYPE_FIXED64, // unsigned int64 (8 bytes)
PROTOBUF_C_TYPE_FLOAT, // float
PROTOBUF_C_TYPE_DOUBLE, // double
PROTOBUF_C_TYPE_BOOL, // boolean
PROTOBUF_C_TYPE_ENUM, // enumerated type
PROTOBUF_C_TYPE_STRING, // UTF-8 or ASCII string
PROTOBUF_C_TYPE_BYTES, // arbitrary byte sequence
PROTOBUF_C_TYPE_MESSAGE, // nested message
} ProtobufCType;
3.4.2 ProtobufCLabel 字段标签
typedef enum {
PROTOBUF_C_LABEL_REQUIRED, // 必须字段
PROTOBUF_C_LABEL_OPTIONAL, // 可选字段
PROTOBUF_C_LABEL_REPEATED, // 可重复字段
PROTOBUF_C_LABEL_NONE, // 无标签(proto3)
} ProtobufCLabel;
3.5 二进制数据表示
struct ProtobufCBinaryData {
size_t len; // 数据长度
uint8_t *data; // 数据指针
};
4. Protobuf 逆向工程方法
4.1 逆向分析流程
- 定位 ProtobufCMessageDescriptor 结构体
- 分析字段描述符数组 ProtobufCFieldDescriptor
- 提取每个字段的:
- name: 字段名称
- id: 字段标签号
- label: 字段标签类型
- type: 字段数据类型
- 根据偏移量确定字段在结构体中的位置
4.2 IDA 逆向分析技巧
-
识别 ProtobufCMessageDescriptor 结构体特征:
- 包含 magic number
- 包含消息名称字符串
- 包含字段数量和字段描述符数组指针
-
字段描述符分析:
- name 字段: 指向字段名称字符串
- id 字段: 字段标签号
- label 字段: 对应 ProtobufCLabel 枚举
- type 字段: 对应 ProtobufCType 枚举
- offset 字段: 字段在结构体中的偏移量
-
结构体布局:
- 前 0x18 字节为 ProtobufCMessage 基础结构
- 后续为自定义字段
4.3 还原 .proto 文件
通过逆向分析可以还原出原始 .proto 文件定义,例如:
syntax = "proto3";
package example;
message Person {
string name = 1;
int32 id = 2;
string email = 3;
}
5. 实战案例分析
5.1 [CISCN 2023 初赛]StrangeTalkBot
分析过程:
-
定位到 ProtobufCMessageDescriptor 结构体
-
分析字段描述符数组:
- 第一个字段 actionid:
- id: 1
- label: PROTOBUF_C_LABEL_REQUIRED (0)
- type: PROTOBUF_C_TYPE_SINT64 (4)
- offset: 0x18
- 第一个字段 actionid:
-
还原 .proto 文件
漏洞利用:
- UAF (Use-After-Free) 漏洞
- 利用思路:
- 泄漏 libc 和堆地址
- 修改指针申请到 free_hook
- 构造 ORW 链
- 触发漏洞
5.2 [CISCN 2024]protoverflow
分析过程:
- 识别 C++ 版本的 Protobuf 结构
- 手撕 IDA 还原 .proto 文件
- 发现栈溢出漏洞
利用方法:
- 构造恶意 Protobuf 数据
- 触发栈溢出控制流劫持
6. 工具推荐
- pbtk - Protobuf 逆向工具
- 半自动化 IDAPython 脚本 - 用于快速提取 Protobuf 结构
- protoc - 官方编译器,用于验证还原的 .proto 文件
7. 总结
Protobuf 逆向工程关键点:
- 理解 Protobuf 的核心数据结构
- 掌握 IDA 中识别 Protobuf 结构的方法
- 能够从二进制中还原 .proto 文件定义
- 了解不同语言实现(C/C++/Python)的差异
- 熟悉常见漏洞模式和利用方法
通过系统分析 Protobuf 的结构和逆向方法,可以有效应对相关二进制分析挑战。