计算二进制数据相似度

时间:2009-02-24 00:21:08

标签: comparison binary-data similarity

我在这里看到了一些与确定文件相似性有关的问题,但它们都与特定域(图像,声音,文本等)相关联。作为解决方案提供的技术需要了解所比较文件的基础文件格式。我正在寻找的是一种没有此要求的方法,可以比较任意二进制文件,而无需了解它们包含的数据类型。也就是说,我希望确定两个文件的二进制数据的相似性百分比

为了给你提供更多细节,即使这可能适用于很多事情,我确实有一个我正在研究的具体问题。我目前也有一个有效的解决方案,但我不认为它是理想的。在比较方法方面可能存在许多优化,并存储结果。希望这里的一些人能够给我一些新的想法。我可能会在几天之后编辑一些关于我当前方法的信息,但我不想通过告诉你我是如何做的来偏见人们对这个问题的想法。

我正在处理的问题是视频游戏ROM图像的克隆检测。对于那些没有仿真经验的人来说,ROM是游戏卡带上的数据转储。 ROM“克隆”通常是同一游戏的修改版本,最常见的类型是翻译版本。例如,NES的原始最终幻想的日语和英语版本是克隆。游戏几乎分享了他们所有的资产(精灵,音乐等),但文本已被翻译。

目前有几个小组致力于维护各种系统的克隆列表,但据我所知,这一切都是手动完成的。我试图做的是找到一种方法来自动和客观地检测类似的ROM图像,基于数据相似性而不是“这些似乎是相同的游戏”。检测克隆有几个原因,但其中一个主要动机是与Solid compression一起使用。这允许将所有游戏克隆压缩到同一个存档中,整个压缩克隆集通常只占用比单个ROM更多的空间。

在提出潜在方法时需要考虑的一些问题:

  • ROM的大小各不相同,具体取决于系统。有些很小,但现代系统可能有大型,256MB或更多。一些(所有?)系统只有2个可能的大小的功能,其中一个系统上的130MB游戏将具有256MB的rom,基本上是空的。请注意,因此,如果游戏版本超过阈值并且必须使用两倍大小的盒式磁带,则某些克隆可能具有截然不同的大小。
  • 目前在许多系统上已有数千种已知的ROM,大多数系统仍然不断发布新的系统。即使对于较旧的系统,也有一个主要的ROM黑客社区经常生产修改过的ROM。
  • 为每个可能的ROM对存储相似性数据将导致数百万行数据用于任何更流行的系统。一个拥有5000个ROM的系统将需要2500万行相似性数据,而一个新游戏又增加了5000行。
  • 处理状态必须是可恢复的,因此如果它被中断,它可以从中断的地方继续。使用任何方法,都需要进行大量处理,并且假设整个事件将在一个批处理中运行并不安全。
  • 可以随时添加新ROM,因此该方法不应假设它已经具有“完整”设置。也就是说,即使您已经找出所有现有ROM的相似性,如果添加了一个新的(并且这也可能在之前的处理完全完成之前发生),必须有一种方法将其与之前的所有ROM进行比较,以确定哪个(如果有的话)它是。的克隆。
  • 应优先考虑更高的处理速度而不是准确性(至某一点)。知道两个ROM是94%还是96%相似并不是特别重要,但是如果需要一天的处理时间来比较新的ROM与之前的所有ROM,那么该程序可能永远不会真正完成。

这是一个有趣的问题,我期待看到其他人能想到的东西。如果您想了解更多细节,请在评论中告诉我,我会尝试提供这些细节。

10 个答案:

答案 0 :(得分:20)

听起来你想要二进制增量或者可能是从二进制增量的应用程序派生的索引(就像它的大小一样)。然后,您可以将此索引与您通过实验确定的某个基线进行比较,以确定它是否为“克隆”。

压缩和增量创建之间有很多相似之处,所以我说你对当前的实现并不遥远。

话虽如此,数据库中每个二进制文件的成对比较可能非常昂贵(O(n 2 ),我认为)。我会尝试找到一个简单的哈希来识别可能的候选对象。概念上类似于斯普登和爱德华所暗示的东西。也就是说,找到一个可以应用于每个项目的哈希值,对该列表进行排序,然后对列表中哈希值非常接近的项目使用更细粒度的比较。

构建对一般案例有用的哈希值已成为CS多年来积极追求的研究课题。 LSHKit软件库实现了这种算法。互联网可访问的论文FINDING SIMILAR FILES IN A LARGE FILE SYSTEM似乎可能更多地用于比较文本文件,但可能对您有用。最近的论文Multi-resolution similarity hashing描述了一种更强大的算法。但是,如果没有订阅,它似乎无法访问。当您浏览其他资源时,您可能希望在Locality Sensitive Hashing上保留维基百科文章。它们都具有很强的技术性,维基百科条目本身就非常重要。作为一种更加用户友好的替代方案,您可以在Acoustic Fingerprinting字段中应用一些想法(甚至是可执行文件)。

如果您愿意放弃一般情况,您可能会找到一个更简单(更快)的特定于域的哈希函数,它只适用于您的ROM。可能涉及标准或公共字节序列的放置以及它们附近的选择位的值。我对你的二进制格式并不是很了解,但我想象的是发出文件中部分开头的信号,如声音,图像或文本区域。二进制格式经常在文件开头附近存储这些类型的地址。有些还使用链接机制,将第一部分的地址与其大小一起存储在已知位置。这允许你移动到下一个也包含大小等的部分。一些调查可能会让你发现任何相关的格式,如果你还没有意识到它,并且应该让你很好地建立你的方式一个有用的哈希。

如果哈希函数没有让你一路走来(或者他们需要输入某种来定义度量/距离),那么网上就有几种二进制增量算法和实现。我最熟悉的是subversion版本控制系统使用的。它使用名为xdelta的二进制增量算法来有效地存储二进制文件修订版。这是一个直接链接到其存储库中实现它的文件的链接:xdelta.c。网络上可能还有一个工具可以让它更易于访问。

答案 1 :(得分:11)

您可能希望查看bsdiff,这是一个二进制差异/修补系统。还有一篇论文涉及很多理论。

答案 2 :(得分:7)

使用Plagiarism Detection算法中的一些想法。

我的想法:

为了为每个ROM创建一个类似的“签名”,随着小部分的变化略有不同,产生类似字频图的东西,但是你可以散列非常短的ROM部分,而不是记录单词的频率。 ,并记录哈希值的频率。

不要只散列一个部分,然后是从第一部分的结尾开始的下一部分,而是使用滑动窗口,从字节1开始散列部分,然后从字节2开始散列相同大小的部分,然后从字节3等。这将否定ROM中可变大小的变化部分的影响。

如果你使用了一个简单的散列函数,比如每个8位字节的xor,那么你就可以通过xor计算下一个窗口位置的散列,当前散列的输出为8位,x或输入的8位。另一种替代散列函数可以简单地使用指令代码字长。这可能足以为代表机器指令的代码创建静态模式。重要的是你需要一个哈希函数,它会在指令代码中产生共同的短序列,从而产生相同的哈希值。

您可能希望更少的哈希值具有更高的频率,但不要太远或者图表太平,导致难以比较它们。同样不要太宽,或者你会有很多很小的频率,再次比较难。

按ROM存储此图表。通过计算每个散列值的频率差的平方和来比较两个不同ROM的频率图。如果总和为零,那么ROM可能是相同的。它越远离零,ROM越不相似。

答案 3 :(得分:6)

虽然它比“几天”要多得多,但我想我应该在这里添加我当前的解决方案。

Nils Pipenbrinck与我目前的方法走向相同的方向。由于找到克隆的主要结果之一是实体存档的巨大节省,我想我可以尝试将任意两个ROM压缩在一起并查看节省了多少空间。我在7zip中使用LZMA算法。

第一步是单独压缩每个ROM并记下压缩大小,然后尝试将任意两个ROM存档在一起,并查看最终大小与各个压缩大小的差异。如果组合大小与各个大小的总和相同,则它们相似0%,如果大小与其中一个(最大大小)相同,则它们是相同的。

现在,这需要大量的压缩尝试,所以到目前为止我有一些优化(并想了解更多):

  1. 根据压缩大小的相似程度确定比较的优先级。如果ROM A的压缩大小为10MB,而ROM B的压缩大小为2MB,则它们不可能超过20%,因此比较它们以获得实际结果可以保留到以后。在高度相似的文件上运行相同的压缩算法往往会产生类似大小的结果,因此很快就能找到很多克隆。

  2. 结合上述内容,在任何一对ROM之间保持可能相似性的上限和下限。这允许进一步优先化。如果ROM A和B的相似度为95%,而且B和C的ROM只有2%相似,那么您已经知道A和C在0%和7%之间。这太低而不能成为克隆,因此这种比较可以安全地推迟甚至完全忽略,除非我真的想知道所有内容的确切相似性。

答案 4 :(得分:3)

我认为从数据压缩中借鉴的一些技术可能很有趣:

假设您有两个文件,A和B.

单独压缩每个文件并将压缩的大小一起添加。然后将这两个文件连接成一个大文件并压缩它。

尺寸的差异可以粗略估计文件的相似程度。

我建议您尝试使用Burrow Wheeler Transformation(bzip2)进行压缩。大多数其他压缩算法只有有限的历史记录。 BWT算法otoh可以处理非常大的数据块。该算法同时“看到”两个文件,任何相似性都会导致更高的压缩率。

答案 5 :(得分:2)

XDelta对于获得合适的二进制差异非常有用:http://xdelta.org

答案 6 :(得分:1)

您可以先存储hash trees之类的内容。只需为每个ROM存储一组这样的哈希值,并且所需的存储空间仅与ROM的大小成比例(但远小于),假设块大小恒定。所选的块大小必须提供足够的粒度以确保准确性,例如:最小大小为128MiB,精度约束为1%和Tiger-128 hash(类似于它们用于检查通过DirectConnect传输的文件),块大小为1MiB很好,你可以存储128 * 128/8 = 2048字节的所有哈希值!因此,对10,000个ROM进行此操作只需要大约20MiB的空间。此外,您可以选择安全性较低但速度较快和/或较小的哈希值。添加/检查相似性新ROM将需要类似的东西:

  1. 将新ROM分成块并散列每个ROM。
  2. 对于数据库中已有的每个ROM,将其哈希值与新ROM的哈希值进行比较(见下文)。
  3. 比较函数应检查相似性。但是它应该将每个散列视为不可分割的值,即不要试图在两个散列之间找到逻辑上显着的差异函数。只要块大小足够低并且哈希冲突很少,就可以通过简单的等效比较来保证准确性。

    如您所见,问题简化为性能更简单:检查更小的数据集是否相似。

答案 7 :(得分:1)

两个想法:

  • 考虑将文件组织为数据流图并对该表示进行一些规范化。既然您知道指令集,这可能是可行的,也许只是捆绑一个反汇编程序并进行一些文本处理。
  • CRM114这样的可训练分类器可能会派上用场,因为它可以让您了解二进制文件是否有很多共同之处。

答案 8 :(得分:1)

正如Waylon Flinn所说,您可能需要二进制增量算法。 rsync algorithm是一个很好的。{3}}。它快速可靠。另请参阅utility's documentation

答案 9 :(得分:1)

这里的困难在于,由于您正在处理可执行代码,因此简单的更改可以在整个ROM中传播。 ALL值的地址和偏移量可以通过添加单个变量或无操作指令来更改。这将使基于块的散列变得毫无价值。

一个快速而肮脏的解决方案是使用difflib(或等同的w /您喜欢的语言)来破解解决方案,因为它可以为您提供可以处理数据添加或删除的滑动比较。将ROM拆分为可执行文件和数据部分(如果可能)。数据部分可以直接比较similarity ratio calculated,但你仍然会遇到地址或偏移问题。

可执行部分更有趣。阅读机器的asm格式,获取可执行文件并将其拆分为一系列操作码。保留操作码并注册部件,但屏蔽掉“有效载荷”/“立即”部件(加载变量地址的地方)。将结果信息也传递给相似度计算器。

不幸的是,这仍然是对您跟踪的ROM数量的O(n ^ 2)操作,但可以通过(增量)聚类或基于频率的比较顺序来缓解,以减少比较的数量需要的。