BinDiff 是如何工作的?

逆向工程 工具 工具bindiff
2021-06-25 04:08:38

我想知道BinDiff 软件的基本原理是什么(也许还有一些关于优化和启发式的东西)有没有人对它有很好的教学解释?

2个回答

通常,截至撰写本文时的当前版本 (4.x) 中的 BinDiff 通过在函数级别匹配属性来工作。基本上,匹配分为两个阶段:首先生成初始匹配,然后在向下钻取阶段进行细化。

初始比赛

首先,BinDiff 根据以下属性将签名关联到每个函数:

  • 基本块的数量
  • 这些块之间的边数
  • 调用子函数的次数

这一步为我们提供了每个二进制文件的一组签名,这些签名又用于生成初始匹配集。遵循一对一的关系,BinDiff 根据上述特征选择这些初始匹配项。

下一步尝试在每个二进制文件的调用图上找到匹配项:对于经过验证的匹配项,检查匹配函数中的一组调用函数以找到更多匹配项。只要找到新的匹配项,就会重复此过程。

向下钻取

在实践中,并不是所有的函数都会被初始匹配策略引入的一对一关系匹配,所以在初始匹配确定后,我们仍然有一个未匹配函数的列表。下钻阶段的想法是应用多个不同的功能匹配策略,直到找到匹配为止。应用这些策略的顺序很重要:BinDiff 首先尝试那些它认为具有最高置信度的策略。只有找不到匹配项,才继续执行下一个策略。重复此操作,直到 BinDiff 用完策略,或直到所有函数都匹配为止。示例包括 MD 索引、基于函数名称的匹配(即导入)、调用图边 MD 索引等。

MD-索引纸

基于图形的可执行对象比较

可执行对象的结构比较

(免责声明:在@ team zynamics / google 工作,希望我没有搞砸任何事情,否则 soeren 会折磨我 ;-))

关于控制流图的构建,我只能说几句话,尽管我的答案绝对不是完整的。

BinDiff 使用静态类型的检测执行流,我想是因为执行代码并不总是可能的(例如,对于 ring 0 驱动程序)或合理的(恶意软件)。实际上,给定的文件被反汇编,然后它应该被拆分成基本块(这些是具有直接执行方式的代码段,尽管这个定义在那种情况下是正确的)。很明显(例如,考虑 x86 架构)指令会jxx改变程序的控制流。所以基本块通常由它们终止。这种将代码拆分成块的过程本身并不是一项复杂的任务,更具挑战性的部分是确定跳转目的地。

例如这样的事情:

...
jz eax

因此,我们无法(轻松)通过自动静态分析来理解此调用所指向的位置。可以“模拟”琐碎的案例,但总的来说,这项工作非常艰巨且令人沮丧。另一种选择是跟踪程序以查看代码执行的路径(可以手动完成)。当找到这些块时,剩下的唯一一件事就是构建人类可读的图形。

无论如何,有很多方法可以改变执行流程(异常、另一个线程的热补丁、系统相关事件等)。