# 需求：逆向分析关键点优先策略

## 1. 背景与目标

### 1.1 背景

复盘 CRACKME3.EXE（UPX 壳 crackme）分析过程，AI 检测到 UPX 壳后，花费 ~80% context 逐比特逆向 NRV2E 解压缩算法的 x86 汇编实现，试图手写 Python 解压器，最终 context 耗尽、任务失败。

实际上 AI 已具备所有需要的信息（入口点 0x429000、UPX2 段完整字节码、段布局），只需从 stub 尾部找到 `popad; jmp OEP`（字节 `61 E9 ...`）并计算跳转目标，即可获得 OEP，然后动态 dump 解壳后的内存。

### 1.2 根因

两处指引缺失：

1. **主 prompt 缺少逆向分析哲学** — 没有规则告诉 AI"遇到保护机制应找关键点绕过，而非逆向保护算法本身"
2. **packer-handling.md 策略顺序错误** — "阶段 3：静态分析脱壳"要求"decompile 入口点、理解解壳算法"，这直接导致 AI 试图逆向 NRV2E。缺少"先找关键点绕过"的阶段

### 1.3 目标

- 主 prompt 增加逆向分析核心原则（通用，不限于脱壳）
- packer-handling.md 在静态分析之前插入"关键点绕过"阶段，并给出架构感知的 OEP 定位技术
- 确保描述汇编指令时覆盖 x86/x86_64 和 ARM64 两种架构

### 1.4 评估维度

| 维度 | 改进 |
|------|------|
| 减少上下文 | 脱壳阶段从"耗尽 context"→ 1-2 轮 idat + 1 个脚本，减少 90%+ |
| 减少对话轮次 | 脱壳阶段从 N 轮（失败）→ 2-3 轮（成功） |
| 提升速度 | 省去整个静态逆向解压算法阶段 |
| 提升准确度 | 避免"理解错比特流语义导致解压失败"的风险 |

---

## 2. 技术方案

### 2.1 改动总览

| ID | 方案 | 改动文件 | 类型 |
|----|------|---------|------|
| A | 主 prompt 增加逆向分析核心原则 | `ida-pro-analysis.md` | 修改（+8 行） |
| B | packer-handling.md 重构策略顺序，增加关键点绕过阶段 | `ida-pro-analysis-knowledge-base/packer-handling.md` | 修改（+80 行） |
| C | query.py entry_points 返回 architecture + bits 字段 | `query.py` | 修改（+8 行） |

### 2.2 依赖关系

```
A（主 prompt 核心原则）
  └── 通用原则，适用于所有分析场景（包括但不仅限于脱壳）

B（packer-handling.md 关键点绕过）
  └── A 原则在脱壳场景的具体化
  └── 引用现有 frida_unpack.py（阶段 3.5）
  └── 依赖 C（需要 architecture 信息选择正确的 OEP 模式表）

C（query.py architecture 字段）
  └── B 的前置依赖
```

### 2.3 方案 A：主 prompt 增加逆向分析核心原则

在 `ida-pro-analysis.md` 的"需求类型与执行策略"（第 158-165 行）和"执行流程"（第 168 行）之间插入新章节：

```markdown
## 逆向分析核心原则

1. **找关键点，不逆向机制** — 目标是找到关键调用、关键值、关键跳转。保护/混淆只是障碍，不是目标
2. **绕过优先于逆向** — 除非用户明确要求分析保护机制本身，否则寻找最短绕过路径（找 OEP、动态 dump、hook 关键点）
3. **该吃苦时吃苦，找到规律就切换** — 寻找关键点的过程可能需要笨办法（逐个检查函数、手动追踪数据流），但一旦发现规律或模式，立即用聪明办法
4. **模式识别优于从零分析** — 已知模式（UPX 段名、常见壳结构、密码学常量）直接利用，不重新发现
```

共 6 行（1 标题 + 1 空行 + 4 条原则），通用适用于所有分析场景。

### 2.4 方案 B：packer-handling.md 增加关键点绕过阶段

#### 2.4.1 核心变更：在阶段 2 和阶段 3 之间插入"阶段 2.5：关键点绕过"

当前策略顺序：阶段 1（检测）→ 阶段 2（已知壳自动脱壳）→ 阶段 3（静态分析脱壳）→ 阶段 3.5（动态脱壳）

变更后顺序：阶段 1（检测）→ 阶段 2（已知壳自动脱壳）→ **阶段 2.5（关键点绕过）** → 阶段 3（静态分析脱壳，降级为后备方案）

#### 2.4.2 阶段 2.5 的内容

```markdown
## 阶段 2.5：关键点绕过（适用于任何壳）

**核心思路**：壳的本质是"运行时还原代码"。不需要理解壳如何还原，只需要找到还原完成的那一刻，dump 内存即可。

### 策略 2.5.1：定位 OEP（原始入口点）

解壳 stub 结束时必然跳转到 OEP。通过分析 stub 尾部找到跳转指令，计算 OEP 地址。

#### x86 (32-bit)

| 模式 | 字节序列 | 说明 |
|------|---------|------|
| `popad; jmp OEP` | `61 E9 xx xx xx xx` | 经典 UPX 模式。`popad` 恢复寄存器，`jmp` 跳转到 OEP |
| `popad; push OEP; ret` | `61 68 xx xx xx xx C3` | 变体：push 目标地址后 ret |

**OEP 计算方法**：以 `jmp rel32`（E9 指令）为例：
- OEP = jmp_下一条指令地址 + rel32（有符号 32 位偏移）
- 即 OEP = (jmp_地址 + 5) + int32(偏移字节)

#### x86_64 (64-bit)

| 模式 | 字节序列 | 说明 |
|------|---------|------|
| `pop rXX; ... ; jmp rXX` | `5E`/`5F` + ... + `FF E6`/`FF E7` | 恢复寄存器后间接跳转 |
| `lea rax, [rip+off]; jmp rax` | `48 8D 05 xx xx xx xx FF E0` | RIP-relative 跳转 |

**OEP 计算方法**：同 x86 的 `jmp rel32`（E9 指令），RIP-relative 的 `lea` 计算类似。

#### ARM64 (AArch64)

| 模式 | 指令 | 说明 |
|------|------|------|
| 恢复寄存器 + `ret` | `ldp x29, x30, [sp], #N; ret` | 从栈恢复 FP/LR 后返回，LR 即 OEP |
| 恢复寄存器 + `br` | `ldr x0, [sp, #off]; br x0` | 从栈加载 OEP 到寄存器后间接跳转 |
| `adr` + `br` | `adr x0, #off; br x0` | 短距离 PC-relative 跳转（±1MB） |
| `adrp` + `add` + `br` | `adrp x0, #page; add x0, x0, #off; br x0` | 长距离 PC-relative 跳转（±4GB），比 `adr` 更常见 |

#### 通用定位技巧

1. **从 entry_points 获取 stub 起始地址和 architecture 字段**，根据架构选择对应的 OEP 模式表
2. 用 `read_data`（bytes 模式，`IDA_READ_SIZE` 设为 256~512）读取 stub 尾部（最后 256-512 字节）
3. **搜索特征字节序列**（上表中的模式）
4. **验证 OEP 合理性**：OEP 应在可执行段内（通常在代码段的起始区域），不应在壳的解压数据段内。如果所有候选 OEP 均不合理，视为"找不到 OEP"，进入阶段 3

### 策略 2.5.2：搜索目标特征字符串

目标程序的功能性字符串（如 crackme 的"Failed"、"Enter your name"、错误提示）在解壳后必然出现在内存中。

**步骤**：
1. 从用户描述中提取目标程序的特征信息（UI 文本、错误消息、API 调用）
2. 用 `read_data`（bytes 模式）在目标代码段搜索这些字符串
3. 找到后通过交叉引用或上下文定位关键逻辑

**注意**：此策略在加壳版本上可能搜不到（字符串被压缩），但在动态 dump 后的二进制上有效。

### 策略 2.5.3：动态 dump

定位 OEP 后，使用动态方法 dump 解壳后的内存：

1. **优先使用 frida_unpack.py**（阶段 3.5 已有，仅支持 PE 格式）
2. 如果 Frida 不可用（未安装 / 目标非 PE 格式如 ELF/Mach-O / 反调试 / 无法运行），回退到阶段 3（静态分析）

### 决策树（更新版）

```
检测到加壳
  ├── 已知壳（UPX/ASPack 等）→ 阶段 2：自动脱壳工具
  │     ├── 成功 → 脱壳后后续流程
  │     └── 失败 → 阶段 2.5
  ├── 未知壳 → 阶段 2.5：关键点绕过
  │     ├── 找到 OEP → 动态 dump（阶段 3.5）
  │     │     ├── 成功 → 脱壳后后续流程
  │     │     └── 失败（反调试/无法运行）→ 阶段 3
  │     └── 找不到 OEP → 阶段 3：静态分析脱壳
  └── 阶段 3 失败 2 次 → 告知用户限制
```

**规则**：
1. 优先使用关键点绕过（阶段 2.5）而非静态逆向解壳算法（阶段 3）
2. 阶段 3 仅在以下场景使用：
   - 动态方法失败（反调试、无法运行）
   - 二进制不能运行（嵌入式固件）
   - 用户明确要求分析壳的实现
3. 禁止在阶段 3 上花费超过 30 分钟（即使重试次数未达上限）
4. 禁止在同一个失败方向上重试超过 2 次
```

#### 2.4.3 额外改动：保持描述一致性

以下改动确保各文件之间的描述不冲突：

1. **更新 `ida-pro-analysis.md` 第 196 行**（packer-handling 策略描述）：
   - 原文：`获取完整处理流程（阶段 1-3：壳检测 → 已知壳自动脱壳 → 静态分析脱壳）`
   - 改为：`获取完整处理流程（阶段 1→2→2.5→3→3.5）`

2. **更新 `packer-handling.md` 阶段 2 的失败路径**（第 21 行）：
   - 原文：`**未安装** → 告知用户安装命令（如 brew install upx），然后进入阶段 3`
   - 改为：`**未安装** → 告知用户安装命令（如 brew install upx），然后进入阶段 2.5`
   - 增加：`**已安装但脱壳失败** → 进入阶段 2.5`

#### 2.4.4 阶段 3 的定位调整

将现有阶段 3 的描述从"当没有可用的自动脱壳工具时"改为"当关键点绕过失败且无法动态 dump 时"，明确其作为后备方案的定位。阶段 3 的内容保持不变（仍为：分析 stub → 提取数据 → 生成脱壳机 → 执行验证）。

### 2.5 方案 C：query.py entry_points 返回 architecture + bits 字段

**问题**：方案 B 的 OEP 模式表依赖 AI 知道当前二进制是 x86(32-bit)、x86_64 还是 ARM64。但 `entry_points` 当前仅返回 `file_type`（exe/dll/so），不返回处理器架构。AI 无法选择正确的模式表。

**改动**：在 `query.py` 的 `_query_entry_points` 函数返回中增加 `architecture` 字段。

```json
{
  "success": true,
  "query": "entry_points",
  "data": {
    "entries": [...],
    "file_type": "exe",
    "architecture": "x86",
    "total": 1
  }
}
```

**实现方式**：使用 `ida_ida.inf_get_procname()` 获取 IDA 识别的处理器类型，映射为以下标准化值：

| `inf_get_procname()` 返回值 | `architecture` 值 | 说明 |
|---|---|---|
| `"metapc"` | `"x86"` | x86/x86_64（32/64-bit 由地址宽度区分） |
| `"ARM"` | `"arm"` | ARM（32-bit） |
| `"ARM64"` / `"aarch64"` | `"arm64"` | ARM64/AArch64 |
| `"MIPS"` | `"mips"` | MIPS |
| `"PPC"` | `"ppc"` | PowerPC |
| 其他 | 原始值 | 未知架构保留原始字符串 |

对于 `"x86"` 架构，额外返回 `bits` 字段区分 32/64-bit：

```json
"architecture": "x86",
"bits": 32
```

`bits` 通过 `ida_ida.inf_is_64bit()` / `ida_ida.inf_is_32bit_exactly()` 判断。

#### 2.4.5 更新阶段 3 开头说明

将现有"阶段 3：静态分析脱壳（自定义壳 / 无脱壳工具）"改为：

```markdown
## 阶段 3：静态分析脱壳（后备方案：动态方法失败时）

当阶段 2.5 的关键点绕过和阶段 3.5 的动态 dump 均失败时（反调试壳、嵌入式固件、无法运行），回退到静态分析。

仅在以下场景使用此阶段：
- 二进制无法运行（嵌入式固件、缺失依赖）
- 壳有强反调试（检测 Frida / 调试器后拒绝解壳）
- 用户明确要求分析壳的实现算法

⚠ 此阶段耗时较长，容易消耗大量 context。仅在确实无法绕过时使用。
```

---

## 3. 实现规范

### 3.1 改动范围表

| 文件 | 改动类型 | 行数变化 | 影响范围 |
|------|---------|---------|---------|
| `ida-pro-analysis.md` | 修改 | +8 行（插入新章节）+ 1 行（更新第 196 行描述） | 主 prompt，AI 编排行为 |
| `packer-handling.md` | 修改 | +80 行（新增阶段 2.5 + 更新决策树 + 调整阶段 2/3 定位） | 知识库，按需加载 |
| `query.py` | 修改 | +8 行（entry_points 增加 architecture + bits 字段） | 仅 entry_points 查询类型 |

### 3.2 不修改的文件

- `_base.py`、`_utils.py`、`update.py` — 无变更
- `dynamic-analysis.md`、`crypto-validation-patterns.md`、`script-generation.md` — 无变更
- `disassembler/frida_unpack.py` — 无变更

### 3.3 编码规则

- 不涉及 _base.py/_utils.py/update.py 修改，仅 query.py entry_points 增加 2 个返回字段
- 知识库 .md 文件必须自包含（不依赖主 prompt 上下文即可理解）
- 汇编指令描述必须覆盖 x86/x86_64 和 ARM64 两种架构

---

## 4. 验收标准

### 4.1 功能验收

- [ ] A: `ida-pro-analysis.md` 包含"逆向分析核心原则"章节（4 条原则）
- [ ] A: `ida-pro-analysis.md` 第 196 行描述更新为新的阶段顺序
- [ ] B: `packer-handling.md` 包含"阶段 2.5：关键点绕过"
- [ ] B: 阶段 2.5 包含架构感知的 OEP 定位技术（x86 32-bit / x86_64 / ARM64 三张表）
- [ ] B: 阶段 2 的失败路径更新为进入阶段 2.5（非阶段 3）
- [ ] B: 决策树已更新（2.5 → 3.5 → 3 的优先级顺序）
- [ ] B: 阶段 3 定位调整为后备方案，开头有明确的使用场景说明
- [ ] B: 30 分钟超时规则保留
- [ ] C: `query.py` entry_points 返回 `architecture` 和 `bits` 字段

### 4.2 回归验收

- [ ] 非加壳二进制分析流程不受影响
- [ ] 已知壳（UPX）场景仍优先使用 `upx -d`（阶段 2 不变）
- [ ] 阶段 3 的内容不变（只是定位从"首选"变为"后备"）
- [ ] 阶段 3.5（动态脱壳/Frida）的内容和引用不变
- [ ] `entry_points` 的其他调用方（非加壳场景）不受影响（新增字段为额外信息）

### 4.3 架构验收

- [ ] 依赖方向合规：主 prompt → 知识库（单向）
- [ ] 知识库文件自包含，可独立理解
- [ ] 主 prompt 新增内容 ≤ 10 行

---

## 5. 与现有文档的关系

| 现有文档 | 关系 |
|---------|------|
| [需求-加壳二进制静态分析脱壳流程](需求-加壳二进制静态分析脱壳流程.md) | 产出了阶段 3（静态分析脱壳）。本次需求在阶段 3 前插入阶段 2.5（关键点绕过），将阶段 3 降级为后备方案 |
| [需求-动态分析能力与脱壳流程增强](需求-动态分析能力与脱壳流程增强.md) | 产出了阶段 3.5（动态脱壳）和决策树。本次需求更新决策树，将关键点绕过（2.5）排在静态分析（3）之前 |
| [需求-加壳检测与异常分析增强](需求-加壳检测与异常分析增强.md) | 产出了 packer_detect 能力。本次需求增强检测后的策略选择 |

---

## 6. 不做的事

1. **不新增查询类型**：现有 `entry_points`、`read_data`、`disassemble` 已够用
2. **不写 OEP 自动计算脚本**：OEP 计算足够简单（相对跳转的加减法），AI 可手动完成。如果反复出现需要 3 次以上再考虑沉淀
3. **不修改阶段 3 的内容**：仅调整定位，不改变静态分析脱壳的步骤
