Group of Software Security In Progress

GoSSIP @ LoCCS.Shanghai Jiao Tong University

Automatically Deriving Pointer Reference Expressions From Binary Code for Memory Dump Analysis

论文下载:http://dl.acm.org/citation.cfm?id=2786810

Abs & Intro

在程序调试过程中,指针是造成段错误的根本原因;在程序安全中,指向代码的指针往往是控制流劫持的首要目标。故而对于一个故障转储信息或者一个内核内存快照,需要通过拥有分析其中的内存指针,来定位故障的根本原因,或者用于分析其中的控制流完整性。

定位一个内存中的地址指针,现有的一些方法往往需要对应程序的数据结构信息,但在这实际环境中,并一定能够得到相应的数据结构信息甚至是程序源码。

所以本文提出了一种的新的概念pointer reference expression(ptr-rexp),这种表达式从二进制代码中抽取,并用于定位之指针在内存中的位置,并且能够被用于检测内核函数指针的完整性。

在程序编译完成以后,其中的代码往往是静态的,对于两台机器中运行的相同程序,不同之处就在于程序中的数据。基于这个基础,对于不同机器上的同一各程序,推导出的ptr-rexp是通用的。所以虽然由于堆分配器的分配策略,同一个指针可能被分配到不同的内存地址上,但是这个指针的值依旧是由同一个ptr-rexp推导出来的,它的值应该是相同的。

从仅有的物理内存快照中定位被劫持的内核函数指针,有两个难点:

  1. Where to locate the kernel function pointers;
  2. How to determine whether they are hijacked

Overview

文章给出了分析内核函数指针完整性的工具原型,包括两部分:ptr-rexp extractor以及pointer value comparer:

  • 在一个受信任的机器上,利用extractor分析程序运行,推导其中的ptr-rexp。
  • 对于待分析的不受信任的机器,首先获取它的物理内存快照。
  • 在诊断机器上,利用comparer分析获得的物理内存快照,并通过low-level pointer value or pointer target comparison,来判定是非存在潜在的控制流劫持。

Assumption

  1. 对于获得物理内存快照,文中假设同样能获得page table base register(i.e., CR3 in x86)的值,用于完成虚拟地址与物理地址之间的转换.
  2. 另外,对于要分析的内核系统,内核代码页是不可写的,并且在不同的VM上同一版本的内核代码相同

PTR-REXP Extractor

Overview

ptr-rexp通过一个基址与一定的偏移量以及其他指针引用的结合,来编码一个指针的获取表达式

例子如下:

  1. mov 0xc034bc78(), %eax; move proc_root_fs to ea
  2. mov 0x20(%eax), %eax ; proc_root_fs->f_ops
  3. <… irrelevant instrs …>
  4. call 0x8(%eax) ; proc_root_fs->f_ops->read

通过对上述指令的推导,可以得出如下表达式:

ptr-rexp: ((*(0xc034bc78)+0x20)+0x8)

Detailed Algorithm

The main intuition behind the algorithm is that the roots of data dependence are pre-determined addresses. We recognize the potential locations by looking for literal values, that fall within the kernel address space. We “taint” such values.

Taint Sources

shadow record(包括shadow,instRecord以及instMap)依旧以下两条规则生成

  1. Rule-I:和传统的污点分析相同,依据数据传送指令以及运算指令,判定其中的operand,并生成对应的shadow record;
  2. Rule-II:对于一个内存操作数,如果它直接是一个内核全局内存地址或者是从这样一个内核全局内存地址推导计算而来的话,同样也要生成相应的shadow record。尤其是对于一个由Displacement(BaseAddr, Index, Scale) = BaseAddr + Index × Scale+ Displacement生成的内存操作数,如果其中的BaseAddr来自一个被污染的寄存器,或者其中的Displacement是一个全局的地址,那么这个操作数就需要被记录下来。

Taint Propagation

在向后分析的过程中,Shadow记录会根据指令进行传递(instRecord不会,它通过instMap进行相应的映射),类似于动态的实时更新。如果指令生成了一个新的instRecord,例如进行了运算操作,而不是传送操作,那么就会生成一个新的Shadow记录

Taint Sinks

通过后向遍历instMap导引的instRecord,生成我们所需要的prt-rexp,递归算法如下:

1: let rec resolve_data_path (p: PC) (v: value) (t: instMap): exp =
2: if p = 0 then (Value(v)) else (
3: let (sem, op1, op2) = Hashtbl.find t p in
4: match sem with
5: Move −> resolve_op p op1 t
6: | Binary −> BinOP(resolve_op p op1 t, resolve_op p op2 t)
7: | Call-Mem −> resolve_op p op1 t
8: )
9: and resolve_op (p: PC) (op: taintOP) (t: instMap): exp =:
10: match op with
11: memOpTaint ((v1, pc1), (v2, pc2), scale, disp) −>
12: let regValue1 = resolve_data_path pc1 v1 t in
13: let regValue2 = resolve_data_path pc2 v2 t in
14: DeRef (regValue1, regValue2, scale, disp)
15: | regOpTaint (v3, pc3) −> ( resolve_data_path pc3 v3 t )
16: | NoOpTaint −> Value (0)

Pointer Integrity Checker

  1. Step-I Direct Value Comparison for Core Kernel Code: 如果指针指向core kernel code (which are those excluding kernel modules),只需直接对比地址的值即可判定
  2. Step-II Direct Target Comparison for Kernel Modules: 如果指针指向kernel module,通过比对目标代码来进行比对