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):** ![REQ order 示例](https://static.pigsec.cn/wp-content/uploads/repos/cas/32/3222798e0bb924f68e1ec39a69e5f6465a15ea0b8964c7af6bfd09e4c43f0700.png) **ACK(客户端收到时运行的 handler):** ![ACK order 示例](https://static.pigsec.cn/wp-content/uploads/repos/cas/8b/8b59cd011a8e565fd51a4a663bf9fb02975f55374f2f9f8188d5d952744aa41a.png) 要想对客户端进行任何有用的操作 —— 编写私服、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, 云资产可视化, 云资产清单, 游戏安全, 网络协议分析, 逆向工具, 逆向工程