8

我想确定两个可执行文件中的两个函数是否是从相同的(C)源代码编译的,并且即使它们是由不同的编译器版本或不同的编译选项编译的,我也愿意这样做。目前,我正在考虑实现某种汇编程序级别的函数指纹。函数的指纹应具有以下属性:

  1. 在不同情况下从同一来源编译的两个函数可能具有相同的指纹(或相似的指纹),
  2. 从不同的 C 源代码编译的两个函数很可能有不同的指纹,
  3. (奖励)如果两个源函数相似,则指纹也相似(对于相似的一些合理定义)。

我现在正在寻找的是一组编译函数的属性,它们分别满足 (1.) 并希望也满足 (2.)。

假设

当然,这通常是不可能的,但在大多数情况下可能存在一些可行的方法。以下是一些可以使其更容易的假设:

  • linux ELF 二进制文件(但没有可用的调试信息),
  • 不以任何方式混淆,
  • 由 gcc 编译,
  • 在 x86 linux 上(可以在其他架构上实现的方法会很好)。

想法

不幸的是,我几乎没有组装经验。以下是对上述属性的一些想法:

  • 函数中包含的指令类型(即浮点指令、内存屏障)
  • 从函数访问内存(它是否从堆读取/写入堆?堆栈?)
  • 调用的库函数(它们的名称应该在 ELF 中可用;而且它们的顺序通常不应该改变)
  • 控制流图的形状(我想这将高度依赖于编译器)

现有工作

我只能找到切向相关的工作:


您对函数属性有什么建议吗?或者一个不同的想法也可以实现我的目标?还是已经实现了类似的东西而我完全错过了它?

4

4 回答 4

5

FLIRT 使用字节级模式匹配,因此它会随着指令编码的任何变化(例如不同的寄存器分配/重新排序的指令)而分解。

有关图形匹配,请参阅 BinDiff。虽然它是封闭源代码,但 Halvar 在他的博客上描述了一些方法。他们甚至以BinCrowd 插件的形式开源了一些他们用来生成指纹的算法。

于 2011-09-02T13:30:47.397 回答
0

我建议您从编写代码的语言以及代码对编译器优化的约束条件的角度来解决这个问题。

我对 C 标准并不熟悉,但 C++ 有“可观察”行为的概念。该标准仔细定义了这一点,只要结果给出相同的可观察行为,编译器在优化方面就有很大的自由度。我对尝试确定两个函数是否相同的建议是尝试确定它们的可观察行为是什么(它们执行什么 I/O 以及如何与其他内存区域交互以及以什么顺序进行交互)。

于 2011-09-02T16:54:06.367 回答
0

如果问题集可以简化为由n 个C不同编译器编译的一小组已知或C++源代码函数,每个编译器都有m[n]组不同的编译器选项,那么一个简单但乏味的解决方案就是编译代码使用编译器和选项的每种组合,并对生成的指令字节进行编目,或者更有效地,将它们在数据库中的散列签名编目。

使用的可能的编译器选项集可能很大,但在实际实践中,工程师通常使用相当标准的少量选项集,通常只是针对调试进行了最低限度的优化,并针对发布进行了全面优化。研究许多项目配置可能会发现,在任何工程文化中只有两三个与编译器工作方式的偏见或迷信有关——无论是否准确。

我怀疑这种方法最接近您真正想要的:一种调查疑似盗用源代码的方法。所有重建编译器解析树的建议技术可能会取得成果,但对于被忽视的对称解决方案或模棱两可的无法解决的情况有很大的潜力。

于 2011-09-02T17:33:58.283 回答
0

在我看来,做这样的事情最简单的方法是将函数程序集分解回一些更高级别的形式,其中存在构造(如forwhile、函数调用等),然后匹配这些更高级别构造的结构。

这将防止指令重新排序、循环提升、循环展开和任何其他与比较混淆的优化,您甚至可以在两端将这个更高级别的结构优化到最大值,以确保它们处于同一点,因此未优化之间的比较调试代码并且-O3不会由于缺少临时/缺少寄存器溢出等而失败。

您可以使用诸如回旋镖之类的东西作为反编译的基础(除非您不会吐出 C 代码)。

于 2011-09-02T16:04:43.593 回答