本博客由闲散白帽子胖胖鹏鹏胖胖鹏潜力所写,仅仅作为个人技术交流分享,不得用做商业用途。转载请注明出处,未经许可禁止将本博客内所有内容转载、商用。
Diaphora(διαφορά, 希腊语中的“different”)是一个IDA插件,用来帮助二进制文件对比。他和其他的比较工具很类似,有一个著名的开源工具就叫做Zynamics BinDiff。还有其他的开源工具,比如DarunGrim或者是TurboDiff。但是从实际使用结果上来看,这些Diaphora能够进行更多的操作,并且能够更好地识别效果。Diaphora源码下载。
这里我们使用Diaphora对库函数文件以及固件进行对比,从而进行库函数的识别工作。其实也不算是识别,只是进行比对。而我们在使用IDA过程中,使用过FLIRT进行函数签名,同时也使用过Bindiff进行二进制对比,但是其效果都不如Diaphora的效果好。所以我们这次以Diaphora为例,分析其进行函数签名及比对的算法,之后再对算法进行改进,获得我们想要的功能。
各位如果有兴趣看官方文档的话,我还是推荐先看一遍官方的帮助文档。虽然我这里面也会进行一部分的翻译,但是我觉得原汁原味的还是最好的。帮助文档。
——Diaphora.py:IDAPython 插件。包含了所有启发,图形显示,输出接口等等
——jkutils/kfuzzy.py:这是未经修改的kfuzzy.py库版本,他是DeepToad项目的一部分,该项目主要对二进制文件进行模糊哈希(fuzzy hash)。 因为Diaphora对伪代码进行模糊哈希,所以我们引用了这个文件
——jkutils/factor.py:这是经过修改的基于图论的私有恶意软件集群工具(a private malware clusterization toolkit)。这个库提 供了在Python内快速factor数字的功能,并且能够对比素factor的数组。Diaphora使用该工具进行模糊AST哈希对比以及调用流图模糊哈希对比,这两个对比是基于小素数产品的( small-primes-products,BinDiff作者的思想,可以参考论文)
Pygments:此目录包含了未经修改的pygments库,是一个生成高亮代码的小工具,说英语代码保管,论坛,wiki以及其他需要美化源码的场景。
这其中,我们更关心两个文件Diaphora.py以及factor.py,因为我们其实主要是想了解Diaphora进行函数签名的算法以及对比的算法,这样我们才方便进行迁移,而一些代码美化和结果输出的内容,有兴趣开发IDA插件或者感兴趣的同学可以自行研究~
这里我们只针对Best match进行分析。顾名思义,Best Match是可信度最高的匹配结果,基本上能够达到95%以上的可信度。因为我们在进行二进制分析的时候,需要的是确定正确的匹配结果,如果使用了不可靠的匹配,将导致分析结果的错误。我们舍弃了部分匹配的结果,因此造成了较高的漏报;如果加入了部分匹配结果,将造成误报;从人工分析的角度来看漏漏报造成的影响将小于误报。比如你需要确定malloc函数的位置,进而分析哪个函数调用了malloc函数。而使用best match能够得到1个确定的结果,使用partial match得到了3个可能的结果,但是这3个函数实际上并不是malloc函数,这将增加我们的二进制分析工作,甚至导致分析结果的谬误。因此我们只选择完全匹配的函数。
好了,我们来看下官方文档怎么说明的。“diaphora首先会为两个二进制文件生成不同的的数据库,并且对比两个数据库中的数据是否相同,甚至是主键值都要相同。如果相同的话,数据库就认为是100%相同,并且不进行多余的比较。”那么我们都需要比较那些键值呢?
伪代码:IDA生成的伪代码应该相同,它能够匹配x86,x86_64和ARM指令集
汇编代码:两个函数的汇编必须完全相同
byte哈希和名字:每条汇编指令的第一个字节要相同,同时对应的真实名字,而不是IDA自动生成的伪指令名,要相同
相同的地址、节点、边界和内存:数据库中存储的基本块的数量、快的地址、边界的名字和内存都要相同
RVS和hash:RVA(Relative Virtual Address)相关虚拟地址以及字节哈希都要相同
相同的顺序和hash:两个函数应该具有相同的hash并且在IDA中的顺序应该相同(比如同为第100个函数)
函数hash:两个函数的hash相同。hash = MD5(所有指令的字节)
Byte hash:两个函数的Byte hash应该相同。这个hash是计算了所有指令字节的集合,但是和上面的函数hash不同,他去除了有地址依赖的指令,比如重定位和jump以及relative call。
Byte 数目:这两个函数的长度(也就是字节数)应该完全相同
从这些比对的条件来看,diaphora是一个比较严格的二进制文件对比工具,其实用作于补丁对比更合适,并不完全适合于库函数识别,并且大量使用了函数特征、block、反汇编和伪代码的内容,我们如果想要融入Angr还是需要大量的修改的,同时我们也并不是很确定他能兼容的很好。(鹏鹏小声bb:改什么改呀,直接使用IDA进行文件对比,然后根据生成的比对结果,从中抽选出best match,直接导入Angr不是更方便么)(理智的鹏鹏:我们当然可以这样做,但是这将导致自动化分析工具的不完整性)
使用过IDA的同学都知道,IDA自带了一个FLIRT工具,该工具能够自动生成函数的签名。我们既然说他不是很好用,就需要首先了解FLIRT是如何进行函数签名的。一下内容节选自官方文档。
FLIRT的识别原则基于以下几条。FLIRT只考虑C/C++(我们做的固件分析也主要是这两个语言);FLIRT并不试图进行完美的函数识别,因为某些函数完整的识别将导致不可遇见的结果(比如C/C++中两个不同名字的函数具有相同的代码,这种情况很常见);我们只识别代码段的函数(这个可以理解);只识别函数,不管参数和函数行为。(以上几条,我们也都是同意的。)
同时我们应该遵守以下约束:1.我们应该极力避免误报,理想的情况下误报应该为0(前文我们阐述了);2.识别的函数必须只是用有限的寄存次和内存资源;3.生成的签名必须是无处理器依赖的,能够跨平台使用;4.main函数应该识别出来并且被合理的标注。(注:我们和FLIRT的需求是有一些相关性的,上述两端的要求,我们基本上是同意的,但是我们并不满足于这些要求,我们期望得到更高的准确率和0误报率,我想这也就是FLIRT的局限性;同时,即使是FLIRT采用了避免误报的手段,我们在实际使用中还是会出现很多漏报,当函数比较短时,曾经有过46个函数误识别)。
FLIRT列出了很多库函数识别时遇到的阻碍。主要有:
1.很多字节是难以确定的。比如动态加载地址,某些地址是需要动态加载的时候进行重新纠正的,而连接的时候并不能知道。这些地址有external 名字,所以一定程度上缓解了这种情况。优化技术同时还引入了新的问题,就是可能导致常量字节不同。
0000: 9A........ call far ptr xxx5.当代码相同名字不同时,等待人工智能的应用。
总之FLIRT还是很智能的,进行库函数识别的也很好,但是我想Diaphora应该做的不会比FLIRT差,我们先分析下算法在进行讨论。