dexsql 文档 打开应用

直接调用与 override 相关引用

Markdown 源文件 · dexsql app

定位单个方法时使用完整 descriptor,包含参数类型和返回类型。仅按类名/方法名过滤会包含所有重载。查询 Runnable.run()V 的调用者:

SELECT DISTINCT file_id, method_idx, method
FROM method_calls
WHERE callee = 'Ljava/lang/Runnable;->run()V';

这会找到指令中引用该接口方法的调用,即使接口定义位于输入 APK 之外也可以。指令引用 Lcom/example/Task;->run()V 时,对应的是另一个目标。method_calls 每条调用指令占一行:count(*) 统计调用次数,上面的 DISTINCT 投影则返回按 (file_id, method_idx) 标识的调用者方法。

如果已经确认 Task.run()V 实现了该接口方法,可以一起查询选定目标。将 Task 替换为要检查的实现类:

SELECT DISTINCT file_id, method_idx, method
FROM method_calls
WHERE callee IN (
    'Ljava/lang/Runnable;->run()V',
    'Lcom/example/Task;->run()V'
);

发现候选方法时,将直接接口声明与 virtual 方法定义关联。join 保留 file_id,避免混用不同 DEX 的类索引:

SELECT DISTINCT m.descriptor
FROM methods m
JOIN class_interfaces i
  ON m.file_id = i.file_id AND m.class_idx = i.class_idx
WHERE i.interface_descriptor = 'Ljava/lang/Runnable;'
  AND m.name = 'run'
  AND m.param_count = 0
  AND m.return_descriptor = 'V'
  AND m.method_kind = 'virtual';

调用方可以从结果中选取候选方法,并在同一 dataset 上复用得到的 descriptor 集合。统计指向任一候选方法或接口方法本身的调用记录数:

WITH targets AS (
    SELECT 'Ljava/lang/Runnable;->run()V' AS descriptor
    UNION
    SELECT m.descriptor
    FROM methods m
    JOIN class_interfaces i
      ON m.file_id = i.file_id AND m.class_idx = i.class_idx
    WHERE i.interface_descriptor = 'Ljava/lang/Runnable;'
      AND m.name = 'run'
      AND m.param_count = 0
      AND m.return_descriptor = 'V'
      AND m.method_kind = 'virtual'
)
SELECT count(*) AS call_count
FROM method_calls
WHERE callee IN (SELECT descriptor FROM targets);

覆盖范围与查询成本:

  • 候选查询找到的是在接口列表中直接声明 Runnable 的类型自己定义的 run()V 方法。method_kind = 'virtual' 排除了 static/private 方法和仅引用行;它不要求存在具体方法体,抽象声明也可以是调用目标。
  • 间接接口实现、子接口以及从父类继承的方法,需要沿 classes.superclass_descriptor 和 class_interfaces 进一步遍历,再解析对应方法。调用方可以迭代查询这些关系,对已访问的类型 descriptor 去重,按签名和访问规则构造目标集合。有参数的方法要匹配参数类型及顺序,不能只比较 param_count。
  • 组合查询统计选定方法的引用数,不表示某次接口调用一定执行哪个实现。使用选定目标查询调用者时,如果需要保留不同调用目标,可同时投影 callee,得到去重后的调用者/目标对。
  • 按需展开目标。只查直接调用者时优先使用精确 descriptor,目标已知时使用选定的 IN 列表。全候选 CTE 在大型 APK 上可能生成很大的目标集合,查询成本显著增加;它不是持久缓存。将计数改为返回完整的 DISTINCT 调用者列表,还可能触发中间结果内存预算。大范围查找调用者时,可按实测确定的批量大小查询选定目标,再在调用方按 (file_id, method_idx) 合并去重。重复查询可在同一 dataset 内复用已发现的目标,高频使用前先测量组合查询成本。
  • 跨 DEX 匹配目标仍使用完整 descriptor。定义处的 method_idx 只属于该 DEX,其他 DEX 中的引用有自己的索引。这些示例使用现有 schema,仅在查询要求时展开目标。

Copyright (c) 2026 LSPosed Developers。本文档采用 MIT 许可证。