mcpField
R
re-source

re-source

github.com/allthingsida开发4947 次安装3 周前更新
代码仓库

关于 re-source

re-source 是一项 idasql 技能,提供从编译后二进制递归恢复源代码层面理解的方法。它面向逆向工程人员,涵盖反编译、注释、调用关系追踪及逐步重建数据结构。

主要功能

  1. 01反编译并注释函数
  2. 02追踪调用者与被调用者
  3. 03关联结构字段访问
  4. 04创建并应用恢复出的类型
  5. 05持久化分析进度

适用场景

  1. 01理解陌生函数
  2. 02根据指针偏移恢复结构
  3. 03追踪函数的使用方式
  4. 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;

核心原则

  1. 自底向上理解:从叶子被调函数开始,注释它们,再逐步向上分析调用者。每个已注释的被调函数都会让调用者更容易理解。

  2. 跨函数关联结构体:单个函数很少能揭示完整的结构体布局。检查同一函数的多个调用者和被调函数,以发现不同字段。

  3. 迭代优化:应用已掌握的信息,重新反编译,观察输出是否有所改善。随着发现更多信息,添加更多字段和类型。

  4. 验证每次编辑:遵循 annotations 技能中的强制变更循环(读取 → 编辑 → 刷新 → 验证)。

  5. 定期保存:使用 SELECT save_database() 保存工作。


相关技能

  • annotations — 编辑和注释工作流:如何重命名、重新设定类型、添加注释
  • decompiler — 深入的反编译器参考:ctree、类型、parse_decls、联合体选择
  • types — 类型系统机制:结构体、联合体和枚举的创建与操作
  • xrefs — 调用者/被调函数遍历、call_graph / shortest_path 表、string_refs 视图
  • disassembly — 结构恢复期间用于理解控制流的 cfg_edges
  • storage — 用于跨会话跟踪进度的 netnode_kv

评论

支持 Markdown