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 を置き換えられます。各ステップで呼び出し元のコンテキスト(オフセットアクセスなど)を JOIN する必要がある場合に限り、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つの関数だけでは、構造体のレイアウト全体が明らかになることはほとんどありません。同じ関数の複数の呼び出し元や呼び出し先を調べ、異なるフィールドを見つけてください。
-
反復して精度を高める: 分かったことを適用して再度逆コンパイルし、出力が改善されたか確認します。新たなフィールドや型を見つけたら追加してください。
-
編集内容をすべて検証する:
annotationsスキルの必須変更ループ(読み取り → 編集 → 更新 → 検証)に従ってください。 -
定期的に保存する:
SELECT save_database()を使用して作業を保存します。
関連スキル
annotations— 編集・注釈のワークフロー:名前変更、型の変更、コメントの追加方法decompiler— 逆コンパイラーの詳細リファレンス:ctree、型、parse_decls、ユニオンの選択types— 型システムの仕組み:構造体/ユニオン/列挙型の作成と操作xrefs— 呼び出し元/呼び出し先の探索、call_graph/shortest_pathテーブル、string_refsビューdisassembly— 構造体の復元時に制御フローを理解するためのcfg_edgesstorage— セッションをまたいだ進捗管理のための netnode_kv