sw1ndle777/findmicrovolts-orders
GitHub: sw1ndle777/findmicrovolts-orders
一个 IDA Pro 插件,利用 YARA 规则自动发现 MicroVolts/MegaVolts 游戏客户端中的网络数据包 order ID 及其对应的发送和接收处理函数。
Stars: 0 | Forks: 0
# 查找 Microvolts Orders
一个用于逆向工程 **MicroVolts / MegaVolts** 游戏客户端的 **IDA Pro** 插件。
它可以自动在客户端二进制文件中找到两样东西:
1. **REQ orders** —— 客户端*构建并发送*网络数据包的位置(发送端)。
2. **ACK handlers** —— 客户端用来*接收和处理*服务器数据包的分发表(接收端)。
这两项结果都会在 IDA 中以可点击的列表显示,因此你可以直接跳转到相应代码。
## 它的用途(简而言之)
MicroVolts 客户端通过小型网络“数据包”与服务器进行通信。每个数据包都有一个
**order ID** —— 本质上是一个用来说明*这是什么类型的消息*的数字(例如 `162 = USER_CHAT`,
`140 = ROOM_JOIN`,`88 = ITEM_EQUIP`)。请参阅下面的两个示例:
**REQ(客户端发送的 order):**

**ACK(客户端收到时运行的 handler):**

要想对客户端进行任何有用的操作 —— 编写私服、bot、数据包记录器,
或是梳理协议 —— 你首先需要知道二进制文件中**哪个 order ID 对应哪个函数**。
这正是这个插件能为你提供的。
## 在此之前我是怎么做的(手动方式)
在这个插件出现之前,梳理 orders 意味着要在 IDA 中全靠手工完成:
- **对于 REQ(发送)orders:** 打开反汇编代码,寻找构建数据包的代码,并用肉眼从指令中读出
order ID。order ID 并不是以纯数字形式存储的 —— 它会被掩码处理并移位到数据包头部的
一个 10 位字段中(`... & 0x3FF`,然后 `<< 6`),因此你必须在大脑中解码每一处:
找到 `mov reg, 3FFh`、`and`、`shl`,然后计算出真正的 order 编号。每个客户端版本都会
打乱地址,所以每次都必须从头重新做一遍。
- **对于 ACK(接收)handlers:** 手动搜寻巨大的 handler 表 —— 一个以 order ID 为索引的
函数指针块 —— 通常通过寻找一处 `mov [table+N*4], func` 写入操作,然后滚动寻找其余
部分,以此猜测表的起始位置以及哪个索引对应哪个 order。
这个过程很慢、容易出错,而且必须针对每个新的客户端版本重复进行。这个插件将
整个流程变成了两个快捷键。
## 安装
将这两个文件复制到你的 IDA `plugins` 文件夹中(它们必须放在同一目录下):
```
/plugins/FindMicrovoltsOrders.py
/plugins/packets.rules
```
要求:
- 带有 IDAPython (Python 3) 运行时的 IDA Pro。
- 为 IDA 的 Python 安装了 [`yara-python`](https://pypi.org/project/yara-python/) 包
(`pip install yara-python`)。
你也可以将额外的 `*.rules` 文件放入 IDA 用户目录下的
`plugins/findmicrovolts-orders/` 中 —— 插件会自动将它们与 `packets.rules` 一起加载。
## 用法
在 IDA 中打开客户端,等待自动分析完成,然后:
| 操作 | 快捷键 | 作用 |
| --- | --- | --- |
| **查找 REQ orders** | `Ctrl-Alt-O` | 使用 YARA 扫描 `.text` 寻找数据包构建位置,解码每个 order ID,并列出它们 |
| **查找 ACK handlers** | `Ctrl-Alt-A` | 自动发现接收 handler 表,并将每个槽位映射到一个 order ID |
你也可以从 **Search** 菜单运行它,或者按下插件快捷键 `Ctrl-Alt-O`
来获取一个 REQ / ACK / 取消的提示。
### REQ 结果
一个包含地址、匹配的规则、解码后的 **order ID** 及其可读名称(例如 `162 -> USER_CHAT`)的表格。
双击一行即可跳转到该代码。作为额外奖励,当它找到 `CTcpPacket` 构造函数时,它会对其
进行命名并为你应用正确的 struct 原型。
### ACK 结果
一个列出每个 handler 槽位的表格:**order ID**、其名称、表索引、handler
函数地址/名称,以及它在代码中注册的位置。双击即可跳转到该 handler。
## 工作原理
### REQ 扫描 —— YARA 规则 (`packets.rules`)
order ID 在代码中从不会以纯数字形式出现。客户端将其打包到数据包头部的 10 位
字段中,因此编译后的指令总是呈现出可识别的形状。
`packets.rules` 文件是一组匹配这些形状的 **YARA 字节模式**。每条规则都带有
`meta` 字段,告诉插件*如何从*匹配的字节中*读取回 order ID*。
共有三类模式:
**模式 A —— 较小的 order ID (≤ 0x7F),ID 是一个立即数 byte。**
编译器发出 `mov reg32, 3FFh`(10 位掩码),然后是 `and reg16, imm8`,接着是 `shl reg16, 6`。
order byte 就位于 `and` 指令内部。
```
B8 FF 03 00 00 mov eax, 3FFh ; the 0x3FF mask
66 83 E0 ?? and ax, ; <-- order byte is here (offset 8)
66 C1 E0 06 shl ax, 6
```
规则:`order_and_imm8_eax`、`order_and_imm8_ecx`、`order_and_imm8_edx`(编译器碰巧
选择的每个寄存器各一条)。`meta: src_type="u8", src_off=8` 告诉插件在
匹配项的偏移量 8 处读取一个 byte。
**模式 B —— 较大的 order ID,ID 是一个完整的 dword 立即数。**
这里 ID 首先被加载为一个 32 位的值,然后在第二个寄存器中与 `3FFh` 进行掩码操作。
```
B8 ?? ?? 00 00 mov eax, ; <-- order dword at offset 1
B9 FF 03 00 00 mov ecx, 3FFh
66 23 C1 and ax, cx
66 C1 E0 06 shl ax, 6
```
规则:`order_mov_eax_and_ecx`、`order_mov_ecx_and_edx`、`order_mov_edx_and_eax`。
`meta: src_type="u32le", src_off=1` → 在偏移量 1 处读取一个 little-endian dword。
**模式 C —— 主机/中继构建器(`and` 掩码 + `or` 一个预先移位的值)。**
一些服务器端/主机端的数据包是通过屏蔽掉低位并 OR 操作一个已经移位过的 order
值来构建的。插件读取该值并将其右移 6 位以恢复 ID。
```
83 E0 3F and eax, 3Fh
0D ?? ?? 00 00 or eax, ; <-- value at offset 4, shift right by 6
```
规则:`order_or_eax_mask3f`、`order_or_eax_mask0f`、`order_or_ecx_mask0f`,以及一个
允许中间出现 `push 0` 的 `..._gap` 变体。`meta: rshift=6` 执行反向移位。
还有一个特殊的 **`CTcpPacket_ctor`** 规则(`kind="ctor"`)。它匹配数据包类
构造函数的指纹(其头部初始化代码,包括由 `rand` 生成种子的 "bugus" 字段)。
找到后,插件会重命名该函数并应用一个干净的 C struct 原型 —— 这既为分析提供了锚点,
又让插件能够**确认每个匹配项确实是数据包构建器**(它只保留函数中实际调用了
`CTcpPacket` 的 REQ 命中,从而消除误报)。
**为什么使用 YARA 而不是硬编码地址?** 因为每个客户端构建版本都会重定位所有内容。
然而,指令的*形状*保持不变。基于字节模式匹配意味着相同的规则
可以跨 MicroVolts 和 MegaVolts 客户端版本工作,实现**零硬编码地址**。
### ACK 扫描 —— handler 表自动发现
接收端是一个以 order ID 为索引的大型函数指针数组。客户端使用类似
`mov [table + index*4], handler_func` 的指令填充它 —— 即 `C7 05` 操作码(将 dword 移动到
绝对地址)。插件会:
1. 扫描 `.text` 中每一个 `C7 05` 写入操作,要求其**目的地是一个按 4 字节对齐的数据地址**,并且
其**值是一个真实函数的起始地址**。这些就是 handler 注册。
2. 按邻近度对目的地地址进行**聚类**,以找到连续的表。
3. **自动检测表基址**:它向后滑动候选基址,并根据生成的索引与已知 order 名称(`EORDER_NAMES`)
匹配的数量对每个猜测进行评分。得分最高者获胜 —— 无需硬编码基址。
4. 以同样的方式**自动检测第二个 handler 区域**(`handler_2`),适用于将 handlers
拆分为两个范围的客户端。
5. 输出最终的 `order ID -> handler 函数`映射。
`EORDER_NAMES`(脚本顶部的大字典)是已知 order ID 和名称的真实列表。
它同时支持自动检测评分和结果表中的友好名称显示。
## 文件
| 文件 | 用途 |
| --- | --- |
| `FindMicrovoltsOrders.py` | IDA 插件(UI、REQ 扫描、ACK 自动发现、order 名称表)。 |
| `packets.rules` | 用于发送端数据包构建器的 YARA 字节模式。 |
## 注意事项
- 如果在 REQ 上没有任何匹配项,请确保 `packets.rules` 与 `.py` 文件在同一目录下,并且 `yara-python`
可以在 IDA 的 Python 中被导入。
- ACK 扫描需要足够的 handler 注册才能进行聚类(如果找到的太少它将中止);请在
完整的自动分析完成后运行它。
- 除了构造函数的重命名/原型设计和定义任何缺失的 handler 函数外,这两项扫描都是只读的。
标签:IDA Pro 插件, YARA, 云资产可视化, 云资产清单, 游戏安全, 网络协议分析, 逆向工具, 逆向工程