首页
/ Ghidra BSim Overview Query 实战:函数匹配数、Self-Significance 与 Vector Hash 判重

Ghidra BSim Overview Query 实战:函数匹配数、Self-Significance 与 Vector Hash 判重

2026-09-04 23:44:55作者:俞予舒Fleming

本文基于 Ghidra BSim(行为相似性)教程中的 Overview Queries 章节展开,讲解如何使用 BSim Overview Query 统计可执行文件中每个函数在 BSim 数据库中的匹配数量,并结合结果表中的 Hit Count、Self Significance 和可选的 Vector Hash 列完成三个典型实战练习:识别高重复度函数、基于命中数筛选发起精准搜索、以及用向量哈希判定两个函数是否拥有相同的 BSim 签名。读完后你可以独立完成从执行 Overview 查询、解读结果表到发起后续相似函数搜索的完整工作流。

Overview Query 是什么

Overview Query(概览查询) 会向 BSim 数据库查询:当前可执行文件中的每个函数各有多少个匹配项(match count)。它只返回每个函数的匹配数量,不返回具体的匹配函数本身。与普通的相似函数搜索(Search Functions)相比,Overview Query 有以下特性:

  • 可以设置 Similarity(相似度)Confidence(置信度) 阈值;
  • 没有 “Matches per Function(每个函数最大匹配数)” 上限;
  • 不能 施加 BSim 过滤器(filter)。

发起方式:在 Ghidra 的 Code Browser 中执行菜单操作 BSim -> Perform Overview...

源码视角:Overview 任务如何构造

Perform Overview... 菜单项由 BSimSearchPlugin.java 注册:

new ActionBuilder("BSim Overview", getName()).menuPath("BSim", "Perform Overview...")
        .helpLocation(new HelpLocation(getName(), "BSim_Overview_Dialog"))
        .enabledWhen(c -> currentProgram != null)
        .onAction(c -> showOverviewDialog())
        .buildAndInstall(tool);

点击后弹出 BSim Overview 对话框,选择一个已配置的 BSim 服务器与数据库,随后插件会创建一个后台 OverviewTask。从 OverviewTask 的构造与执行逻辑 可以看到它与文档描述完全对应:

overviewInfo = new SFOverviewInfo(functions);
overviewInfo.setSimilarityThreshold(settings.getSimilarity());
overviewInfo.setSignificanceThreshold(settings.getConfidence());
...
queryService.overviewSimilarFunctions(overviewInfo, this, monitor);

对比同一文件中的 SearchTask(普通搜索),可以发现 Overview 路径不调用 setMaximumResults(...),也没有复制 BSimFilterSet 过滤器——这正是“无每函数匹配数上限、无过滤器”这一行为限制的实现依据。

另外,被统计的对象是当前程序中全部非 stub 函数。插件通过 getOverviewFunctions()functionManager.getFunctionsNoStubs(true) 遍历得到,因此结果表的行数通常等于程序中的非 stub 函数数。

结果表:列含义与源码实现

Overview 结果在独立的 “BSim Function Overview” 窗口中展示(窗口分组为 bsim.overview,见 BSimOverviewProvider.java),默认列由表模型 BSimOverviewModel 定义:

列名 说明 源码取值
Address 函数入口地址,支持点击导航 BSimOverviewModel.getAddress()
Function Name 函数名 FuncNameColumn
Hit Count 该函数在数据库中的匹配数量 simvec.getTotalCount()
Self Significance 以该函数为查询时可能得到的最高显著性得分 vectorFactory.getSelfSignificance(...)
Vector Hash 特征向量的唯一哈希(默认隐藏的可选列) lshVector.calcUniqueHash(),按 %016X 十六进制显示

几个关键点来自 BSimOverviewRowObject 的构造逻辑:

  • Hit Count 取自数据库返回的 SimilarityVectorResult.getTotalCount()
  • 源码注释明确说明 selfsignif 是 “Maximum significance score a query with this function could return”(以该函数作为查询时,查询结果所能达到的最大显著性分数)。换句话说,一个函数的 Self Significance 就是它“自我匹配”的天花板:如果连它自己都只能得到较低的显著性得分,那么用它去查询时所有结果的 Confidence 都不会太高;
  • Vector Hash 列在 列描述符 中通过 addHiddenColumn(new VectorHashColumn()) 注册为隐藏列,因此教程中要求“在 Overview 表中启用 Vector Hash 列”——即通过 Ghidra 表格的列选择器把它勾选出来。

下面是 Overview 查询结果窗口的示意:

BSim Overview 查询结果窗口,显示函数名、Hit Count 与 Self Significance 列

练习一:Hit Count 与 Self-Significance 的关系

目标:用默认阈值对 postgres 可执行文件执行 Overview 查询,观察命中数与自显著性之间的负相关。

操作步骤:

  1. 以默认查询阈值对 postgres 执行 Overview 查询(BSim -> Perform Overview...),结果如上图所示。
  2. 点击表头,按 Hit Count 列升序 排序。通常命中数(Hit Count)最大的那些函数,其 Self Significance 会很低。请对照上表验证这一点。
  3. 思考题:命中数最高的那些函数为什么会有这么多匹配?
    • 参考答案:它们全都是 PostgreSQL 的错误报告函数(error-reporting functions)。这些函数的函数体非常相似,因此具有完全相同的 BSim 签名——数据库中每出现一份这样的函数副本,就为所有这些副本各贡献一次命中。

这一现象正是“高命中 ⇒ 低 Self Significance”直觉的典型例证:当某个函数的签名与大量几乎相同的函数共享时,它作为查询向量时的显著性得分上限就会被摊薄。理解这一规律后,Overview 表就成了一个“噪声探测器”——命中数极高的行往往对应模板化/重复生成的代码,直接拿它们去查询价值有限。

练习二:利用命中数筛选后发起搜索

Overview 表虽然只给数量,但 Hit Count 列可以直接用作排除策略:把命中数过多的“噪声函数”排除在查询范围之外,只对命中数低(即更独特、更可能匹配到有意义目标)的函数发起真正的相似函数搜索。

操作步骤:

  1. 在 Overview 表中,选中所有 Hit Count ≤ 2 的函数行(可用表格过滤或多选)。
  2. 在选区上右键,执行 Search Selected Functions 操作;按 Confidence 降序 排序查询结果,验证 demangler_gnu_v2_41 位于列表靠后的位置。

这一步对应的工具栏/右键动作在 BSimOverviewProvider.createActions() 中定义:

new ActionBuilder("Overview BSim Search From Dialog", getOwner())
        .popupMenuPath("Search Selected Functions...")
        ...
        .onAction(c -> initialBSimSearch(true))
        .buildAndInstallLocal(this);

new ActionBuilder("Overview BSim Search", getOwner())
        .popupMenuPath("Search Selected Functions")
        ...
        .onAction(c -> initialBSimSearch(false))
        .buildAndInstallLocal(this);

两个动作的区别在于是否弹出查询对话框:不带 ... 的 “Search Selected Functions” 直接使用上次使用的服务器与设置(initialBSimSearch(false))立即执行;带 ... 的变体先打开 BSim 搜索对话框以便修改阈值与过滤器。实现上,选区被转换为函数入口地址列表后交给 plugin.doBSimSearch(program, selectedFunctionAddresses, showDialog),走的是与主菜单 “Search Functions...” 相同的 SearchTask 通道——此时 setMaximumResults 和过滤器都会正常生效,这正是练习二相对 Overview 查询多出约束能力的原因。

练习三:用 Vector Hash 判定“签名相同”

假设 Overview 表中 foobar 的 Hit Count 相同,存在两种可能:

  1. foobar特征向量不同,只是碰巧匹配数量相同;
  2. foobar 拥有同一个特征向量(即完全相同的 BSim 签名)。

为了区分这两种情况,可以启用可选列 Vector Hash

  1. 在 Overview 表中通过列选择器启用 Vector Hash 列;
  2. 找到两个 Vector Hash 相同的函数;
  3. 选中这两行,点击 BSim Overview 工具栏的 Make Selection 图标 Make Selection 工具栏图标Icons.MAKE_SELECTION_ICON),把选区转移到 Listing 视图;
  4. 在 Listing 中右键执行 Function -> Compare Function(s)
  5. 在弹出的函数比较窗口中,验证这两个函数确实应当拥有相同的 BSim 签名。

源码中,“两行哈希相同 ⇔ 特征向量相同” 这一语义由 BSimOverviewRowObject 的构造 保证:哈希直接对函数签名记录中的 LSH 向量调用 calcUniqueHash() 计算,是对向量内容的唯一性摘要。因此 Vector Hash 相等是判定“同签名”的快速充分依据,而工具栏的 Make Selection 动作(makeSelection())通过 GoToService 跳转到首个选中地址并广播 ProgramSelectionPluginEvent,让 Listing 获得同样的函数选择,从而无缝衔接 Compare Function 操作。

实操前提与小结

执行以上练习需要满足的前提:

  • 已启用 BSim 功能并配置好服务器(BSim -> Manage Servers),数据库中已摄入相关可执行文件(如 postgres、含 demangler_gnu_v2_41 的库等),具体摄入流程参见教程其他章节(如 Creating Database From GUIEnabling);
  • Overview 插件同一时间只允许一个 BSim 查询在后台运行(checkBusy() 会提示 “Only one BSim query permitted at a time!”)。

小结:Overview Query 是 BSim 工作流中“先普查、后精查”的第一步——它用 Hit Count + Self Significance 快速标出重复度极高、查询价值低的函数;练习二展示了如何把这种统计信息转化为查询范围过滤;练习三则利用 Vector Hash 这一隐藏列完成签名级判重。掌握了这三点后,你可以进一步学习带过滤器与每函数匹配上限的常规查询。下一节:Queries and Filters

登录后查看全文
热门项目推荐
相关项目推荐