CCS 05 CFI
CFI(Control-Flow Integrity)来自于CCS 05!
简介
控制流完整性,可以防止那些任意控制程序行为的攻击。CFI的特点:
- CFI实施是很简单的,保障很容易建立;更近一步说,它是很实用的,它可以兼容现存的。
- CFI实施提供保护,即使是对整个执行程序的数据有完全控制的敌人。
- CFI为实施进一步的安全策略提供一个有用的基础。
CFI的安全策略要求/命令软件执行过程必须按照一个提前设定的CFG中的路径,然后这个CFG可以通过源码分析、二进制分析或者执行剖析得到.这个提前设定的CFG是一个关键。
因为从我的感觉,这个方法就像是程序设计者或者说保护着预先设计了一个游戏,这个游戏规定用户只能做出这样或者那样的行为,不能跳出他的设计范围,否则即刻出局.
用原文中的话就是说,运行时的动态检测必须保证控制流保持在给定CFG的范围内。CFI的主要工作就是对于直接或者间接的控制流进行限制。
由于在一些攻击中不正常的控制流更改是必须的步骤,所以CFI对于它们的防御还是很有效果的。比如说classic, stack-based buffer overflow。CFI实施并不是万能钥匙,因为对于已允许CFG边界内的渗透无法防御。这些渗透包含依赖错误参数字符串转化的特定渗透等。就像上面说的游戏的例子,只要用户行为在游戏规定的范围之类,就算这属于攻击行为,游戏程序也是不会对它做出反应的。
CFI需要,程序执行期间,无论何时一个机器指令转移控制流,它目标定位于一个有效的目的地址,正如先前创建的CFG图判定的。因为大部分的指令目标是一个常量目标地址,所以这个要求通常静态地施行。然而,对于目的地址只能在动态运行时确定的计算控制流,这个要求必须实施一个动态检查.
实现
利用ID来进行跳转的动态检查,这里的call,ret的用法都不是原本这两个命令所有的用法,有可能是进行插桩修改过的!完全看不出来作者的实现方法.
这张图解析了两种可行的ID和ID检测方式。作者使用12345678h来作为ID,上图中第一部分是一个简单的间接跳转实例!左侧是直接跳转指令jmp ecx
,右侧是一个从栈上取内容的mov
指令。其中ecx
中放的是目的指令所在的地址。
-
a方案:在目的地址
mov
指令处加上ID
,同时在计算目的地址处加上lea
指令(这个指令的 好处是不修改eflags
,同时将ecx
加4)。直接比较[ecx]
和12345678h
,不等的话直接跳转到错误label
,否则将ecx
加上4,然后跳转. -
b方案:使用ID-1作为ID检查的常量,在运行期间加1。相同的是,b方案也没有改变计算跳转地址,但是它并不是插入一个ID,而是加入了一个无副作用的
prefetchnta
指令来人工合成ID。要注意,这里所谓的无副作用指令,就是没有效果,单纯放在那里,引出来参数地址。这里有一个疑问3没有解决。
我们来说明一下核心思想:对于限制间接跳转的动态检查,我们可以这样做!假设我们有一个动态检查的可能集合。对于间接跳转,我们将动态运行的结果和我们的集合进行对比,如果动态结果不在我们预先设置的集合,就跳到错误处理的地方.
但是,对于动态集合大小的设定,我们需要在精确和效率做一个权衡,这里涉及到图1中的ID设定,如果sort2
不同的放回位置设置不同的ID,我感觉也是可以的!但是这样就要考虑由于动态检查集合的增大而带来的性能损失.
这张图解释了函数调用和返回的时候使用的插桩方法.函数调用这里综合了a,b两种方案,你可以对比它们!对于后边prefetchnta
的作用是为函数返回时增加动态验证.关于这个地方,我们可以从函数返回处的插桩代码很容易看出来.ecx
原本指向prefetchnta [AABBCCDDh]
这个指令的地址,所以使用cmp [ecx+4],AABBCCDDh
来进行动态检查.
这篇文章向我讲述了很多关于间接转移指令,如函数返回,函数指针调用,switch
结构和其他如C++ vtables
之类的结构。
问题讨论
-
文章中说已给出的CFG是如何生成,间接跳转它是如何拿出来的?
-
关于作者自己定义的
Lable ID; call ID,DST; ret ID
这些指令是自己实现的,还是一个虚拟定义,具体实现的时候并不是这样的?为什么问这个,因为我去查过关于call
和ret
,没有这样的用法!ret指令,是可以带参数,但是这个表示的也不是ID啊. -
关于
mov eax,12345677h;inc eax;
没有想出来原因. -
关于
prefetchnta
这个指令的使用,无论是Figure 2(b)还是Figure 3中,完全不明白是什么意思?曾经去查过这个指令,它是intel用于缓存的命令,但是手册上面并没有详细说明? -
CFG结构正确处理了x86中执行计算控制转移的指令,包括函数返回,通过函数指针发出的函数调用,switch结构,动态调度。上述话语描述了程序中的间接跳转,但是我不是很明白这个可能集合如何得到??? 特别是switch结构的各种case如何得到?对于函数指针发出的函数调用,本文利用二进制文件中的重定位信息来获得.这个问题貌似又回到了问题1.