mcpField
X
xrefs

xrefs

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

关于 xrefs

xrefs 技能介绍如何用 SQL 查询 IDA 中的交叉引用、调用关系和结构体成员引用,面向二进制分析人员。它提供便捷视图以及调用图遍历和最短路径查询,并强调使用索引过滤来缩小范围。

主要功能

  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 xrefs

安装后在 Claude Code 中输入/xrefs确认已加载。

附带文件agents/openai.yaml

详细介绍


触发意图

用户提出以下问题时使用此技能:

  • “谁调用了它?”/“它调用了什么?”
  • “这个字符串/导入项在哪里被引用?”
  • “显示调用图依赖关系。”

路由至:

  • 关系分析前,先用 grep 按名称/模式查找候选实体
  • 用 analysis 获取更广泛的排查背景
  • 缩小图范围后,用 decompiler 进行语义解读
  • 用 disassembly 提供指令级调用点证据

首先执行(快速启动流程)

-- 1) Core relation volume
SELECT COUNT(*) AS xref_count FROM xrefs;

-- 2) Top imports (dependency hints)
SELECT module, COUNT(*) AS import_count
FROM imports
GROUP BY module
ORDER BY import_count DESC;

-- 3) Most called functions
SELECT printf('0x%X', to_addr) AS callee, COUNT(*) AS callers
FROM xrefs
WHERE is_code = 1
GROUP BY to_addr
ORDER BY callers DESC
LIMIT 20;

解读指导:

  • 在进行耗时的深入分析前,利用关系计数优先排查热点。
  • 优先使用索引筛选条件(to_addr/from_addr),以便快速响应。

故障与恢复

  • 全表扫描查询太慢:
    • 添加 to_addr = X 或 from_addr = X 约束。
  • 无法按名称解析目标:
    • 先通过 names 表或显式 EA 字面量解析/验证地址。
  • 结果稀疏:
    • 通过 imports、strings 或 disasm_calls 联接进行切换排查。
  • 查找对结构体/联合体 FIELD 的引用:
    • 使用 struct_member_xrefs(按 type_name/type_ordinal/member_id 筛选)。 不要对 instructions.disasm/instruction_operands.text 使用 LIKE 扫描字段名—— 这是此表旨在取代的缓慢且容易导致崩溃的模式。
  • 目标位于静态结构体或分发表中:
    • 在现代二进制文件中,字符串/数据目标通常由注册表或虚函数表引用,而非代码。因此,xrefs WHERE to_addr = X 可能只返回 .rdata 行。回溯到表头,然后针对表的地址重新查询 xrefs。
      -- 1) 查找包含目标的项的头部
      SELECT addr FROM heads
      WHERE addr <= 0x140027D50 ORDER BY addr DESC LIMIT 1;
      -- 2) 查询指向表头的 xrefs(代码使用方所指向的位置)
      SELECT * FROM xrefs WHERE to_addr = <head> AND is_code = 1;
      

移交模式

  1. 将 xrefs 交给 decompiler,分析最有可能的候选函数语义。
  2. 将 xrefs 交给 analysis,综合分析攻击活动层面的信息。
  3. 将 xrefs 交给 annotations,保存关系分析结果。

xrefs

交叉引用——理解代码关系最重要的表。

列类型描述
from_addrINT源地址(引用方)
to_addrINT目标地址(被引用对象)
typeINTXref 类型代码(不包含普通流代码,见下文)
is_codeINT1=代码 xref(调用/跳转),0=数据 xref
from_funcINT预先计算的所属函数地址(不属于任何函数时为 NULL)

语义:此接口仅公开实际引用——普通顺序流边(fl_F:每条指令“引用”下一条指令)在全表扫描以及 to_addr/from_addr/from_func 快速查询路径中均会被排除,与 IDA 的 Ctrl-X 视图及其他 xsql 工具一致。因此,计数反映的是调用/跳转/数据引用,而非指令相邻关系。

-- Who calls function at 0x401000?
SELECT printf('0x%X', from_addr) as caller FROM xrefs WHERE to_addr = 0x401000 AND is_code = 1;

-- What does function at 0x401000 reference?
SELECT printf('0x%X', to_addr) as target FROM xrefs WHERE from_addr >= 0x401000 AND from_addr < 0x401100;

struct_member_xrefs

指向特定结构体/联合体成员的交叉引用(Issue 11)。解析成员的 IDA tid,并直接枚举交叉引用——请使用此功能,而不要对 instructions.disasm / instruction_operands.text 执行 LIKE 扫描。必须按 type_ordinal、type_name 或 member_id 筛选(未筛选的查询会报错)。嵌入式成员会递归展开,并使用点分隔的 member_path。

ColumnTypeDescription
type_ordinal / type_nameINT/TEXT查询的(顶层)类型
member_indexINT叶成员在其直接父类型中的索引
member_name / member_pathTEXT叶成员名称 / 点分隔路径(payload.leaf.data)
member_offset / member_offset_bitsINT在查询类型中的累计偏移量
member_idINT成员 tid(也可用作筛选键)
xref_fromINT引用地址
xref_toINT成员 tid(== member_id)
xref_typeINT原始 IDA 交叉引用类型代码
xref_kindTEXTread / write / offset / call / jump / flow
function_addr / function_nameINT/TEXT所属函数(若无则为 NULL)
operand_indexINT引用该成员的操作数(尽力获取;可能为 NULL)
instruction_textTEXTxref_from 处的反汇编行(若不是指令则为 NULL)
access_offset / access_sizeINT访问子偏移量 / 宽度(尽力获取;可能为 NULL)
-- Who writes _EH3_EXCEPTION_REGISTRATION.TryLevel?
SELECT member_path, xref_kind, function_name, instruction_text
FROM struct_member_xrefs
WHERE type_name = '_EH3_EXCEPTION_REGISTRATION' AND member_name = 'TryLevel';

-- All member references for a type (incl. nested), grouped by member
SELECT member_path, count(*) AS refs FROM struct_member_xrefs
WHERE type_ordinal = (SELECT ordinal FROM types WHERE name = 'db_entry_t')
GROUP BY member_path;

imports

从外部库导入的函数。

ColumnTypeDescription
addrINT导入地址(IAT 条目)
nameTEXT导入名称
moduleTEXT模块/DLL 名称
ordinalINT导入序号
folder_pathTEXT可写的导入文件夹路径
full_pathTEXT完整导入 dirtree 路径
-- Imports from kernel32.dll
SELECT name FROM imports WHERE module LIKE '%kernel32%';

-- Organize network-related imports
UPDATE imports
SET folder_path = 'idasql/imports/network'
WHERE name LIKE '%socket%' OR name LIKE '%connect%' OR name LIKE '%send%';

Convenience Views

callers

每个函数的调用方。此视图会从 funcs 和 names 表中解析调用方名称。

ColumnTypeDescription
func_addrINT目标函数地址
caller_addrINT交叉引用源地址
caller_nameTEXT调用函数名称
caller_func_addrINT调用函数的起始地址(来自 from_func)

底层查询:

SELECT x.to_addr as func_addr, x.from_addr as caller_addr,
       COALESCE((SELECT name FROM names WHERE addr = x.from_func LIMIT 1), printf('sub_%X', x.from_func)) as caller_name,
       x.from_func as caller_func_addr
FROM xrefs x WHERE x.is_code = 1 AND x.from_func != 0
-- Who calls function at 0x401000?
SELECT caller_name, printf('0x%X', caller_addr) as from_addr
FROM callers WHERE func_addr = 0x401000;

-- Most called functions
SELECT printf('0x%X', func_addr) as addr, COUNT(*) as callers
FROM callers GROUP BY func_addr ORDER BY callers DESC LIMIT 10;
callees

每个函数调用的对象。与 callers 视图相反。使用 from_func 高效地按函数分组。

ColumnTypeDescription
func_addrINT调用函数地址(来自 from_func)
func_nameTEXT调用函数名称
callee_addrINT被调用地址
callee_nameTEXT被调用函数/符号名称
-- What does main call?
SELECT callee_name, printf('0x%X', callee_addr) as addr
FROM callees WHERE func_name LIKE '%main%';

-- Functions making most calls
SELECT func_name, COUNT(*) as call_count
FROM callees GROUP BY func_addr ORDER BY call_count DESC LIMIT 10;
string_refs

预先联接的字符串交叉引用视图,包含所属函数信息。

ColumnTypeDescription
string_addrINT字符串地址
string_valueTEXT字符串内容
string_lengthINT字符串长度
ref_addrINT引用指令的地址
func_addrINT所属函数地址
func_nameTEXT所属函数名称
-- Strings referenced by a specific function
SELECT string_value, func_name FROM string_refs WHERE func_addr = 0x401000;

-- Find functions referencing password-related strings
SELECT string_value, func_name FROM string_refs WHERE string_value LIKE '%password%';

-- Most referenced strings
SELECT string_value, COUNT(*) as ref_count
FROM string_refs GROUP BY string_addr ORDER BY ref_count DESC LIMIT 10;
data_refs

缓存的数据(非代码)交叉引用表,包含所属函数信息。 支持全程序聚合。

ColumnTypeDescription
from_addrINT引用的源地址
to_addrINT目标数据地址
from_func_addrINT所属函数地址
from_func_nameTEXT所属函数名称
ref_typeINT交叉引用类型代码
-- Data references from a specific function
SELECT * FROM data_refs WHERE from_func_addr = 0x401000;

-- Functions with most data references
SELECT from_func_name, COUNT(*) as data_ref_count
FROM data_refs GROUP BY from_func_addr ORDER BY data_ref_count DESC LIMIT 10;

call_graph

用于 BFS 调用图遍历的表值函数。使用 HIDDEN 参数控制遍历。

列类型描述
func_addrINT图中的函数地址
func_nameTEXT函数名称
depthINT距起点的 BFS 深度
parent_addrINT遍历中的父函数地址
startINTHIDDEN — 起始函数地址
directionTEXTHIDDEN — 'down'(被调用方)、'up'(调用方)或 'both'
max_depthINTHIDDEN — 最大遍历深度

始终为全部 3 个隐藏参数提供 WHERE 约束。

-- Forward call tree from main
SELECT func_name, depth FROM call_graph
WHERE start = (SELECT addr FROM funcs WHERE name = 'main')
  AND direction = 'down' AND max_depth = 5;

-- All transitive callers
SELECT func_name, depth FROM call_graph
WHERE start = 0x405000 AND direction = 'up' AND max_depth = 10;

-- Bidirectional exploration
SELECT func_name, depth FROM call_graph
WHERE start = 0x401000 AND direction = 'both' AND max_depth = 3;

-- Join with string_refs to find strings reachable from a function
SELECT DISTINCT sr.string_value, sr.func_name
FROM call_graph cg
JOIN string_refs sr ON sr.func_addr = cg.func_addr
WHERE cg.start = 0x401000 AND cg.direction = 'down' AND cg.max_depth = 3;

-- Imported APIs reachable from a function's call tree
SELECT DISTINCT i.module, i.name as api
FROM call_graph cg
JOIN disasm_calls dc ON dc.func_addr = cg.func_addr
JOIN imports i ON dc.callee_addr = i.addr
WHERE cg.start = 0x401000 AND cg.direction = 'down' AND cg.max_depth = 5
ORDER BY i.module, i.name;

性能:使用已访问集合的 BFS。O(可达函数数)。始终约束隐藏参数。 当目标是导入项时,使用此模式。


shortest_path

用于查找两个函数之间最短调用路径的表值函数。使用双向 BFS。

列类型描述
stepINT路径中的步数(0 = 源)
func_addrINT此步的函数地址
func_nameTEXT此步的函数名称
from_addrINTHIDDEN — 源函数地址
to_addrINTHIDDEN — 目标函数地址
max_depthINTHIDDEN — 最大搜索深度

始终为全部 3 个隐藏参数提供 WHERE 约束。 两个端点都必须解析为函数。来自 imports 的导入 API 地址不能作为 shortest_path 端点。

-- Find shortest call path between two functions
SELECT step, func_name FROM shortest_path
WHERE from_addr = (SELECT addr FROM funcs WHERE name = 'main')
  AND to_addr = (SELECT addr FROM funcs WHERE name = 'target_func')
  AND max_depth = 20;

-- Check reachability between two functions
SELECT COUNT(*) > 0 as reachable FROM shortest_path
WHERE from_addr = 0x401000 AND to_addr = 0x405000 AND max_depth = 20;

-- Annotate path steps with call count and string refs
SELECT sp.step, sp.func_name,
       (SELECT COUNT(*) FROM disasm_calls dc WHERE dc.func_addr = sp.func_addr) as calls_made,
       (SELECT COUNT(*) FROM string_refs sr WHERE sr.func_addr = sp.func_addr) as strings_used
FROM shortest_path sp
WHERE sp.from_addr = 0x401000 AND sp.to_addr = 0x405000 AND sp.max_depth = 20
ORDER BY sp.step;

性能:双向 BFS。当 b 为分支因子、d 为路径长度时,复杂度为 O(b^(d/2))。如果在 max_depth 范围内不存在路径,则返回空结果集。


表优先的交叉引用查询

-- Incoming references to an address
SELECT from_addr, to_addr, type, is_code, from_func
FROM xrefs
WHERE to_addr = 0x401000;

-- Exact outgoing references from an item address
SELECT from_addr, to_addr, type, is_code, from_func
FROM xrefs
WHERE from_addr = 0x401000;

-- Outgoing references from anywhere inside a function
SELECT from_addr, to_addr, type, is_code, from_func
FROM xrefs
WHERE from_func = 0x401000;

grep

在进行关系分析之前,使用 grep 解析内部符号、类型和成员。如果被调用方可能仅作为导入 API 存在,则使用 imports。 规范用法见 ../grep/SKILL.md。 有关规范架构和所有者映射,请参阅 ../connect/references/schema-catalog.md(grep)。

-- Resolve internal functions with grep
SELECT name, kind, addr
FROM grep
WHERE pattern = 'main%' AND kind = 'function'
ORDER BY name;

-- Resolve imported APIs with imports
SELECT module, name, addr
FROM imports
WHERE name LIKE 'CreateFile%'
ORDER BY module, name;

-- Then pivot into callers/callees/xrefs
SELECT caller_name, printf('0x%X', caller_addr) AS from_addr
FROM callers
WHERE func_addr = (
    SELECT addr
    FROM imports
    WHERE name = 'CreateFileW'
    ORDER BY name
    LIMIT 1
);

性能规则

约束下推

xrefs 表使用高效的 IDA SDK API 进行优化筛选:

筛选条件成本行为
to_addr = X0.5O(指向 X 的 xrefs 数量)— 快速,使用 IDA 的 xref 索引
from_addr = X0.5O(来自 X 的 xrefs 数量)— 快速,使用 IDA 的 xref 索引
from_func = X1.0O(X 的被调用方数量)— 使用 XrefsFromFuncIterator
对 to_addr / from_addr / from_func 没有相等条件筛选—回退到仅包含指向函数入口点的 xrefs 缓存 — 避免依赖它来完整覆盖导入项、数据和非函数

始终使用 to_addr、from_addr 或 from_func 筛选 xrefs。避免无约束扫描。

-- FAST: xrefs to a specific target
SELECT * FROM xrefs WHERE to_addr = 0x401000;

-- FAST: xrefs from a specific source
SELECT * FROM xrefs WHERE from_addr = 0x401000;

-- FAST: all xrefs originating from a function
SELECT * FROM xrefs WHERE from_func = 0x401000;

-- INCOMPLETE/avoid: unconstrained scan falls back to a function-entry cache
SELECT * FROM xrefs WHERE is_code = 1;

常见 Xref 模式

查找被调用次数最多的函数
SELECT f.name, COUNT(*) as caller_count
FROM funcs f
JOIN xrefs x ON f.addr = x.to_addr
WHERE x.is_code = 1
GROUP BY f.addr
ORDER BY caller_count DESC
LIMIT 10;
查找调用特定 API 的函数
SELECT DISTINCT (SELECT name FROM funcs WHERE from_addr >= addr AND from_addr < end_addr LIMIT 1) as caller
FROM xrefs
WHERE to_addr = (SELECT addr FROM imports WHERE name = 'CreateFileW');
字符串交叉引用分析
SELECT s.content, (SELECT name FROM funcs WHERE x.from_addr >= addr AND x.from_addr < end_addr LIMIT 1) as used_by
FROM strings s
JOIN xrefs x ON s.addr = x.to_addr
WHERE s.content LIKE '%password%';
导入依赖关系图
-- Which modules does each function depend on?
SELECT f.name as func_name, i.module, COUNT(*) as api_count
FROM funcs f
JOIN disasm_calls dc ON dc.func_addr = f.addr
JOIN imports i ON dc.callee_addr = i.addr
GROUP BY f.addr, i.module
ORDER BY f.name, api_count DESC;
数据段引用
-- Functions referencing data sections
SELECT
    f.name,
    s.name as segment,
    COUNT(*) as data_refs
FROM funcs f
JOIN xrefs x ON x.from_addr BETWEEN f.addr AND f.end_addr
JOIN segments s ON x.to_addr BETWEEN s.start_addr AND s.end_addr
WHERE s.class = 'DATA' AND x.is_code = 0
GROUP BY f.addr, s.name
ORDER BY data_refs DESC
LIMIT 20;

高级 Xref 模式(CTE 和递归查询)

优先使用 call_graph,而不是手动编写 CTE。 call_graph 表使用带已访问集合的 C++ BFS,能保证 BFS 深度正确,不会在菱形调用图中重复扩展,并支持提前终止。

之前(递归 CTE — 不跟踪已访问节点,在菱形图中会呈指数增长):

WITH RECURSIVE call_chain(root, current_func, depth) AS (
    SELECT 0x401000, callee_addr, 1
    FROM disasm_calls WHERE func_addr = 0x401000
    UNION ALL
    SELECT cc.root, dc.callee_addr, cc.depth + 1
    FROM call_chain cc
    JOIN disasm_calls dc ON dc.func_addr = cc.current_func
    WHERE cc.depth < 10
)
SELECT DISTINCT current_func, MIN(depth) FROM call_chain GROUP BY current_func;

之后(call_graph 表 — 使用已访问集合的 C++ BFS,深度正确):

SELECT func_addr, func_name, depth FROM call_graph
WHERE start = 0x401000 AND direction = 'down' AND max_depth = 10;
递归调用图 — 正向遍历

注意: 对于定向遍历,优先使用 call_graph 表,该表使用带已访问跟踪的 C++ BFS。仅当需要自定义联接逻辑时才使用递归 CTE(例如,在每一步按被调用方属性筛选)。

查找从起始函数可达的所有函数(最多深度 5):

-- Preferred: use call_graph table
SELECT func_name, depth FROM call_graph
WHERE start = (SELECT addr FROM funcs WHERE name = 'main')
  AND direction = 'down' AND max_depth = 5;

-- Manual CTE (when custom filtering is needed at each step):
WITH RECURSIVE cg AS (
    SELECT addr as func_addr, name, 0 as depth
    FROM funcs WHERE name = 'main'

    UNION ALL

    SELECT f.addr, f.name, cg.depth + 1
    FROM cg
    JOIN disasm_calls dc ON dc.func_addr = cg.func_addr
    JOIN funcs f ON f.addr = dc.callee_addr
    WHERE cg.depth < 5
      AND dc.callee_addr != 0
)
SELECT DISTINCT func_addr, name, MIN(depth) as min_depth
FROM cg
GROUP BY func_addr
ORDER BY min_depth, name;
递归调用图 — 反向(谁调用了此函数?)

注意: 对于定向遍历,优先使用 call_graph 表并设置 direction = 'up'。只有在需要自定义联接逻辑时,才使用递归 CTE。

向上追踪调用者,最多 5 层:

-- Preferred: use call_graph table
SELECT func_name, depth FROM call_graph
WHERE start = 0x401000 AND direction = 'up' AND max_depth = 5;

-- Manual CTE (when custom filtering is needed at each step):
WITH RECURSIVE callers_cte AS (
    SELECT DISTINCT dc.func_addr, 1 as depth
    FROM disasm_calls dc
    WHERE dc.callee_addr = 0x401000

    UNION ALL

    SELECT DISTINCT dc.func_addr, c.depth + 1
    FROM callers_cte c
    JOIN disasm_calls dc ON dc.callee_addr = c.func_addr
    WHERE c.depth < 5
)
SELECT (SELECT name FROM funcs WHERE func_addr >= addr AND func_addr < end_addr LIMIT 1) as caller, MIN(depth) as distance
FROM callers_cte
GROUP BY func_addr
ORDER BY distance, caller;
CTE:同时调用 malloc 并检查 NULL 的函数
WITH malloc_callers AS (
    SELECT DISTINCT func_addr
    FROM disasm_calls
    WHERE callee_name LIKE '%malloc%'
),
null_checkers AS (
    SELECT DISTINCT func_addr
    FROM ctree_v_comparisons
    WHERE rhs_num = 0 AND op_name = 'cot_eq'
)
SELECT f.name
FROM funcs f
JOIN malloc_callers m ON f.addr = m.func_addr
JOIN null_checkers n ON f.addr = n.func_addr;
CTE:分配内存但未释放的情况(可能存在内存泄漏)
WITH allocators AS (
    SELECT func_addr, COUNT(*) as alloc_count
    FROM disasm_calls
    WHERE callee_name LIKE '%alloc%' OR callee_name LIKE '%malloc%'
    GROUP BY func_addr
),
freers AS (
    SELECT func_addr, COUNT(*) as free_count
    FROM disasm_calls
    WHERE callee_name LIKE '%free%'
    GROUP BY func_addr
)
SELECT f.name,
       COALESCE(a.alloc_count, 0) as allocations,
       COALESCE(r.free_count, 0) as frees
FROM funcs f
LEFT JOIN allocators a ON f.addr = a.func_addr
LEFT JOIN freers r ON f.addr = r.func_addr
WHERE a.alloc_count > 0 AND COALESCE(r.free_count, 0) = 0
ORDER BY allocations DESC;
EXISTS:至少包含一个字符串引用的函数

对于存在性检查,这比 JOIN + DISTINCT 更高效:

SELECT f.name
FROM funcs f
WHERE EXISTS (
    SELECT 1 FROM xrefs x
    JOIN strings s ON x.to_addr = s.addr
    WHERE x.from_addr BETWEEN f.addr AND f.end_addr
);
EXISTS:叶子函数(没有向外的调用)
SELECT f.name, f.size
FROM funcs f
WHERE NOT EXISTS (
    SELECT 1 FROM disasm_calls dc
    WHERE dc.func_addr = f.addr
)
ORDER BY f.size DESC;

另请参阅

  • disassembly — 调用点上下文(disasm_at、disasm_calls、操作数级读取)。
  • data — to_addr 背后的字符串/数据目标;当 xrefs 返回的行仅来自 .rdata 时,这是规范的切入点。
  • decompiler — 调用代码逻辑,以及通过被调函数类型解析间接调用。

评论

支持 Markdown