本博客由闲散白帽子胖胖鹏鹏胖胖鹏潜力所写,仅仅作为个人技术交流分享,不得用做商业用途。转载请注明出处,未经许可禁止将本博客内所有内容转载、商用。

0x00 Diaphora介绍

Diaphora(διαφορά, 希腊语中的“different”)是一个IDA插件,用来帮助二进制文件对比。他和其他的比较工具很类似,有一个著名的开源工具就叫做Zynamics BinDiff。还有其他的开源工具,比如DarunGrim或者是TurboDiff。但是从实际使用结果上来看,这些Diaphora能够进行更多的操作,并且能够更好地识别效果。Diaphora源码下载。

这里我们使用Diaphora对库函数文件以及固件进行对比,从而进行库函数的识别工作。其实也不算是识别,只是进行比对。而我们在使用IDA过程中,使用过FLIRT进行函数签名,同时也使用过Bindiff进行二进制对比,但是其效果都不如Diaphora的效果好。所以我们这次以Diaphora为例,分析其进行函数签名及比对的算法,之后再对算法进行改进,获得我们想要的功能。

0x01 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插件或者感兴趣的同学可以自行研究~

0x02 Diaphora的匹配规则

这里我们只针对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不是更方便么)(理智的鹏鹏:我们当然可以这样做,但是这将导致自动化分析工具的不完整性)

0x04 FLIRT 的函数匹配算法介绍

使用过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 xxx
       替换成了
               0000: 90                nop
               0001: 0E                push    cs
               0002: E8....            call    near ptr xxx
        2.某些函数功能相同,但是调用方式不同,比如strcmp和fstrcmp在大内存模型中相同。问题在于,我们在进行识别的时候不想忽略这些函数,但是这对用户很重要,而我们却不能分辨出他们。
3.另外一个问题
                call    xxx
                ret
       或者
                jmp     xxx
      第一眼没有什么异常,但是问题在于目前标准库中存在着大量这样的代码。直接对比这些函数不会有什么结果,识别出这些函数的唯一方法就是找到他们调用的函数。通常来说,所有的端函数(2-3行指令)很难识别,并且误识别率很高。然而不识别他们也不行,可能会导致雪崩式错误:你没有识别出这个函数,调用他的函数你也识别不了。
       IDA的解决方案:
      IDA为我们行识别的函数库中的所有函数创建了一个数据库。IDA检查每个程序开头的byte是否被反汇编了,以及是否能够作为标准库函数的开始标志。识别算法所需要的信息都写在了一个 signature(签名)文件中,每个函数书都以pattern的形式表示。pattern是一个函数的前32字节,并且所有的变量字节都被标记出来。
      558BEC0EFF7604..........59595DC3558BEC0EFF7604..........59595DC3 _registerbgidriver
      558BEC1E078A66048A460E8B5E108B4E0AD1E9D1E980E1C0024E0C8A6E0A8A76 _biosdisk
      558BEC1EB41AC55604CD211F5DC3.................................... _setdta
      558BEC1EB42FCD210653B41A8B5606CD21B44E8B4E088B5604CD219C5993B41A _findfirst
      在pattern中,"."表示的是变量byte。一些函数的起始字节队列是相通的。因此,树形结构似乎特别适合于这些函数的存储。上面的paatern可以修改为:
558BEC
      0EFF7604..........59595DC3558BEC0EFF7604..........59595DC3 _registerbgidriver
      1E
        078A66048A460E8B5E108B4E0AD1E9D1E980E1C0024E0C8A6E0A8A76 _biosdisk
      B4
        1AC55604CD211F5DC3                                       _setdta
        2FCD210653B41A8B5606CD21B44E8B4E088B5604CD219C5993B41A   _findfirst
      树中的节点就是byte队列。在此例中,树的根节点包含队列"558BEC",并且派生出三个字数,分别以"0E","1E","B4"开头。以B4开头的子树又派生出了两个子树。每个子树都以叶子节点结束。函数名字就保存在叶子节点中(上述例子中尽有函数名可见)
树形数据结构主要完成了两个目标:
       1.我们在树种存储了多个函数共有的几个byte,因此了内存使用。当然这种接生的效果和函数数量成正比。
       2.他非常适合快速模式匹配。如果是将某个函数与函数库中所有函数进行匹配,进行匹配的次数将随着特征文件中的函数数量成对数增长。O(n)=log(n)
      那么根据函数的前32byte进行匹配就是一件明智的事情了。正如前文所述,二进制库中有很多函数的开头几个byte是相同的:
558BEC
      56
        1E
          B8....8ED8
                   33C050FF7608FF7606..........83C406
                                                      8BF083FEFF
                    0. _chmod   (20 5F33)
                    1. _access  (18 9A62)
      但是当两个函数有超过32字节向同师,我们将这两个叶子都存储下来。为了解决这个问题,我们计算33字节到第一个变量字节的CRC16值。CRC存储在签名文件中。进行CRC签名的bytre同样需要存储,因为它随着函数不同而不同。在上面例子中,CRC16是对——chmod函数的从33到52这20个字节进行计算的,对于_access函数计算18个字节。
      当然,第一个变量字节在33字节处也是有可能的,用于计算CRC16的字节长度为0.实际上这很少发生,而且这种算法给出误识别的概率非常低。
      有时候,函数的前32字节的pattern是相同的,CRC16也是相同的。比如下面的例子。
      05B8FFFFEB278A4606B4008BD8B8....8EC0
                0. _tolower (03 41CB) (000C:00)
                1. _toupper (03 41CB) (000C:FF)
      我们这里只使用3个字节计算CRC16,他在两个函数中是一样的。在这种情况下,我们将尝试找到一个位置,其中叶子中的所有函数有不同字节。在这个例子中,是32+3+000c
但即使这样,我们仍不能识别出所有的函数,比如:
... (partial tree is shown here)
                0D8A049850E8....83C402880446803C0075EE8BC7:
                  0. _strupr (04 D19F) (REF 0011: _toupper)
                  1. _strlwr (04 D19F) (REF 0011: _tolower)
      这两个函数在没有变量字节的情况下是相同的,而是根据调用他们的函数而产生了不同。在这个例子红,唯一的办法就是识别出在偏移11处使用他们的函数的名称。这个算法的缺点是,识别_strupr()和_strlwr依赖于识别出__toupper和_tolower,而在后者未识别之前,我们就要推迟识别;这就需要二次识别,但是好在只有几个函数需要二次识别。
最后我们还会遇到两个函数名字不同,但是代码相同的情况。这种情况在C++代码中很常见,我们将这种情况成为“碰撞”。看来只有人工智能能够解决此问题,FLIRT还在等待后续开发。
    总结:FLIRT进行函数识别算法如下。
       1.采用树形结构存储函数的前32字节,并且识别函数时进行逐一比对;
   2.当32字节出现冲突时,使用CRC16对从第33字节开始到第一个变量字节处的byte串进行计算;
   3.让CRC16相同时,尝试寻找一个每个函数的byte都不相同的位置;
   4.当函数字节完全相同,只有调用者不同时,先记录下来,随后进行二次检查;

5.当代码相同名字不同时,等待人工智能的应用。

总之FLIRT还是很智能的,进行库函数识别的也很好,但是我想Diaphora应该做的不会比FLIRT差,我们先分析下算法在进行讨论。

Diaphora源码分析——二进制文件对比工具相关推荐

  1. 链路追踪 SkyWalking 源码分析 —— Agent 插件体系

    点击上方"芋道源码",选择"设为星标" 做积极的人,而不是积极废人! 源码精品专栏 中文详细注释的开源项目 消息中间件 RocketMQ 源码解析 数据库中间件 ...

  2. 4hutool源码分析:DateUtil(时间工具类)-格式化时间(万字长文源码分析,学大佬如何写代码)

    技术活,该赏 点赞再看,养成习惯 看本篇文章前,建议先对java源码的日期和时间有一定的了解,如果不了解的话,可以先看这篇文章: 万字博文教你搞懂java源码的日期和时间相关用法 关联文章: huto ...

  3. BFD库的使用介绍 nm工具源码分析

    bfd介绍 想深入了解elf等可执行文件的原理(包括结构.运行等细节),用bfd库作切入点是比较好的选择. BFD是Binary format descriptor的缩写, 即二进制文件格式描述,是很 ...

  4. 网站压测工具 Webbench 源码分析

    介绍 Webbench是一个在Linux下使用的非常简单的网站压测工具.它的源代码只有500多行,挺值得一看的开源项目. 实现原理 只是简单的fork()出多个子进程模拟客户端去访问设定的URL,测试 ...

  5. 【FFmpeg】ffmpeg工具源码分析(一):main函数

    ffmpeg工具经常用来转换.生成媒体文件,下面是它的源码分析(一):main函数 ffmpeg版本:4.2.1 int main(int argc, char **argv) {int i, ret ...

  6. jquery1.43源码分析之工具方法

    相关文章: jQuery插件开发全解析 读jq之四 jquery1.43源码分析之核心部分 推荐圈子: Jquery 更多相关推荐 这个部分是jquery一些常用的工具方法. 包括为jquery对象扩 ...

  7. Java高并发程序设计学习笔记(五):JDK并发包(各种同步控制工具的使用、并发容器及典型源码分析(Hashmap等))...

    转自:https://blog.csdn.net/dataiyangu/article/details/86491786#2__696 1. 各种同步控制工具的使用 1.1. ReentrantLoc ...

  8. 微软开源软件特征源码分析工具 Application Inspector

    微软近日开源了其内部使用的软件特征源码分析工具 Application Inspector. 现代软件开发实践通常需要基于数百个现有组件中构建应用,无论它们是由组织中的另一个团队.外部供应商还是开源社 ...

  9. 【转】HashMap,ArrayMap,SparseArray源码分析及性能对比

    HashMap,ArrayMap,SparseArray源码分析及性能对比 jjlanbupt 关注 2016.06.03 20:19* 字数 2165 阅读 7967评论 13喜欢 43 Array ...

最新文章

  1. 1024,点亮人间烟火
  2. Amazon Redshift数据库
  3. 计算机驱动伺服的程序,伺服调试软件V-ASSISTANT始终找不到驱动-工业支持中心-西门子中国...
  4. 在JavaWeb中,什么是监听器?(建议收藏)
  5. SharePoint【ECMAScript对象模型系列】-- 07. 获取和修改List的Lookup字段
  6. 视觉定位VBL 视觉里程计VO 视觉SLAM 区别与联系
  7. 旧版sai笔刷_PaintTool SAI2.0笔刷
  8. 实现京东商城手机注册获取验证码
  9. c语言程序设计教程实验指导吴国栋,C语言程序设计教程实验指导
  10. linux系统ss命令详解,ss命令 - Linux命令大全 | linux教程
  11. win7配置TomCat环境
  12. 【Flutter实战静态页面】--在线点餐app(7)——页面跳转
  13. win10与ubuntu20.04双系统启动引导问题
  14. python中字符串输出乱码怎么解决_python字符乱码的解决小结
  15. php去除编辑器html标签,js处理富文本编辑器转义、去除转义、去除HTML标签
  16. 笔记本怎么用android,电脑上如何使用Android系统
  17. 精华阅读第 13 期 |常见的八种导致 APP 内存泄漏的问题 1
  18. 思科交换机如何配置Trunk?
  19. (最优化理论与方法)第二章最优化所需基础知识-第七节:保凸的运算和共轭函数
  20. python replace函数 成功 失败_解决python replace函数替换无效问题

热门文章

  1. I3C协议Single Data Rate(SDR)模式研读(六):通用命令代码(CCC)
  2. 如何利用matplotlib作出高精度的图片
  3. 第二幅“码绘”——创意自画像
  4. 苹果 Apple 发布的 AR 头显 Vision Pro 介绍
  5. brew安装mysql ,最后ln -sfv /usr/local/opt/mysql/*.plist ~/Library/LaunchAgents 这句什么意思?
  6. 软件测试面试-自定义表单配置该如何测试?
  7. ROS总结 rosdep update时 遇到了timeout报错
  8. 佩戴舒适的蓝牙耳机推荐,不堵耳朵的骨传导耳机
  9. c盘留多少空间比较好(win7c盘留多少空间比较好)
  10. RelativeLayout 相对布局