Group of Software Security In Progress

GoSSIP @ LoCCS.Shanghai Jiao Tong University

Binary Code Is Not Easy

论文下载

Abs & Intro

对于程序安全分析,二进制代码逆向分析的准确率影响着程序分析的正确性。静态二进制代码分析工具往往都具备以下这些功能:

  • decoding bytes into machine instructions
  • understanding the instruction semantics
  • performing control flow and dataflow analyses
  • assigning source language semantics to binary code:这里是指恢复出高级语言中的一些特征语义,例如函数,循环,函数参数,局部变量等等。

为了在逆向分析中提供准确的二进制程序中的信息,主要涉及三类问题:

  • code discovery
  • CFG construction
  • CFG partitioning:决定一个函数的范围,即function boundary问题

Fig

Basic Definations

给出了一些关于Program,CFG,Function的定义,主要是为了说明在二进制代码中,函数在内存空间上是连续。

Code Discovery

Non-Code Bytes

Non-code bytes主要是指跳转表数据,静态只读数据以及填充字节往往会穿插在代码段中,例如在libc2.12中的如下例子:

Fig

碰到这种问题使用linear scan来发现其中的代码指令往往就会产生错误。处理这种问题更好的方法就是使用control flow (recursive) traversal,根据控制流推断其中哪些是代码指令保证了代码识别的准确度。 但是由于函数入口识别问题(missing symbols)以及间接跳转问题(Indirect control flow)的存在,会致使漏识别其中的一部分指令,代码覆盖不完全。

Missing symbols

函数入口的正确识别有助于完整的代码发现,以及确定函数边界。但是由于stripped binary中的符号表会被移除,而导致在函数入口识别时缺少了大部分的信息,存在着一定的困难。 对于检测函数入口,文章基于二进制代码中函数入口的指令序列的观察,这些指令序列往往具有一定的特征,所以可以利用模式匹配的方式来识别这些具有共同特征的指令序列,并判定为函数入口。 文章中利用了Rosenblum的方法,先通过对二进制函数入口处指令序列的大量学习,并对每一种指令序列指定一个权重,来代表其作为函数入口的可能性。并在识别时,根据这个权重判断是否将这段指令序列判定为一个函数的入口。

Overlapping instructions

Overlapping instructions往往出现在一些恶意程序中,但是在一些常用代码里也存在这样的问题,作者在libc-2.12.so中发现了如下的Overlapping instructions.

Fig

对于这种问题只要根据control flow (recursive) traversal的汇报进行识别即可。

CFG construction

Jump Table

间接跳转的出现往往伴随着跳转表数据的存在,所以寻找并分析程序中的跳转表可以很大程度上解决间接跳转问题。 作者在这里用了很大的篇幅介绍了他们用于表示间接跳转计算式的数学模型,此处不予详细解释,和以前的一些同类数学模型大同小异。 基于这个数学模型,可以利用后向数据流分析来推导出间接跳转地址的计算式,基于这个计算式就可以给出一个完整的跳转目标集合。

Non-returning functions

在静态二进制程序分析中,如果不能够识别其中的non-returning function就会可能会导致假的控制流的边的产生。一般情况下控制流在进入callee后,callee会进行返回,控制流会回到caller,但是对于Non-returning Function来说,例如exit以及abort这样的函数,控制流不再会返回,这就导致了一些静态分析工具在caller函数中调用callee后判定了一条假的控制流。

判定一个函数是否non-returning function,只需要判定这个函数的所有结束点是否都调用了已知的Non-return function,如果是,那么这个函数也是一个non-returning function。在相关函数都给出判定后,如果依旧存在两个未知类型的functions,由于之间调用关系构成了循环,那这两个函数也可以判定为non-returning function。

CFG partitioning

Complex functions

完整函数的识别涉及了Function sharing code以及Non-contiguous functions等问题,这里根据control flow (recursive) traversal可以完整地对函数范围进行判定

Tail calls

编译器在优化时,往往会用一个jmp指令来代替call指令来完成函数调用。 为了识别tail calls,作者提出:首先,根据跳转目标是否为函数入口点进行识别,其次,根据如下两个启发式规则进行判定:

  1. 如何在跳转前,检测到对栈帧的消除,那么这是一个tail call;
  2. 如果有证据表明跳转目标和这条指令属于同一个函数,那么这个跳转不是一个tail call。

Evaluation

文章的evaluation主要从两个方面进行了说明:

  • 首先是,说明了这些问题在现实的软件程序中士存在的:

Fig

  • 其次,把他们的工具Dyninst与其他的工具,在一些存在的例子中进行了检测对比:

Fig