When to Use
- A program updated and old annotations/symbols must be migrated to the new build.
- Recovering changed functions between two versions of a stripped binary.
Scope of application
Use this skill when the task falls into the following scenarios:
- Kernel/driver missing PDB — You have symbols for an old version of ntoskrnl.exe, the new version's PDB has been taken down by Microsoft, and you need to use the old symbols to infer the addresses of non-exported functions in the new version
- Symbol migration after a program update — You have reverse engineered a program before, the program has been updated, and you don't want to reverse it all over again; use the old results to migrate in bulk
- Protection mechanism update — The old version has complete reverse engineering results, and the new version requires quickly locating the new offset of the same function
- Any binary comparison scenario with "old version has symbols + new version has no symbols"
Division of labor with other skills
| Scenario | What to use |
|---|---|
| Reverse engineering a binary from scratch | ida-reverse/ or radare2/ |
| Have old version results, migrate to the new version | This skill |
| Comparing two completely different binaries | BinDiff / Diaphora (traditional tools) |
Core advantages
Compared with traditional solutions:
| Solution | Cost for 200 functions | Time | Accuracy |
|---|---|---|---|
| Manually opening two IDA windows to compare | Free but life-draining | Several hours | High |
| BinDiff automatic matching | Free | Fast | Medium (fails when structural changes are large) |
| Fully delegated to an Agent (CC/Codex) | 50-100 yuan | Slow | High |
| This skill (LLM batch comparison) | ~1 yuan | ~10 seconds/function | High |
Core principles
旧版函数(有符号) 新版同一函数(无符号)
↓ ↓
导出反汇编 + 伪代码 导出反汇编 + 伪代码
↓ ↓
└──────── LLM 结构化比对 ────────┘
↓
输出 YAML(符号映射表)
↓
程序化解析 → 批量应用到新版 IDB
Key points:
- The prompt is a fixed template, filled in programmatically
- Input and output formats are fixed, parsed programmatically
- The LLM is only responsible for the step of "looking at two pieces of code and finding the correspondence"
- Time cost and token cost are extremely low
Prompt template
Standard comparison Prompt
I have disassembly outputs and procedure code of the same function.
This is the function for reference:
**Disassembly for Reference**
```c
{disasm_for_reference}
Procedure code for Reference
{procedure_for_reference}
This is the function you need to reverse-engineering:
Disassembly to reverse-engineering
{disasm_code}
Procedure code to reverse-engineering
{procedure}
What you need to do is to collect all references to "{symbol_name_list}" in the function you need to reverse-engineering and output those references as YAML.
Example:
found_vcall: # This is for indirect call to virtual function or virtual function pointer fetching.
- insn_va: '0x180777700' # Always be the instruction with displacement offset
insn_disasm: call [rax+68h] # Always be the instruction with displacement offset
vfunc_offset: '0x68'
func_name: ILoopMode_OnLoopActivate
- insn_va: '0x180777778' # Always be the instruction with displacement offset
insn_disasm: mov rax, [rax+80h] # Always be the instruction with displacement offset
vfunc_offset: '0x80'
func_name: INetworkMessages_GetNetworkGroupCount
found_call: # This is for direct call to non-virtual regular function.
- insn_va: '0x180888800'
insn_disasm: call sub_180999900
func_name: CLoopMode_RegisterEventMapInternal
- insn_va: '0x180888880'
insn_disasm: call sub_180555500
func_name: CLoopMode_SetSystemState
found_funcptr: # This is for non-virtual regular function pointer.
- insn_va: '0x180666600' # Must load/reference the function pointer target address
insn_disasm: lea rdx, sub_15BC910 # Must load/reference the function pointer target address
funcptr_name: CLoopMode_OnClientPollNetworking
found_gv: # This is for reference to global variable.
- insn_va: '0x180444400'
insn_disasm: mov rcx, cs:qword_180666600 # Must load/reference the global variable
gv_name: g_pNetworkMessages
- insn_va: '0x180333300'
insn_disasm: lea rax, unk_180222200 # Must load/reference the global variable
gv_name: s_EventManager
found_struct_offset: # This is for reference to struct offset. NOTE THAT virtual function pointer should not be here! virtual function pointer should ALWAYS be in found_vcall !
- insn_va: '0x1801BA12A' # Always be the instruction with displacement offset
insn_disasm: mov rcx, [r14+58h] # Always be the instruction with displacement offset
offset: '0x58'
size: 8
struct_name: CResourceService
member_name: m_pEntitySystem
If nothing found, output an empty YAML. DO NOT output anything other than the desired YAML. DO NOT collect unrelated symbols.
### 变量说明
| 变量 | 来源 | 说明 |
|------|------|------|
| `{disasm_for_reference}` | 旧版 IDA 导出 | 有符号的反汇编 |
| `{procedure_for_reference}` | 旧版 IDA 导出 | 有符号的伪代码 |
| `{disasm_code}` | 新版 IDA 导出 | 无符号的反汇编 |
| `{procedure}` | 新版 IDA 导出 | 无符号的伪代码 |
| `{symbol_name_list}` | 从旧版提取 | 需要在新版中定位的符号列表 |
## 工作流
### 完整流程
```text
Step 1: 准备数据
- 旧版二进制加载到 IDA(有 PDB/符号)
- 新版二进制加载到 IDA(无符号)
- 找到两个版本中相同的锚点函数(导出函数、字符串引用等)
Step 2: 批量导出
- 从旧版导出:锚点函数的反汇编 + 伪代码(含符号名)
- 从新版导出:同一锚点函数的反汇编 + 伪代码(无符号名)
Step 3: LLM 比对
- 用 prompt 模板填充数据
- 调用 LLM API(推荐:deepseek 量大便宜,超大函数切 gpt)
- 解析返回的 YAML
Step 4: 应用结果
- 将 YAML 中的符号映射批量应用到新版 IDB
- 用 idapro_rename 或 IDAPython 脚本批量重命名
Step 5: 迭代
- 第一轮迁移的函数成为新的锚点
- 进入这些函数,继续对比内部调用
- 重复直到覆盖所有目标函数
Anchor selection strategy
| Anchor type | Reliability | Description |
|---|---|---|
| Exported functions | Highest | Names do not change, addresses may change |
| String references | High | String contents do not change, reference locations may change |
| Constants/magic numbers | Medium | Characteristic values do not change |
| Code patterns | Medium | Function structures are similar but addresses all change |
Batch processing recommendations
- Compare 1 function at a time (to avoid context explosion)
- Use deepseek for medium functions (<200 lines)
- Switch to gpt-4o or claude for very large functions (>500 lines)
- Use concurrent calls to improve speed (10-20 concurrent)
- Cache results to avoid repeated calls
Output format
The 5 symbol types in YAML output
| Type | Meaning | Key fields |
|---|---|---|
found_vcall |
Virtual function call (indirect call) | vfunc_offset, func_name |
found_call |
Direct function call | insn_va, func_name |
found_funcptr |
Function pointer reference | insn_va, funcptr_name |
found_gv |
Global variable reference | insn_va, gv_name |
found_struct_offset |
Struct offset reference | offset, struct_name, member_name |
Parsed application action
found_call → idapro_rename(addr=call_target, name=func_name)
found_vcall → idapro_set_comments(addr=insn_va, comment="vcall: {func_name} @ +{offset}")
found_funcptr → idapro_rename(addr=funcptr_target, name=funcptr_name)
found_gv → idapro_rename(addr=gv_addr, name=gv_name)
found_struct_offset → idapro_set_comments(addr=insn_va, comment="{struct_name}.{member_name}")
Typical scenario examples
Scenario 1: ntoskrnl.exe missing PDB
已有:ntoskrnl.exe 10.0.26100.2000 + 完整 PDB
目标:ntoskrnl.exe 10.0.26100.2605(PDB 被下架)
需求:定位 PspSetCreateProcessNotifyRoutine 的新地址
步骤:
1. 两个版本都加载到 IDA
2. 找到导出函数 PsSetCreateProcessNotifyRoutine(两个版本都有)
3. 旧版中它调用了 PspSetCreateProcessNotifyRoutine(有符号)
4. 新版中它调用了 sub_140822108(无符号)
5. LLM 一眼看出:sub_140822108 = PspSetCreateProcessNotifyRoutine
6. 批量应用
Scenario 2: Migrate after application update
已有:target.exe v1.0 的完整逆向结果(200+ 函数已命名)
目标:target.exe v1.1(所有符号丢失)
需求:批量迁移 200 个函数名
步骤:
1. 从旧版导出所有已命名函数的反汇编+伪代码
2. 在新版中通过导出函数/字符串找到对应锚点
3. 批量调用 LLM 比对
4. 解析 YAML,批量 rename
5. 迭代深入
LLM selection recommendations
| Model | Suitable scenarios | Cost | Speed |
|---|---|---|---|
| DeepSeek V3 | Small to medium functions (<200 lines), batch processing | Very low | Fast |
| GPT-4o | Very large functions, complex control flow | Medium | Fast |
| Claude Sonnet | Medium to large functions, requires reasoning | Medium | Fast |
| Claude Opus | Extremely complex functions, requires deep understanding | High | Slow |
Recommended strategy: default to DeepSeek, automatically upgrade when context exceeds limits or results are inaccurate.
Notes
- Do not throw the entire binary at the LLM — compare only one function at a time
- Anchors must be reliable — if the anchor itself is wrong, everything that follows is wasted
- Results require manual spot checks — LLMs are not 100% accurate; key symbols must be verified
- Cache intermediate results — avoid repeated calls that waste tokens
- Pay attention to context limits — very large functions (>1000 lines of disassembly) need to be split or a large-context model used
On-Demand Bootstrap
Tool dependencies
| Tool | Purpose | Can be installed automatically |
|---|---|---|
| IDA Pro | Export disassembly/pseudocode | ✗ (commercial software) |
| Python | Script execution, API calls | ✓ |
| PyYAML | Parse the YAML returned by the LLM | ✓(pip install pyyaml) |
| LLM API | Execute comparison | Requires API key |
Description
The core of this skill does not depend on installing heavy tools; it mainly depends on:
- IDA Pro already available (managed with the
ida-reverse/skill) - Python + requests/httpx (calling the API)
- An LLM API endpoint
Routing context
Upstream entry: skills/SKILL.md(overall control),routing.md
Trigger conditions: there are old-version symbols/reverse-engineering results that need to be migrated to the new version
Downstream exit:
- Need to open the binary first →
ida-reverse/ - Need to quickly scout and confirm version differences →
radare2/
Peer related modules: ida-reverse/(both data export and symbol application go through IDA)
Task completion self-check (MUST pass before claiming completion)
- Did I execute every step in the workflow (rather than just reading it)?
- Am I based on
tool-indexand used real tool paths? - Did I produce reproducible evidence (commands/scripts/screenshots/reports)?
- Did I complete and write back the Checklist items required by RULES?
Limitations
- Diff quality degrades with heavy recompilation or obfuscation between versions.
- Requires local diff tooling (e.g., BinDiff, Diaphora, radiff2).
Adapted from zhaoxuya520/reverse-skill (MIT).