2.4 计算域内安全防御技术

了解完计算域内安全的基础逻辑后,我们这一小节开始介绍从CPU角度如何做好这个方向上的安全。

Note

从这一小节开始,我们就需要一些技术常识了。从这一章开始,面向的读者有一定的转变:从完全不懂计算机如何运行对计算机的原理有一定的了解,无奈之举呀~ 不过笔者还是尽量保持对架构无关的前提,保持文中的概念对于不同的CPU架构都适用。

首先,我们先要明确,上一章节末尾所介绍的思路:把程序看做一个特殊CPU,而数据是这个特殊CPU的代码指针。是CPU视角无法防御的。因为CPU并没有关于数据本身的语义,也就是说,对于CPU来说,它指认得自己最初始的指令,而不包括软件定义的抽象的“指令”。

并且,正如前一小节中所述,该类问题是永远无法杜绝的。而CPU在这个方向的作用就是,尽可能阻止一些常用的攻击技术,同时不产生过多的开销。因此这个方向的安全通常以安全Feature为具象,例如我们在2.2节中介绍的DEP技术。

所以,这个方向上的防御的主要目标就是:如何保证CPU执行的指令是编程者所预期的,而非攻击者所控制的。而这个术语就是控制流完整性 CFI(Control-flow Integrity),这里控制流指CPU的指令流(对应与读写数据的数据流),完整性指没有被攻击者劫持。这个概念并不局限于CPU的安全特性,也可能由纯软件机制来保护,本系列只关注与CPU设计相关的部分。

有了控制流完整性这个概念,我们就可以对这个目标进行切分了,从以下几个维度:

  • 控制流有前向和后向两种,即调用/跳转指令为前向(它是指令流“向前”寻找新的指令),和与之相对的返回指令为后向。因此,控制流完整性也分为前向和后向两种。
  • 纯软件实现的控制流完整性和硬件协助的控制流完整性。限于本系列的目的,我们只介绍硬件协助的控制流完整性。
  • 根据检查的粒度分为粗粒度控制流完整性和细粒度控制流完整性。这里的粒度是一个相对概念,例如,对一个间接调用指令来说,其理论上正确的跳转目的地址可能有3个,而对于被攻击者篡改的情况下来说,这个可能性的空间是所有可能的地址。如果能够检查每次跳转确实属于这3个之一,那么就是最细粒度的控制流完整性。而很多方案可能限制了它能够跳转到1000个目标(3个正确目标的超集),这对于全内存空间来说,是相对安全的,但是是一个粗粒度的。

前向与后向

稍微回顾一下前面的一个案例: