SuperAPI AI Directory

ida

Coding & development

View repository

Updated 10-15 21:53

About this skill

IDA Pro Reverse Engineering

General reverse engineering methodology + MCP toolchain + Headless API.


Part 1: General Reverse Engineering Methodology

Key Principles

  • F5 first, disasm second (HARD RULE) — MUST call decompile before disasm for any function analysis. Only fall back to disasm when decompile explicitly fails/errors or returns obvious artifacts. NEVER skip decompile and go straight to disasm for "convenience" or "safety".
  • Rename-as-you-go (HARD RULE) — each time you decompile a function and understand its functionality, you must immediately rename_symbol, "analyze everything first then name it all at once" is not allowed. Unnamed sub_XXXXX is technical debt and will make the caller's pseudocode unreadable.build_callgraph find leaves → decompile bottom-up → each time you understand a functionimmediately rename_symbol → re-decompile the caller.
  • Iterative loop — F5→annotate types→rename→F5 again→discover new offsets→verify struct→F5 again… compound effect
  • Constants are literal — when you see 0x6E in IDA, just write 110, don't interpret the physical meaning
  • disasm when decompile disagrees — when diff is 1-10, switch to disasm and verify instruction by instruction
  • Analysis log (HARD RULE) — during the reverse engineering process, you must maintain an analysis log. Each time you complete the reverse engineering of a function (decompile + rename + understand functionality), immediately append a log entry. At the end of the analysis (beforeclose_idb ) output the complete log summary. For the log format and rules, see "Analysis Log Specification" below.

1. Decompilation Strategy

Conditions for falling back to disasm: F5 errors/times out (>5000 instructions); pseudocode has obvious artifacts.

The Decompiler's Four Great Lies:

Lie Pseudocode manifestation Actual instruction
Constant folding result = x * 1718750 / 1000000 MUL X8, X8, #110; UDIV X8, X8, #64
Loses +1/-1 discount = bias * 110 / 64 after division there is still ADD X8, X8, #1
Type confusion int v10 = *(int *)(ctx + 0x1DC) LDR W8, [X0, #0x1DC](actually u32)
Hidden saturating arithmetic result = a - b SUBS X8, X9, X10; CSEL X8, XZR, X8, LO

Pseudocode quality iteration:

初始 F5 → sub_XXXXX(v42, v38)
  ↓ rename_symbol 叶子
二次 F5 → parse_header(v42, v38)
  ↓ apply_type
三次 F5 → parse_header(ctx, buf)
  ↓ declare_c_type + rename_local_variable
最终 F5 → ctx->msg_type 可读

2. Naming and Annotation

Timing: name as you understand, don't save it all for the end.

Naming conventions:

Prefix Meaning Example
check_/validate_ validation check_permissions
parse_/deserialize_ deserialization parse_config
compute_/calc_ computation compute_checksum
dispatch_ dispatch entry dispatch_command
init_/setup_ initialization init_context

Compound effect: after renaming a callee,re-decompile the caller — the pseudocode updates immediately.

Cross-session persistence: all annotations are saved in .i64. When resuming analysis, open_idb("<file>.i64"), not the original binary.

2.5 Analysis Log Specification (HARD RULE)

During reverse engineering you must record while analyzing. This is not optional—reverse engineering without a log is as if it wasn't done.

When to record

Trigger event Must record
Rename a function ✅ Function address, old name → new name, functionality summary
Discover a key struct ✅ Struct name, field layout, purpose
Identify an arithmetic formula ✅ Formula expression, constants involved, verification status
Discover an error code mapping ✅ error code → meaning
Identify dispatch/selector ✅ selector value → handler address → functionality
Complete an analysis phase ✅ Phase Summary

Log Format

## Analysis Log: <binary_name>

### Functions Reversed

| # | Address | Old Name | New Name | Purpose |
|---|---------|----------|----------|---------|
| 1 | 0x1234 | sub_1234 | parse_header | 从输入 buffer 解析消息头 |
| 2 | 0x5678 | sub_5678 | compute_checksum | CRC32 校验: crc = update(crc, data, len) |

### Structs Identified

| Struct | Size | Key Fields | Used By |
|--------|------|-----------|---------|
| MsgHeader | 0x40 | +0x00: magic (u32), +0x04: msg_type (u16), +0x08: payload_len (u32) | parse_header, dispatch_command |

### Key Findings

- Dispatch: tag byte at msg_type field, 0x01=init, 0x02=query, 0x03=update
- Error 0x1770: InvalidInput (length == 0)
- Checksum computed over payload only, header excluded

### Open Questions

- sub_9ABC: 被 compute_checksum 调用,疑似 lookup table init,待确认

Rules

  1. Record in real time — append a log entry immediately after rename, don't wait until the analysis is done to recall it
  2. Every rename must have a Purpose — one sentence explaining what the function does, not repeating the function name
  3. Formulas must be written out — record identified arithmetic formulas as mathematical expressions, annotate verification status (✅ verified / ⚠️ unverified)
  4. Open Questions must be listed — record functions/logic not fully understood here, don't pretend to understand everything
  5. At the end of the analysis, output — close_idb Before, output the complete analysis log as the deliverable of this reverse engineering session

3. Struct Reverse Engineering

From F5's *(ptr + 0xNNN) pattern, reconstruct the struct.

LDR → field type (ARM64):

LDR X8, [X0, #0x130]     → u64  (64-bit, X register)
LDR W8, [X0, #0x144]     → u32  (32-bit, W register)
LDRH W8, [X0, #0x168]    → u16
LDRB W8, [X0, #0x178]    → u8
LDP X8, X9, [X0, #0x130] → 两个 u64

decompile may show LDR W8 as int(signed), but it is actually u32. Must confirm with disasm.

Incremental verification loop:

1. decompile → 收集所有 ptr+offset 模式
2. disasm → 指令宽度确认字段类型
3. read_dword/read_qword → 读实际值 + 语义交叉验证
4. declare_c_type → 定义 struct
5. apply_type → 应用到参数
6. 重新 decompile → *(ptr+0x1DC) 变成 obj->field_name
7. get_xrefs_to_struct_field → 找所有访问该字段的代码
8. 发现新 offset → 回到步骤 2

Array detection: consecutive equally-spaced loads → [type; N]

4. Call Graph Navigation

Don't read large functions linearly. Dispatch function 10KB+, first build_callgraph target.

Leaf-first strategy:

1. build_callgraph(roots: [entry], max_depth: 3) → 找叶子
2. decompile 每个叶子 → 理解 + 立即 rename
3. decompile 父函数 → sub-call 显示有意义名字
4. 向上重复直到入口完全可读

Anti-pattern: decompile the entry first → see 48KB of sub_XXXXX(v42, v38) wall → completely unreadable.

5. Search and Locate

Byte search: watch out for little-endian (arm64/x86 = LE):

search_bytes(pattern: "6E 00 00 00")       # u32 0x6E (110),LE
search_bytes(pattern: "80 96 98 00")       # u32 0x989680 (10,000,000)

Immediate:search_text(kind: "imm", targets: ["110", "10000"]) Full pseudocode:search_pseudocode(pattern: "amount") Instruction sequence:search_instructions(patterns: ["MUL", "UDIV"])

6. Formula Extraction

Translation rules:

  1. Translate constants directly — 0x6E → 110
  2. Follow IDA strictly for arithmetic order — a * 110 / 64 + 1 ≠ (a * 110 + 64) / 64
  3. u128 precision — multiply two u64s first as u128
  4. Saturation arithmetic — use saturating_mul/saturating_sub
  5. decompile might lie — switch to disasm when diff is small

IDA pseudocode arithmetic patterns:

Pattern Meaning
__umulh(a, b) u128 multiplication high 64 bits
__multi3(a, b) Compiler u128 multiplication
__udivti3(hi, lo, denom) u128 division
SUBS + CSEL XZR saturating_sub

Convergence methodology:

diff range Typical root cause Fix strategy
> 100 Wrong scale or formula Rewrite from IDA constants
10-100 Wrong operator/operand Confirm MUL/DIV/ADD/SUB with disasm
1-10 off-by-one / saturation arithmetic Compare instruction by instruction with disasm
0 ✅ exact match Done

7. decompile_structured Arithmetic Chain Analysis

When the diff is > 0 after pseudocode translation and manual comparison is inefficient:

1. decompile_structured(address: <func>, max_depth: 20, include_types: true)
   → JSON AST

2. 遍历 AST 找算术子树:
   op: "mul"/"div"/"add"/"sub" → 核心算术
   op: "call" → __umulh / __udivti3 等 u128 操作
   op: "num" → 常量值(直接翻译)
   op: "var" → 变量引用

3. 从 AST 直接生成代码表达式,避免人工翻译错误
4. 与 disasm 交叉验证算术链的完整性

Applicable scenarios: Complex multi-step formulas (5+ steps), repeatedly non-zero diff, need to compare formulas across multiple functions.


Part 2: MCP Tool Quick Reference

The IDA MCP server provides 73 tools. High-frequency tool parameters and dynamic discovery methods → mcp-tool-reference.md


Part 2.5: CLI Direct Invocation (non-MCP)

ida-cli(~/.local/bin/ida-cli) — connect directly to the running ida-cli server via Unix Socket, no MCP protocol required. Suitable for scripts, automation, pipe composition.

Zero configuration

Server auto-management—automatically starts on the first CLI call, then reused for subsequent calls. No manual serve-http &。

Command discovery

ida-cli --help                        # 列出全部子命令
ida-cli rename-symbol --help          # 查看具体命令参数

Common examples

# --path 必传,自动 spawn worker + 打开文件
ida-cli --path <file> list-functions --limit 20
ida-cli --path <file> get-function-by-name --name main
ida-cli --path <file> decompile-function --address 0x1234
ida-cli --path <file> disassemble-function --name func_name --count 20
ida-cli --path <file> rename-symbol --address 0x1234 --new-name parse_pool
ida-cli --path <file> get-callees --address 0x1234
ida-cli --path <file> build-callgraph --roots 0x1234 --max-depth 3
ida-cli --path <file> search-pseudocode --pattern "amount" --limit 10
ida-cli --path <file> get-xrefs-to --address 0x1234
ida-cli --path <file> batch-decompile --addresses "0x1234,0x5678"

# 输出格式
ida-cli --json --path <file> list-functions --limit 5     # pretty JSON
ida-cli --compact --path <file> list-functions --limit 5   # 单行 JSON

# Server 管理(无需 --path)
ida-cli server-start                  # 手动启动(一般不需要,自动管理)
ida-cli server-status                 # 运行状态
ida-cli server-stop                   # 停止
ida-cli server-logs                   # tail -f 日志

# IDB 缓存管理
ida-cli idb-list                      # 列出所有缓存的 IDB
ida-cli idb-info --hash <hash>        # 查看详情
ida-cli idb-remove --hash <hash>      # 删除缓存(同时删除所有相关文件 .i64/.id0/.id1/.nam/.til/.imcp)

# Raw JSON-RPC(任意方法)
ida-cli raw '{"method":"list_functions","params":{"path":"<file>","limit":5}}'

# Pipe 模式(多命令流水线)
echo '{"method":"status"}
{"method":"decompile_function","params":{"path":"<file>","address":"0x1234"}}' | ida-cli --json pipe

Supported file types

Type Example Behavior
.i64 / .idb target.i64 Directly opens IDA database
Raw binary Mach-O / ELF / PE IDA auto-analysis

Concurrency safety

Multiple files concurrently: each file has an independent worker process, with no interference between them:

ida-cli --path binary_a.elf list-functions --limit 5 &
ida-cli --path binary_b.elf list-functions --limit 5 &
wait  # 并行打开 + 查询
ida-cli server-status   # worker_count: 2
ida-cli --path binary_a.elf close  # 只关 A,B 不受影响

Multiple clients concurrently on the same file: multiple CLIs can operate on the same program at the same time. The worker executes serially internally, and the router matches responses by request ID, so they will not get mixed up:

# 同时执行 3 个 rename —— 全部成功
ida-cli raw '{"method":"rename_symbol","params":{"path":"target.elf","current_name":"sub_1000","name":"main_loop","flags":0}}' &
ida-cli raw '{"method":"rename_symbol","params":{"path":"target.elf","current_name":"sub_2000","name":"parse_input","flags":0}}' &
ida-cli raw '{"method":"rename_symbol","params":{"path":"target.elf","current_name":"sub_3000","name":"handle_request","flags":0}}' &
wait

# 读写混合也安全:rename + decompile + disasm 同时发
ida-cli raw '{"method":"rename_symbol","params":{"path":"target.elf","current_name":"sub_4000","name":"send_response","flags":0}}' &
ida-cli --path target.elf decompile-function --address 0x5e8 &
ida-cli --path target.elf disassemble-function-at --address 0x700 --count 10 &
wait

Auto-start race prevention: when multiple CLIs cold-start at the same time, the file lock ensures only 1 server is started, and the rest wait until it is ready and then reuse it.

Server failure recovery

When a CLI request times out or returns a connection error, the server may be stuck. Handling steps:

# 1. 先尝试正常 shutdown
ida-cli server-stop

# 2. 如果 shutdown 也卡住(5s 无响应),强杀
pkill -9 -f "ida-cli"
rm -f ~/.ida/server.sock ~/.ida/server.pid ~/.ida/startup.lock

# 3. 下次 CLI 调用自动重新启动 server
ida-cli --path <file> list-functions --limit 5

Log troubleshooting:ida-cli server-logs or cat ~/.ida/logs/server.log。


Part 3: General Workflow

Workflow 1: Binary Orientation (first open, 30 seconds)

1. get_database_info()                                    → arch, file type, functions
2. list_segments()                                        → code/data/rodata layout
3. list_exports()                                         → 找入口
4. list_imports()                                         → 导入符号/API
5. list_functions(limit: 50)                              → first batch
6. build_callgraph(roots: [entry_addr], max_depth: 2)     → top-level structure

Workflow 2: Struct Reconstruction

1. decompile → 收集所有 ptr+offset 模式
2. disasm → 确认字段类型(LDR W vs X / MOV 宽度)
3. read_dword/read_qword → 读实际值,交叉验证语义
4. declare_c_type(decl: "struct Foo { ... }")
5. apply_type(addr: param, decl: "Foo *")
6. get_xrefs_to_struct_field(name: "Foo", member_name: "bar")
7. 重新 decompile → ptr+offset 变成 obj->field_name

Workflow 3: Arithmetic Chain Verification

1. decompile_function(addr: func)                         → high-level formula
2. get_pseudocode_at(addr: suspicious_line)               → zoom in
3. disassemble_function_at(addr: func)                    → full disasm
4. search_instructions(patterns: ["MUL", "UDIV"])         → arithmetic ops
5. search_instruction_operands(patterns: ["#0x6E"])       → constant usage
6. 逐指令验证:operand width, saturation, order
7. convert_number(inputs: ["0x989680"])                   → verify constants

Workflow 4: Error Code Mapping

1. search_text(kind: "imm", targets: ["0x0F00000000"])  → error constant loads
2. get_function_at_address(address: match) → 所属函数
3. get_pseudocode_at(address: match) → 条件上下文
4. set_comment(address: match, comment: "Error: InvalidAmount")
5. search_pseudocode(pattern: "return 0x") → 所有错误返回点
6. 复杂场景 → run_script 批量提取

Workflow 5: Factory/Dispatch Table Analysis

1. list_strings(query: "factory") / list_strings(query: "registry")
2. get_xrefs_to_string → 谁构建/读取这个表
3. decompile table builder → 找 table base address
4. scan_memory_table(base: addr, stride: 24, count: 50) → 读取 entries
5. 每个 fn_ptr → get_address_info → decompile → handler 逻辑

Workflow 6: Multi-Database Concurrent Analysis

1. Orchestrator: open_idb → db_handle + close_token(每个 IDB 独立 worker 进程)
2. Share db_handle with concurrent agents
3. Read ops (decompile, xrefs, callees) → 安全并行
   Write ops (rename_symbol, apply_type) → 串行避免冲突
4. 用 batch_decompile 减少往返
5. 完成后: close_idb(close_token)

支持同时调试:
6. 每个 IDB 可独立 dbg_load_debugger + dbg_start_process(各自的 debug server + 端口)
7. 所有 dbg_* 工具通过 db_handle 路由到对应 IDB 的调试会话
8. 两个调试会话完全隔离:断点、寄存器、内存、执行控制互不影响
9. 调试期间可对任意 IDB 执行反编译/反汇编(使用 rebase 后的地址)

Workflow 7: Batch Annotation

1. batch_rename(renames: [
     {"address": "0x1000", "name": "parse_header"},
     {"address": "0x2000", "name": "dispatch_command"},
     {"address": "0x3000", "name": "handle_request"}
   ])                                               → 批量重命名,返回每条成功/失败

2. set_function_prototype(address: "0x1000",
     prototype: "int64_t __fastcall parse_header(MsgHeader *hdr)")
                                                   → 设置函数原型

3. set_function_comment(address: "0x1000",
     comment: "Parses message header from input buffer")
                                                   → 设置函数注释

4. rename_stack_variable(func_address: "0x2000",
     var_name: "v1", new_name: "cmd_type")         → 重命名栈变量

5. set_stack_variable_type(func_address: "0x2000",
     var_name: "cmd_type", type_str: "uint32_t")   → 设置栈变量类型

6. create_enum(decl: "enum ErrorCode { InvalidInput = 0, BufferOverflow = 1 };")
                                                   → 创建错误码枚举

7. batch_decompile(addresses: ["0x1000", "0x2000", "0x3000"])
                                                   → 验证所有标注已生效

Workflow 8: Dynamic Debugging

8a: start_process (control target process lifecycle)

1. open_idb(path: "target.i64")                           → db_handle
2. dbg_load_debugger(debugger: "mac", is_remote: true)    → 加载远程调试器
3. dbg_start_process(path: "/path/to/target", args: "", timeout: 30)
   → 启动进程并自动附加,返回 pid + ip
4. dbg_add_breakpoint(address: "0x<main_addr>")           → IDB 地址,IDA 自动 rebase
5. dbg_continue(timeout: 10)                              → 运行到断点
   → event_code=5 (BREAKPOINT)
6. dbg_get_registers()                                    → 检查寄存器状态
7. decompile_function(address: "<rebased_ip>")            → 调试期间反编译正常工作
8. dbg_step_into / dbg_step_over / dbg_run_to             → 执行控制
9. dbg_read_memory(address: "<addr>", size: 64)           → 读运行时内存
10. dbg_exit_process(timeout: 10)                         → 终止进程 + 清理 debug server

8b: attach_process (attach to a running process)

1. 外部启动目标进程(或 HostDebugRunner)                 → 获取 PID
2. open_idb(path: "target.i64")
3. dbg_load_debugger(debugger: "mac", is_remote: true)
4. dbg_attach_process(pid: <PID>, timeout: 15)            → 附加
5. dbg_add_breakpoint / dbg_continue / ...                → 正常调试
6. dbg_detach_process(timeout: 10)                        → 分离,进程继续运行

Analysis capabilities during debugging

During debugging (while the process is suspended), all of the following operations are available:

  • decompile_function / decompile_structured — decompile ✅
  • disassemble_function / disassemble_function_at — disassemble ✅
  • list_functions / get_function_by_name — function query ✅
  • get_xrefs_to / get_xrefs_from — cross-references ✅
  • rename_symbol / set_comment — annotations ✅

Note: when using tools that operate by address, you need to use the rebased runtime address.


Part 4: Error recovery

decompile failure

→ get_pseudocode_at(address, end_address)   — 只看关键代码段
→ disassemble_function_at(address)          — 降级到反汇编
→ build_callgraph 找子函数 → 分别 decompile — 拆分分析

get_analysis_status returns auto_is_ok=false

→ run_auto_analysis(timeout_secs: 120)   — 强制等待分析完成
→ 或重复调用 get_analysis_status 直到 auto_is_ok=true
→ 重要:xrefs/decompile 在分析完成前可能返回不完整结果!

Tool timeout

→ run_script 默认 120s,最大 600s:run_script(code: "...", timeout_secs: 300)
→ batch_decompile 对大型数据库可能较慢,分批处理

Common Pitfalls

  1. Decompile the root function first → 48KB sub_XXXXX wall → use build_callgraph + leaf-first
  2. After rename, the caller was not decompiled again → the caller pseudocode still shows the old name
  3. Trust the exact arithmetic of decompile → when the diff is nonzero, you must verify with disasm
  4. search_bytes does not consider endianness → arm64/x86 are LE,0x6E search 6E 00 00 00
  5. Ignore the number of results from get_xrefs_to → high-frequency helpers have 50+ xrefs; combine with build_callgraph to filter
  6. Do not use read_dword/read_qword to verify the offset → what decompile displays as int64_t may actually be u32
  7. Open the raw binary instead of the .i64 to restore the session → all annotations are lost
  8. Query before analysis is complete → get_analysis_status confirm auto_is_ok=true

Part 5: Headless API

IDA Domain API (pip install ida-domain) + idalib + idapro headless execution → headless-api.md


Reference Files

⚠️ Load on demand, do not read everything. Each Reference file is 96-727 lines.Read only the one matching the current task. Most MCP reverse-engineering tasks only need mcp-tool-reference.md。

File Lines When to Load NOT needed for
mcp-tool-reference.md 300 When MCP tool parameters are uncertain(high frequency, default first choice) —
counterfactual-patch.md 96 Counterfactual Patching methodology Pure static analysis
headless-api.md 124 IDA Domain API / idapro headless execution MCP reversing (no need for headless)
idapython-cheatsheet.md 727 Write IDAPython scripts (run_script) When MCP tools are sufficient
ida-domain-api.md 680 Full reference for IDA Domain API (Python headless) MCP reversing
idalib-headless.md 385 Batch analysis with idalib C++ / idapro module MCP reversing
binary-analysis-patterns.md 312 Malware analysis, vulnerability discovery, firmware RE Simple static analysis
plugin-development.md 564 IDA plugin development Reverse engineering tasks