关于 re-source
re-source 是一项 idasql 技能,提供从编译后二进制递归恢复源代码层面理解的方法。它面向逆向工程人员,涵盖反编译、注释、调用关系追踪及逐步重建数据结构。
主要功能
- 01反编译并注释函数
- 02追踪调用者与被调用者
- 03关联结构字段访问
- 04创建并应用恢复出的类型
- 05持久化分析进度
适用场景
- 01理解陌生函数
- 02根据指针偏移恢复结构
- 03追踪函数的使用方式
- 04跨会话记录逆向分析进度
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ allthingsida/ idasql-skills --skill re-source安装后在 Claude Code 中输入/re-source确认已加载。
附带文件agents/openai.yamlreferences/struct-recovery-patterns.md
详细介绍
有关结构恢复模式,请参阅:references/struct-recovery-patterns.md
此技能介绍如何使用 idasql 从编译后的二进制文件中恢复源代码级理解的方法论。它将其他技能编排成系统化的工作流。
核心工作流:递归还原源码
1. 从一个函数开始
选择一个函数——入口点、被有趣字符串引用的函数,或已知函数的被调函数。
-- Decompile the target function
SELECT decompile(0x401000);
-- Or by name
SELECT decompile('DriverEntry');
2. 注释函数
使用 annotations 技能编辑反编译结果,使其更易读:
-- Rename local variables to meaningful names
UPDATE ctree_lvars SET name = 'driver_object' WHERE func_addr = 0x401000 AND idx = 0;
UPDATE ctree_lvars SET name = 'registry_path' WHERE func_addr = 0x401000 AND idx = 1;
-- Apply types to arguments/locals
UPDATE ctree_lvars SET type = 'PDRIVER_OBJECT'
WHERE func_addr = 0x401000 AND idx = 0;
-- Inspect pseudocode anchors before writing comments
SELECT line_num, addr, line, comment
FROM pseudocode
WHERE func_addr = 0x401000
ORDER BY line_num;
-- Add inline comments explaining logic
-- Example below uses a previously resolved writable anchor, not the function entry row.
UPDATE pseudocode SET comment = 'initialize dispatch table'
WHERE func_addr = 0x401000 AND addr = 0x401020;
-- Verify changes
SELECT decompile(0x401000, 1);
3. 设置函数注释
编写简洁的摘要,描述该函数的作用。这样一来,后续查询就可以索引该函数。
关于精确的触发语义(function summary / func-summary / add function comment 单数形式),请遵循 annotations 技能的 Function Summary 约定。
SELECT addr, name, comment, rpt_comment
FROM funcs
WHERE addr = 0x401000;
UPDATE funcs
SET rpt_comment = 'DriverEntry: initializes driver dispatch routines and device object'
WHERE addr = 0x401000;
4. 递归分析被调函数
跟进函数内部的调用。以相同方式注释每个被调函数,自底向上构建理解。
-- List callees to visit
SELECT callee_name, printf('0x%X', callee_addr) as addr
FROM disasm_calls WHERE func_addr = 0x401000;
-- Or map the full call subtree at once (BFS with depth tracking)
SELECT func_name, depth FROM call_graph
WHERE start = 0x401000 AND direction = 'down' AND max_depth = 5;
-- Decompile each callee
SELECT decompile(0x401050);
-- Annotate and recurse...
5. 递归分析调用者
跟进调用者,了解更大的整体:此函数是如何被使用的?
-- Who calls this function?
SELECT caller_name, printf('0x%X', caller_addr) as addr
FROM callers WHERE func_addr = 0x401000;
-- Or map ALL transitive callers at once
SELECT func_name, depth FROM call_graph
WHERE start = 0x401000 AND direction = 'up' AND max_depth = 10;
-- Find the shortest path from an entry point to this function
SELECT step, func_name FROM shortest_path
WHERE from_addr = (SELECT addr FROM funcs WHERE name = 'main')
AND to_addr = 0x401000 AND max_depth = 20;
-- Decompile callers to see usage context
SELECT decompile(0x400F00);
6. 结构恢复
这是最难的部分。反编译代码中经常会出现 *(DWORD *)(a1 + 0x10) 这样的强制类型转换——它们是结构体字段访问。
分步流程
a) 在单个函数中识别偏移模式:
-- Look at the decompiled code for cast patterns
SELECT decompile(0x401000);
-- Query ctree for pointer arithmetic (field accesses)
SELECT addr, op_name, num_value
FROM ctree WHERE func_addr = 0x401000
AND op_name IN ('cot_add', 'cot_idx')
AND num_value IS NOT NULL;
b) 跨函数关联——查找更多字段:
-- Find all callers that pass the same struct pointer
SELECT DISTINCT (SELECT name FROM funcs WHERE dc.func_addr >= addr AND dc.func_addr < end_addr LIMIT 1) as caller
FROM disasm_calls dc
WHERE dc.callee_addr = 0x401000;
-- Decompile each caller and look for more offset accesses
-- Each caller may reveal different fields of the same struct
c) 被调函数关联——通过被调函数揭示字段类型:
-- What does the function call with the struct pointer?
SELECT COALESCE(call_obj_name, call_helper_name) AS callee_name,
arg_idx, arg_op, arg_num_value
FROM ctree_call_args
WHERE func_addr = 0x401000
AND arg_var_name = 'a1';
-- If callee expects HANDLE, the field at that offset is a HANDLE
d) 逐步构建结构体:
-- Create the struct
INSERT INTO types (name, kind) VALUES ('MY_CONTEXT', 'struct');
-- Get the ordinal
SELECT ordinal FROM types WHERE name = 'MY_CONTEXT';
-- Add fields as you discover them (ordinal derived from the name above)
INSERT INTO types_members (type_ordinal, member_name, member_type)
SELECT ordinal, 'handle', 'void *' FROM types WHERE name = 'MY_CONTEXT';
INSERT INTO types_members (type_ordinal, member_name, member_type)
SELECT ordinal, 'buffer_ptr', 'void *' FROM types WHERE name = 'MY_CONTEXT';
INSERT INTO types_members (type_ordinal, member_name, member_type)
SELECT ordinal, 'buffer_size', 'unsigned int' FROM types WHERE name = 'MY_CONTEXT';
e) 应用恢复出的结构体:
-- Make this step self-contained (MY_CONTEXT was recovered in step d above)
SELECT parse_decls('struct MY_CONTEXT { void *handle; void *buffer_ptr; unsigned int buffer_size; };');
-- Apply to function prototype
UPDATE funcs SET prototype = 'int __fastcall process_context(MY_CONTEXT *ctx);'
WHERE addr = 0x401000;
-- Or apply to a local variable
UPDATE ctree_lvars SET type = 'MY_CONTEXT *'
WHERE func_addr = 0x401000 AND idx = 0;
-- Re-decompile to verify clean rendering
SELECT decompile(0x401000, 1);
7. 跟踪进度
使用 netnode_kv 在不同会话间保存进度:
-- Mark a function as fully annotated
INSERT INTO netnode_kv(key, value)
VALUES('re_source:0x401000', '{"status":"done","summary":"DriverEntry init"}');
-- Check progress
SELECT key, value FROM netnode_kv WHERE key LIKE 're_source:%';
高级源码还原模式(CTE)
传递式调用者发现
查找所有沿调用链传递某个结构体的函数——最终是谁提供了数据?
简单遍历优先使用
call_graph:SELECT func_name, depth FROM call_graph WHERE start = 0x401000 AND direction = 'up' AND max_depth = 5可替代下方的 CTE。只有在需要关联每一步的调用者上下文(例如偏移访问)时,才使用 CTE。
-- Recursive CTE: walk callers up to 5 levels
WITH RECURSIVE caller_chain AS (
-- Base: direct callers of the struct-consuming function
SELECT c.caller_func_addr AS func_addr,
c.caller_name AS func_name,
1 AS depth
FROM callers c
WHERE c.func_addr = 0x401000
UNION ALL
-- Recurse: callers of callers
SELECT c.caller_func_addr,
c.caller_name,
cc.depth + 1
FROM caller_chain cc
JOIN callers c ON c.func_addr = cc.func_addr
WHERE cc.depth < 5
)
SELECT DISTINCT func_name, printf('0x%X', func_addr) AS addr, MIN(depth) AS min_depth
FROM caller_chain
GROUP BY func_addr
ORDER BY min_depth;
汇总所有调用者中的偏移访问
收集每个访问该结构体的函数中的偏移模式,构建全面的结构体字段映射:
-- Collect field offset accesses from all functions that call process_context
WITH callers_of AS (
SELECT DISTINCT func_addr
FROM disasm_calls
WHERE callee_addr = 0x401000
),
offset_accesses AS (
SELECT func_addr,
(SELECT name FROM funcs WHERE func_addr >= addr AND func_addr < end_addr LIMIT 1) AS func_name,
num_value AS field_offset,
op_name
FROM ctree
WHERE func_addr IN (SELECT func_addr FROM callers_of)
AND op_name IN ('cot_add', 'cot_idx')
AND num_value IS NOT NULL
AND num_value BETWEEN 0 AND 0x1000
)
SELECT field_offset,
printf('0x%X', field_offset) AS hex_offset,
COUNT(DISTINCT func_addr) AS seen_in_funcs,
GROUP_CONCAT(DISTINCT func_name) AS functions
FROM offset_accesses
GROUP BY field_offset
ORDER BY field_offset;
查找结构体访问密集的函数(结构恢复候选函数)
包含最多 cot_add 偏移模式的函数,可能通过原始指针算术操作结构体:
-- Functions with most pointer arithmetic (struct field access candidates)
WITH offset_funcs AS (
SELECT func_addr,
COUNT(*) AS offset_accesses,
COUNT(DISTINCT num_value) AS unique_offsets
FROM ctree
WHERE func_addr IN (SELECT addr FROM funcs ORDER BY size DESC LIMIT 100)
AND op_name = 'cot_add'
AND num_value IS NOT NULL
AND num_value BETWEEN 1 AND 0x1000
GROUP BY func_addr
)
SELECT (SELECT name FROM funcs WHERE func_addr >= addr AND func_addr < end_addr LIMIT 1) AS name,
printf('0x%X', func_addr) AS addr,
offset_accesses,
unique_offsets
FROM offset_funcs
ORDER BY unique_offsets DESC
LIMIT 15;
将结构体字段偏移与已知类型大小交叉引用
将观察到的偏移与现有类型的字段大小匹配,以推测字段类型:
-- Compare observed offsets with known struct sizes
WITH observed AS (
SELECT DISTINCT num_value AS offset
FROM ctree
WHERE func_addr = 0x401000
AND op_name = 'cot_add'
AND num_value IS NOT NULL
AND num_value BETWEEN 0 AND 0x200
),
candidate_types AS (
SELECT t.name AS type_name, t.size AS type_size
FROM types t
WHERE t.is_struct = 1 AND t.size > 0
)
SELECT o.offset, printf('0x%X', o.offset) AS hex_offset,
ct.type_name, ct.type_size
FROM observed o
LEFT JOIN candidate_types ct ON ct.type_size = o.offset
ORDER BY o.offset;
核心原则
-
自底向上理解:从叶子被调函数开始,注释它们,再逐步向上分析调用者。每个已注释的被调函数都会让调用者更容易理解。
-
跨函数关联结构体:单个函数很少能揭示完整的结构体布局。检查同一函数的多个调用者和被调函数,以发现不同字段。
-
迭代优化:应用已掌握的信息,重新反编译,观察输出是否有所改善。随着发现更多信息,添加更多字段和类型。
-
验证每次编辑:遵循
annotations技能中的强制变更循环(读取 → 编辑 → 刷新 → 验证)。 -
定期保存:使用
SELECT save_database()保存工作。
相关技能
annotations— 编辑和注释工作流:如何重命名、重新设定类型、添加注释decompiler— 深入的反编译器参考:ctree、类型、parse_decls、联合体选择types— 类型系统机制:结构体、联合体和枚举的创建与操作xrefs— 调用者/被调函数遍历、call_graph/shortest_path表、string_refs视图disassembly— 结构恢复期间用于理解控制流的cfg_edgesstorage— 用于跨会话跟踪进度的 netnode_kv