操作系统开发人员总是热衷于提高漏洞防御技术,所以微软在Windows10和Windows8.1Update3(2014年11月发布)系统中已经默认启用了一种新的机制,这种技术称为控制流防护(Control Flow Guard , CFG)。
早先的抑制技术如地址空间布局随机化(ASLR)和数据执行保护(DEP),虽然并不完美,但已经成功地使得漏洞利用变得更加困难。ASLR导致了堆喷射的发展,而DEP导致了漏洞利用代码中返回导向编程(ROP)技术的发展。
为了探索CFG这种特殊的技术,我使用Windows 10技术预览版(build6.4.9841)系统,并使用VS2015预览版来创建测试程序。最新的Windows 10技术预览版(build10.0.9926)系统的CFG实现有一个细微的变化,我将会在后文指出。
为了完全实现CFG机制,编译器和操作系统都必须恰当地支持它。作为一种系统级的漏洞利用抑制机制,CFG的实现需要编译器、操作系统用户模式库和内核模块的配合。MSDN上的一篇 博文 介绍了开发者需要怎样做才能支持CFG机制。
微软的CFG是专注于间接调用的保护机制,以下面我所创建的测试程序为例,来讲解该机制的大致原理。
首先,我们看一下在CFG机制未启用时,上图红线所圈部分的代码编译之后的结果:
在上图中显示了一种间接调用类型,在编译时期它的目标地址并未确定,而是在运行时才确定该地址。这样,我们就能通过以下方式来进行漏洞利用代码的调用:
微软的CFG实现主要集中于抑制问题,即如果间接调用被利用来调用一个无效的目标(在exploit中,这可能是shellcode的第一步),CFG则可以有效抑制。
这个无效目标有一个与众不同的特征:在大多数情况下,它不是一个有效函数的起始地址。微软CFG的实现基于这样一个理念:间接调用的目标必须是一个有效函数的起始地址。那么,启用了CFG之后生成的汇编代码如何呢?
在间接调用之前,目标地址会传递给函数_guard_check_icall,而CFG机制正是在该函数中实现。在之前不支持CFG机制的Windows系统版本中,该函数未做任何事。在支持CFG的Windows 10中,_guard_check_icall调用了_guard_check_icall函数,该函数以目标地址作为参数,并按以下流程执行:
1、访问一个位图(调用CFGBitmap),该位图代表进程空间所有函数的开始位置,进程空间中每8个字节的状态对应CFGBitmap中的一个位。如果在每8个字节组中有一个函数起始地址,则CFGBitmap中对应的位会被设置为1,否则会被设置为0。下图是一个例子用来展示CFGBitmap的一部分。
2、将目标地址转换为CFGBitmap中的一个位,以00b01030为例:
最高位的3个字节(上图中蓝线圈中的24位)代表CFGBitmap的偏移量(以4字节为单元)。在该例子中,最高位的3个字节值等于0xb010。因此,指向CFGBitmap中一个4字节单元的指针就是CFGBitmap的基地址加上0xb010。
与此同时,第4到第8位(上图红线圈中的5位)代表值X。如果目标地址与0×10对齐(目标地址&0xf==0),则X就是单元内的位偏移值。如果目标地址没与0×10对齐(目标地址&0xf!=0),则X|0×1就是位偏移值。
在这个例子中,目标地址是0x00b01030,X的值为6。表达式0x00b01030&0xf的值等于0,这表明位偏移也是6。
3、接下来我们看下第2步中所提到的位。如果这个位等于1,这就表明该间接调用目标有效,因为它是一个函数的起始地址;如果该位等于0,表明该间接调用目标无效,因为它不是一个函数的起始地址。如果间接调用目标有效,则函数将什么也不做。如果目标无效,将会抛出一个异常,以此阻止利用代码的执行。
控制流防护的实现有助于阻止某些类型的漏洞利用。而从长远来看,这将有助于减少因软件漏洞而带来的威胁。
[参考来源 trendmicro ,转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)]