marin-m/pbtk
GitHub: marin-m/pbtk
一套用于从多种平台逆向提取 Protobuf 定义并支持可视化编辑、重放与模糊测试的图形化工具集。
Stars: 1631 | Forks: 198
# pbtk - 逆向工程 Protobuf 应用
**[Protobuf](https://developers.google.com/protocol-buffers/) 是一种序列化格式**,由 Google 开发,并被越来越多的 Android、Web、桌面及其他应用程序所使用。它包含一种**用于声明数据结构的语言**,随后根据目标实现将其编译为代码或其他类型的结构。
pbtk (*Protobuf toolkit*) 是一套功能齐全的脚本集合,通过统一的 GUI 访问,提供两个主要功能:
- **从程序中提取 Protobuf 结构**,将其转换回可读的 *.proto* 文件,支持多种实现:
- 所有主要的 Java 运行时(base、Lite、Nano、Micro、J2ME),完全支持 Proguard,
- 包含嵌入式反射元数据的二进制文件(通常是 C++,有时是 Java 和大多数其他绑定),
- 使用 JsProtoUrl 运行时的 Web 应用程序。
- **编辑、重放和模糊测试**发送到 Protobuf 网络端点的数据,通过一个方便的图形界面,允许您实时编辑 Protobuf 消息的字段并查看结果。

## 安装
PBTK 需要 Python ≥ 3.5、PySide 6、Python-Protobuf 3 以及一些可执行程序(chromium、jad、dex2jar...)来运行提取脚本。
Archlinux 用户可以直接通过[软件包](https://aur.archlinux.org/packages/pbtk-git/)安装:
```
$ yay -S pbtk-git
$ pbtk
```
在大多数其他发行版上,您需要直接运行它:
```
# 对于 Ubuntu/Debian testing 衍生版:
$ sudo apt install python3-pip git openjdk-8-jre python3-qtpy-pyside6
# 然后,使用 UV:
$ sudo snap install --classic astral-uv
$ uv tool install pbtk
$ pbtk
# Or using pipx:
$ sudo apt install pipx
$ pipx install pbtk
$ pbtk
```
也支持 Windows(需要相同的模块)。运行 GUI 后,根据您尝试执行的操作,它会提示您缺少哪些组件。
## 命令行用法(通过包管理器安装)
可以通过主脚本启动 GUI:
```
pbtk
```
以下脚本也可以在没有 GUI 的情况下独立使用:
```
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir]
```
## 命令行用法(本地)
可以通过主脚本启动 GUI:
```
uv sync # Download dependencies to the .venv folder
source .venv/bin/activate # Put the local scripts in $PATH for the current shell session
uv tool install -e . # Put the local scripts in $PATH all time
pbtk
```
以下脚本也可以在没有 GUI 的情况下独立使用:
```
pbtk-jar-extract [-h] input_file [output_dir]
pbtk-from-binary [-h] input_file [output_dir]
pbtk-web-extract [-h] input_url [output_dir]
```
## 典型工作流程
假设您正在对一个 Android 应用程序进行逆向工程。您使用自己喜欢的反编译器对应用程序进行了一些探索,并发现它以典型的方式通过 HTTPS 传输 Protobuf 作为 POST 数据。
您打开 PBTK,会受到有意义的问候:

第一步是将您的 .protos 转换为文本格式。如果您的目标是 Android 应用程序,放入 APK 并等待应该就能完成神奇的工作!(除非它是一个非常异类的实现)

完成后,您跳转到 `~/.pbtk/protos/`(通过命令行,或欢迎屏幕底部的按钮打开文件浏览器,随您喜欢)。应用程序的所有 .protos 确实都在这里。
回到反编译器中,您偶然发现了构建发送到您感兴趣的 HTTPS 端点的数据的类。它通过调用一个由生成的代码组成的类来序列化 Protobuf 消息。

后一个类应该在您的 .protos 目录中有一个完美的匹配(即 `com.foo.bar.a.b` 将匹配 `com/foo/bar/a/b.proto`)。无论哪种方式,grep 其名称都应该能让您找到它。
太棒了:下一步是进入 **Step 2**,选择所需的输入 .proto,并填写有关您的端点的一些信息。

您还可以提供一些通过 mitmproxy 或 Wireshark 捕获的、发送到此端点的原始 Protobuf 数据样本,您将以十六进制编码的形式粘贴它。
**Step 3** 是有趣的部分,点击按钮看看会发生什么!您有一个树状视图,代表 Protobuf 结构中的每个字段(重复字段以“+”为后缀,必填字段没有复选框)。

只需将鼠标悬停在字段上即可获得焦点。如果字段是整数类型,请使用鼠标滚轮对其进行递增/递减。枚举信息也会在悬停时出现。
就是这样!您可以据此确定每个字段的含义。如果您是从混淆代码中提取的 .protos,您可以根据您注意到的含义重命名字段,方法是单击它们的名称。
祝逆向愉快! 👌 🎉
## 本地数据存储
PBTK 将提取的 .proto 信息存储到 `~/.pbtk/protos/`(在 Windows 上为 `%APPDATA%\pbtk\protos`)。
您可以直接通过常规文件浏览器和文本编辑器在此目录中移入、移出、重命名、编辑或删除数据,这是预期的操作方式,不会干扰 PBTK。
基于 HTTP 的端点作为 JSON 对象存储在 `~/.pbtk/endpoints/` 中。这些对象是请求/响应对的数组,如下所示:
```
[{
"request": {
"transport": "pburl",
"proto": "www.google.com/VectorTown.proto",
"url": "https://www.google.com/VectorTown",
"pb_param": "pb",
"samples": [{
"pb": "!....",
"hl": "fr"
}]
},
"response": {
"format": "other"
}
}]
```
## 源代码结构
PBTK 在内部使用两种类型的可插拔模块:提取器(extractor)和传输器(transport)。
* **extractor** 支持从目标 Protobuf 实现或平台提取 .proto 结构。
提取器在 `src/extractors/*.py` 中定义。它们被定义为一个前面带有装饰器的方法,如下所示:
```
@register_extractor(name = 'my_extractor',
desc = 'Extract Protobuf structures from Foobar code (*.foo, *.bar)',
depends={'binaries': ['foobar-decompiler']})
def my_extractor(path):
# Load contents of the `path` input file and do your stuff...
# Then, yield extracted .protos using a generator:
for i in do_your_extraction_work():
yield proto_name + '.proto', proto_contents
# Other kinds of information can be yield, such as endpoint information or progress to display.
```
* **transport** 支持一种通过网络反序列化、重新序列化和发送 Protobuf 数据的方式。例如,最常用的传输方式是 HTTP 上的原始 POST 数据。
传输器在 `src/utils/transports.py` 中定义。它们被定义为一个前面带有装饰器的类,如下所示:
```
@register_transport(
name = 'my_transport',
desc = 'Protobuf as raw POST data',
ui_data_form = 'hex strings'
)
class MyTransport():
def __init__(self, pb_param, url):
self.url = url
def serialize_sample(self, sample):
# We got a sample of input data from the user.
# Verify that it is valid in the form described through "ui_data_form" parameter, fail with an exception or return False otherwise.
# Optionally modify this data prior to returning it.
bytes.fromhex(sample)
return sample
def load_sample(self, sample, pb_msg):
# Parse input data into the provided Protobuf object.
pb_msg.ParseFromString(bytes.fromhex(sample))
def perform_request(self, pb_data, tab_data):
# Perform a request using the provided URL and Protobuf object, and optionally other transport-specific side data.
return post(url, pb_data.SerializeToString(), headers=USER_AGENT)
```
## 未来的改进
以下内容可能会在未来的版本中推出:
* 完成自动模糊测试部分。
* 支持从 Java 代码中提取扩展。
* 支持 JSPB(主要的 JavaScript)运行时。
* 如果您希望支持任何其他平台,请提出 issue,我会查看。
我已尽力为大多数模块生成易于阅读和注释完善的代码(除了那些主要是不言自明的部分,如连接 GUI 信号),以便您可以做出贡献。
## 许可
pbtk 根据 [GNU GPL](https://www.gnu.org/licenses/gpl-3.0.html) 许可证发布(我在此声明,等等)。
对于项目名称的字母大小写没有形式化的规则,规则只是随心所欲 ❤
标签:C++逆向, Fuzzing, GUI工具, Java反编译, Protobuf, Proto文件生成, PySide, Python, TLS抓取, 二进制分析, 云安全运维, 云资产清单, 协议分析, 协议缓冲区, 安卓逆向, 序列化格式, 数据提取, 数据结构还原, 无后门, 权限提升, 系统运维工具, 网络流量分析, 网络端点, 谷歌协议缓冲区, 软件安全, 逆向工具, 逆向工程