二进制分析实战第六章

Posted by Kody Black on September 14, 2023

第六章 反汇编与二进制分析基础

反汇编一般分为静态反汇编和动态反汇编(前者不执行,后者执行)

静态反汇编

加载二进制文件——>找出所有的机器指令——>反汇编指令为可读形式。

一般有线性反汇编和动态反汇编两种方法。

  • 线性反汇编:它以二进制的形式遍历所有的代码段,连续解码所有的字节,并将其解析为指令列表。
    • 并非所有的字节都是指令
    • 内联数据甚至会导致指令流不同步
  • 递归反汇编:先从已知的入口点开始进入二进制文件,然后递归地跟随控制流(如跳转和调用)来发现代码,这使得递归反汇编可以在少数情况下解决有数据字节包围的问题。
    • 通常很难静态地找出间接跳转或调用的可能位置 ,可能会导致丢失

动态反汇编

动态反汇编工具(也称为执行跟踪程序或指令跟踪程序)能够在程序执行时简单地转储指令(以及可能的内存、寄存器内容)。

动态反汇编的主要缺点是代码覆盖率的问题:动态反汇编工具不能看到所有的指令,而只会看到它所执行的指令。

如何提高代码覆盖率?

  • 精心设计的测试套件
  • 模糊测试(使用Fuzzer,例如AFL)
  • 符号执行(我的理解是已经实现了代码的分块,然后通过符号执行的约束,得到可以进入这部分代码的符号条件,再进行执行,成功后更换分支,确定新的条件再执行,从而提高覆盖率)

构建反汇编的代码和数据

这部分工作就是说:将大型的非结构化的指令进行划分,从而恢复代码和数据。

  • 构建代码:

    • 分块(划分函数):一般利用函数签名(常见的函数开头的函数序言都是push ebp;mov ebp,esp,并且函数结尾是leave;ret )书里面说目前有很多研究是基于代码结构进行函数检测,另外还可以使用ELF文件的.eh_frame节进行函数检测

    • 揭示控制流

      有控制流图(IDA pro默认的图表视图)和调用图(IDA pro中的引用函数图)

    注意:对于面向对象的代码,想要恢复有层次的类以及虚函数等,目前大部分的反汇编工具无能为力

  • 构建数据

    • 根据库函数的规范自动推断数据类型
    • 在动态分析中,可以根据对象在代码中的访问方式来判断内存中对象的数据类型

    理论上,无法准确检测复合数据类型。为了便于手动构建数据,IDA Pro允许你自定义复合类型(通过逆向代码来推断),并且将它们分配给数据项。

另外,在反汇编基础上,例如IDA pro还可以进行反编译(但是,注意反编译后的代码可以与原始代码相去甚远)

中间表示(Intermediate Representation,IR)是一种简化分析的好方法,机器码——>IR——>汇编。

基本分析方法

二进制分析的特性

  • 程序间和程序内分析
    • 前者是会结合程序间关系进行分析
    • 后者的每段分析限于函数本身
  • 流敏感性:意味着分析将指令的顺序也考虑在内
  • 上下文敏感性:在程序间分析中,考虑函数调用的顺序

书上说的这三点,程序间分析其实就一定程度上包含了上下文的敏感性,区别只是上下文敏感的程度(对连续多少个上下文敏感)

控制流分析

主要方法是进行循环检测

书中提出的主要是通过支配树的方式(A支配B==A是到达B的必经点),如果B也能到达A,那么就构成了一个自然循环。

具体的算法是使用DFS,然后在回溯的时候看有没有出现重复节点。

数据流分析

  • 到达定义分析

    gen:函数生成的数据

    kill:函数使用并不再需要了的数据

    in:in[B] = U[out(P)] ,P∈pre[B]

    out: out[B] = gen[B]U(in[B]-kill[B])

  • def-use 分析

    在B中使用的数据y的定义位置。

  • 程序切片

    分析数据y,只提取处和y有关的内容

另外,编译器的优化会很大的影响反汇编