关于 decompiler
用于在 Hex-Rays 中分析反编译函数的技能,涵盖伪代码、AST、局部变量和模式视图。它指导按函数范围查询,并将后续工作衔接到相关分析技能。
主要功能
- 01查看反编译伪代码
- 02查询 AST 和局部变量
- 03搜索调用、分支与循环
- 04编辑伪代码注释
- 05通过反汇编工作流恢复分析
适用场景
- 01理解函数行为
- 02查找调用或条件模式
- 03检查局部变量语义
- 04在汇编层面验证反编译结果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ allthingsida/ idasql-skills --skill decompiler安装后在 Claude Code 中输入/decompiler确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/ctree-manipulation.mddecompiler-views.mddecompiler-workflows.md详细介绍
触发意图
用户提出以下请求时使用此技能:
- “反编译此函数”
- 理解伪代码或进行 AST 级分析
- 理解反编译形式中的局部变量语义
- 以反编译器为中心的模式挖掘(返回值/调用/条件)
转交给:
annotations:解释后添加持久化注释/重命名types:构建和应用结构体/枚举/类型disassembly:反编译器不可用或能力不足时使用
首先执行(预热顺序)
-- 1) Capability/profile probe
SELECT * FROM pragma_table_list WHERE name IN ('pseudocode', 'ctree', 'ctree_lvars');
-- 2) Pick one concrete function target
SELECT name, printf('0x%X', addr) AS addr, size
FROM funcs
ORDER BY size DESC
LIMIT 10;
-- 3) View decompiled text via primary read surface
SELECT decompile(0x401000);
解读指南:
decompile(addr)是主要的显示界面。pseudocode/ctree*是结构化查询/编辑界面。
全局约束提醒(重要)
始终按函数限制反编译器表:
WHERE func_addr = 0x...
否则,反编译器表可能会反编译每个函数,导致速度极慢。
故障与恢复
- 没有 Hex-Rays/反编译器表不可用:
- 回退到
disassembly+xrefs工作流。
- 回退到
- 行为空/不完整:
- 确认目标
func_addr存在,并刷新反编译缓存(在支持的情况下使用decompile(addr, 1))。
- 确认目标
- 修改未显示:
- 执行强制修改循环(读取 -> 编辑 -> 刷新 -> 验证)。
移交模式
decompiler->types,用于局部类型注入和更丰富的声明。decompiler->annotations,用于持久化说明和命名。decompiler->disassembly,用于操作码级验证。
反编译器表(需要 Hex-Rays)
重要: 始终按 func_addr 筛选。不加约束的话,这些表会反编译每个函数——速度极慢!
pseudocode
pseudocode 表是逐行的结构化伪代码,并支持写入注释。使用 decompile(addr) 查看伪代码;此表仅用于精细编辑(注释)或结构化查询。
| 列 | 类型 | 可写 | 描述 |
|---|---|---|---|
func_addr | INT | 否 | 函数地址 |
line_num | INT | 否 | 行号 |
line | TEXT | 否 | 伪代码文本 |
addr | INT | 否 | 对应的汇编地址(来自 COLOR_ADDR 锚点) |
comment | TEXT | 是 | 此地址处的反编译器注释 |
comment_placement | TEXT | 是 | 注释位置:semi(行内,默认)、block1(行上方) |
筛选行为:
WHERE func_addr = X:性能最佳;仅遍历一个函数的伪代码。WHERE addr = X:仅反编译包含该地址的函数,并返回该 EA 对应的行。WHERE line_num = N:扫描函数并返回该行索引对应的行;仅在需要跨函数对齐行时使用。
注释位置: semi(; 后)、block1(上方单独一行)、block2、curly1、curly2、brace1、brace2、colon、case、else、do、asm、try。无法识别的位置字符串会被静默转换为 semi。
-- VIEWING: Use decompile() function, NOT the pseudocode table
SELECT decompile(0x401000);
-- COMMENTING: Use pseudocode table to add/edit/delete comments
UPDATE pseudocode SET comment_placement = 'semi',
comment = 'buffer overflow here'
WHERE func_addr = 0x401000 AND addr = 0x401020;
-- Add block comment (appears on own line above the statement)
UPDATE pseudocode SET comment_placement = 'block1', comment = 'vulnerable call'
WHERE func_addr = 0x401000 AND addr = 0x401020;
-- Delete comments at a resolved unique anchor
UPDATE pseudocode SET comment = NULL
WHERE func_addr = 0x401000 AND addr = 0x401020;
真正的函数注释不属于 pseudocode:
- 对于常规函数注释,使用
UPDATE funcs SET comment = ... WHERE addr = ... - 对于可重复函数注释,使用
UPDATE funcs SET rpt_comment = ... WHERE addr = ...
pseudocode_orphan_comments
已持久化的 Hex-Rays 注释,但不再附着于当前有效函数的反编译输出。可使用此表检查或删除过期注释。
| 列 | 类型 | 可写 | 描述 |
|---|---|---|---|
func_addr | INT | 否 | 函数地址 |
func_name | TEXT | 否 | 用于分类处理的当前函数名称 |
addr | INT | 否 | 存储的孤立注释 EA |
comment_placement | TEXT | 否 | 存储的 treeloc_t.itp 位置 |
orphan_comment | TEXT | 仅可删除 | 存储的孤立注释文本 |
规则:
UPDATE ... SET orphan_comment = NULL或''会删除该孤立注释。- 任何非空写入都会被拒绝。
pseudocode_v_orphan_comment_groups
分组的只读孤立注释分类处理界面。每个包含孤立注释的函数对应一行。
列:func_addr、func_name、orphan_count、orphan_comments_json
注释锚点解析(关键)
在编写标题样式的反编译器注释之前,请先使用此方法。
规则:
- 不要假设
addr == func_addr。 - 显示的第一行伪代码通常有
addr = 0,不是合适的写入目标。 - 一个
addr可能对应多行({、语句、});优先选择唯一的非大括号锚点。 - 对于真正的函数注释,请更新
funcs.comment/funcs.rpt_comment,而不是pseudocode。
-- Resolve the first attachable non-brace row near function start
SELECT line_num, addr, line
FROM pseudocode
WHERE func_addr = 0x401000
AND addr != 0
AND TRIM(line) NOT IN ('{', '}')
AND addr IN (
SELECT addr
FROM pseudocode
WHERE func_addr = 0x401000 AND addr != 0
GROUP BY addr
HAVING COUNT(*) = 1
)
ORDER BY line_num
LIMIT 1;
-- Write a heading-style summary using the resolved addr
UPDATE pseudocode
SET comment_placement = 'block1',
comment = 'One-paragraph summary of the function.'
WHERE func_addr = 0x401000
AND addr = (
SELECT addr
FROM pseudocode
WHERE func_addr = 0x401000
AND addr != 0
AND TRIM(line) NOT IN ('{', '}')
AND addr IN (
SELECT addr
FROM pseudocode
WHERE func_addr = 0x401000 AND addr != 0
GROUP BY addr
HAVING COUNT(*) = 1
)
ORDER BY line_num
LIMIT 1
);
ctree
反编译代码的完整抽象语法树。
| 列 | 类型 | 描述 |
|---|---|---|
func_addr | INT | 函数地址 |
item_id | INT | 唯一节点 ID |
is_expr | INT | 1=表达式,0=语句 |
op | INT | 原始数值操作码 |
op_name | TEXT | 节点类型(cot_call、cit_if 等) |
addr | INT | 二进制文件中的地址 |
parent_id | INT | 父节点 ID |
depth | INT | 树深度 |
x_id, y_id, z_id | INT | 子节点 ID |
cond_id | INT | 条件子节点 ID(if/while/for) |
then_id | INT | then 分支子节点 ID(if) |
else_id | INT | else 分支子节点 ID(if) |
body_id | INT | 主体子节点 ID(循环) |
init_id | INT | 初始化子节点 ID(for) |
step_id | INT | 步骤子节点 ID(for) |
var_idx | INT | 局部变量索引 |
obj_addr | INT | 目标地址 |
num_value | INT | 数值字面量 |
str_value | TEXT | 字符串字面量 |
helper_name | TEXT | 辅助函数名称(cot_helper) |
member_offset | INT | 成员偏移量(cot_memptr/cot_memref) |
var_name | TEXT | 变量名称 |
var_is_stk | INT | 1=变量是栈变量 |
var_is_reg | INT | 1=变量是寄存器变量 |
var_is_arg | INT | 1=变量是函数参数 |
obj_name | TEXT | 符号名称 |
label_num | INT | 节点定义标签时的标签编号 |
goto_label_num | INT | cit_goto 节点的目标标签编号 |
ctree_lvars
反编译得到的局部变量。
| 列 | 类型 | 描述 |
|---|---|---|
func_addr | INT | 函数地址 |
idx | INT | 变量索引 |
name | TEXT | 变量名称 |
type | TEXT | 类型字符串 |
comment | TEXT | 显示在声明旁的局部变量注释 |
size | INT | 字节数 |
is_arg | INT | 1=函数参数 |
is_result | INT | 1=保存函数结果 |
is_stk_var | INT | 1=栈变量 |
is_reg_var | INT | 1=寄存器变量 |
stkoff | INT | 栈偏移量 |
mreg | INT | 微码寄存器编号(寄存器变量) |
修改指南:
- 优先使用基于
idx的更新,以确保写入具有确定性。 type接受标量、指针和数组类型——例如,对于宽字符栈字符串/固定缓冲区,可设置为SET type = 'WCHAR[6]'或'_BYTE[392]'(类型必须适合局部变量的栈槽)。comment更新会映射到 Hex-Rays 局部变量注释(lv.cmt),并显示在decompile(...)输出中。
ctree_labels
反编译器控制流标签。支持更新(name),并映射 cfunc_t 上的标签功能。
| 列 | 类型 | RW | 描述 |
|---|---|---|---|
func_addr | INT | R | 函数地址 |
label_num | INT | R | 标签编号(LABEL_<n>) |
name | TEXT | RW | 当前标签名称 |
item_id | INT | R | 此标签对应的 ctree 项目 ID |
item_addr | INT | R | 带标签的 ctree 项目的地址 |
is_user_defined | INT | R | 若名称不同于默认的 LABEL_<n>,则为 1 |
ctree_call_args
扁平化的调用参数,便于查询。
| 列 | 类型 | 描述 |
|---|---|---|
func_addr | INT | 函数地址 |
call_item_id | INT | 调用节点 ID |
call_addr | INT | 调用点 EA |
call_obj_name | TEXT | 被调用对象名称 |
call_helper_name | TEXT | 被调用辅助函数名称 |
arg_idx | INT | 参数索引(从 0 开始) |
arg_item_id | INT | 参数表达式项 ID |
arg_op | TEXT | 参数类型 |
arg_var_idx | INT | 参数为变量时的局部变量索引 |
arg_var_name | TEXT | 适用时的变量名称 |
arg_var_is_stk | INT | 1=参数变量是栈变量 |
arg_var_is_arg | INT | 1=参数变量本身是函数参数 |
arg_obj_addr | INT | 参数为全局对象时的对象地址 |
arg_obj_name | TEXT | 参数为全局对象时的对象名称 |
arg_num_value | INT | 数值 |
arg_str_value | TEXT | 字符串值 |
反编译器视图
针对常见模式预构建的视图(始终按 func_addr 筛选):
| 视图 | 用途 |
|---|---|
ctree_v_calls | 包含被调用方信息的函数调用 |
ctree_v_indirect_calls | 用于调用点类型判定的间接/动态调用点 |
ctree_v_loops | for/while/do 循环 |
ctree_v_ifs | if 语句 |
ctree_v_comparisons | 包含操作数的比较 |
ctree_v_signed_ops | 有符号比较/算术运算 |
ctree_v_assignments | 包含操作数的赋值 |
ctree_v_derefs | 指针解引用 |
ctree_v_returns | 包含值详情的 return 语句 |
ctree_v_calls_in_loops | 循环内的调用(递归) |
ctree_v_calls_in_ifs | if 分支内的调用(递归) |
ctree_v_leaf_funcs | 没有出站调用的函数 |
ctree_v_call_chains | 深度最多为 10 的调用链路径 |
类型表和视图
有关 types、types_members、types_enum_values、types_func_args 架构、类型视图和类型 CRUD 示例,请参阅 types 技能。
SQL 函数 — 反编译
何时使用 decompile(),何时查询 pseudocode 表:
- 读取/显示伪代码 -> 始终从
SELECT decompile(addr)开始。它会返回带逐行前缀的完整函数文本块。 - 局部变量声明提示 -> 声明行包含简洁的局部变量索引提示(
[lv:N]),因此重命名操作可以安全地以WHERE func_addr = ... AND idx = N为条件,执行UPDATE ctree_lvars ...。 - 编辑后需要获取最新输出 -> 使用
SELECT decompile(addr, 1)强制重新反编译。 - 需要按结构访问行或执行注释的增删改查 -> 查询/更新
pseudocode表。
| 函数 | 描述 |
|---|---|
decompile(addr) | 首选 -- 带行前缀的完整伪代码 |
decompile(addr, 1) | 输出相同,但会强制重新反编译 |
call_arg_addrs(call_addr) | 以 JSON 格式读取持久化的实参加载器地址 |
set_union_selection(func_addr, addr, path) | 在 EA 处设置/清除联合体选择路径 |
set_union_selection_item(func_addr, item_id, path) | 按 ctree.item_id 设置/清除联合体选择路径 |
set_union_selection_addr_arg(func_addr, addr, arg_idx, path[, callee]) | 首选的调用实参定位辅助函数 |
call_arg_item(func_addr, addr, arg_idx[, callee]) | 将调用实参坐标解析为显式的 arg_item_id |
ctree_item_at(func_addr, addr[, op_name[, nth]]) | 将通用表达式坐标解析为显式的 ctree.item_id |
set_union_selection_addr_expr(func_addr, addr, path[, op_name[, nth]]) | 通过通用表达式坐标设置/清除联合体选择 |
get_union_selection(func_addr, addr) | 在 EA 处读取联合体选择路径 JSON |
get_union_selection_item(func_addr, item_id) | 按 ctree.item_id 读取联合体选择路径 JSON |
get_union_selection_addr_arg(func_addr, addr, arg_idx[, callee]) | 通过调用实参坐标读取联合体选择 JSON |
get_union_selection_addr_expr(func_addr, addr[, op_name[, nth]]) | 通过通用表达式坐标读取联合体选择 JSON |
set_numform(func_addr, addr, opnum, spec) | 直接通过 EA 和操作数索引设置/清除 numform |
get_numform(func_addr, addr, opnum) | 直接通过 EA 和操作数索引读取 numform JSON |
set_numform_item(func_addr, item_id, opnum, spec) | 通过显式的 ctree item id 设置/清除 numform |
get_numform_item(func_addr, item_id, opnum) | 通过显式的 ctree item id 读取 numform JSON |
set_numform_addr_arg(func_addr, addr, arg_idx, opnum, spec[, callee]) | 通过调用实参坐标设置/清除 numform |
get_numform_addr_arg(func_addr, addr, arg_idx, opnum[, callee]) | 通过调用实参坐标读取 numform JSON |
set_numform_addr_expr(func_addr, addr, opnum, spec[, op_name[, nth]]) | 通过通用表达式坐标设置/清除 numform |
get_numform_addr_expr(func_addr, addr, opnum[, op_name[, nth]]) | 通过通用表达式坐标读取 numform JSON |
定位指引:
- 对于重复的被调函数和调用点实参,使用
*_addr_arg辅助函数。 - 对于非调用表达式以及赋值侧的结构体/联合体填充存储,使用
ctree_item_at(..., op_name, nth)搭配*_addr_expr辅助函数。
SQL 函数 — 修改
有关 applied_types、parse_decls() 以及通过 names/funcs 写入名称的信息,请参阅 types 技能。
函数元数据的首选 SQL 写入接口:
UPDATE funcs SET name = '...', prototype = '...', comment = '...', rpt_comment = '...' WHERE addr = ...prototype对应applied_types的行为,并会使反编译器缓存失效。comment/rpt_comment对应get_func_cmt()/set_func_cmt()。
性能规则
| 表 | 架构 | 关键约束 | 备注 |
|---|---|---|---|
pseudocode | 缓存 | func_addr | 按函数惰性缓存,查询结束后释放 |
pseudocode_orphan_comments | 缓存 | func_addr | 查询范围内的孤立行;仅支持删除写入 |
pseudocode_v_orphan_comment_groups | 缓存 | func_addr | 查询范围内的分组孤立项分类;从较宽范围开始并使用 LIMIT |
ctree | 生成器 | func_addr | 惰性流式处理,不会物化完整结果,并遵循 LIMIT |
ctree_lvars | 缓存 | func_addr | 按函数惰性缓存,查询结束后释放 |
ctree_call_args | 生成器 | func_addr | 惰性流式处理,并遵循 LIMIT |
关键规则:
- **所有反编译器表都需要
func_addr约束。**如果没有约束,就会反编译每个函数。 - 生成器表(
ctree、ctree_call_args)会惰性流式返回行,并在达到 LIMIT 时停止。 - 反编译器视图(
ctree_v_calls、ctree_v_indirect_calls、ctree_v_loops等)会继承func_addr约束——务必进行筛选。 - Hex-Rays cfunc 缓存:
decompile(addr)会在内部缓存。decompile(addr, 1)会强制完整重新反编译——仅在需要查看变更效果时使用。
成本模型:
decompile(addr) -> ~50-200ms first call, ~0ms cached
decompile(addr, 1) -> ~50-200ms always (forces re-decompile)
ctree WHERE func_addr=X -> one decompilation + streaming rows
ctree (no constraint) -> one decompilation per row in funcs
其他资源
- 有关详细工作流(能力探测、变更循环、调用点类型处理、局部类型设定、回退模式和完整示例):references/decompiler-workflows.md
- 有关详细视图架构(ctree_v_indirect_calls、ctree_v_returns):references/decompiler-views.md
- 有关 ctree 节点类型、操作模式和高级 CTE:references/ctree-manipulation.md
另请参阅
data— 反编译代码引用的原始字节和字符串内容(用于逐字节读取和有界窗口读取的bytes表,通过WHERE start_addr = X AND n = N查询;使用hex(blob_concat(value))输出十六进制内容)。disassembly— 同一函数的指令级事实依据(disasm_func、instructions、disasm_calls)。xrefs— 反编译函数的调用方和被调用方;从伪代码切换到调用图。types— 已应用的原型决定 ctree 结构;通过applied_types/funcs.prototype重新指定类型,以清理类型转换和间接调用。