GS就是一个缓冲区溢出标志,如果发生缓冲区溢出,该标志会被覆盖,导致其值和其在.data区域的备份值不同,由此系统进入异常处理流程,函数不会正常返回,ret指令也不会执行。
以下情况VS不会开启GS:
- 函数不包含缓冲区
- 函数被定义为具有变量参数列表
- 函数使用无保护的关键字标记
- 函数在第一个语句中包含内嵌汇编代码
- 缓冲区不是8字节类型且大小不大于4字节
开GS和不开GS的对比(此处均关闭了代码优化,防止变量位置被编译器调整):
#include "string.h"
#pragma strict_gs_check(on) //此程序没有4字节以上的缓冲区,故函数不受GS保护,除非使用此句强制开启
int vulfunction(char* str){
char arr[4];
strcpy(arr,str);
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
char *str="yeah,i'm a guy.";
vulfunction(str);
printf("hellworld");
return 0;
}
以下是突破GS的几种策略:
- 专门攻击没有GS保护的函数 - -||
- 溢出覆盖虚表指针,将程序要调用的虚函数地址替换为我们的shellcode地址
利用覆盖虚表指针来突破GS的可行原因是:this_ptr存放在[ebp-0C]处,而cookie存放在[ebp-4]处,所以溢出会先覆盖this_ptr;恰好函数返回前(gs检查前)调用了虚函数,所以可以通过只篡改虚表指针达到调用shellcode、且gs保护丝毫不会察觉的效果 - 攻击异常处理机制
溢出覆盖S.E.H中的异常处理函数地址,当发生异常时,就可以直接跳转执行! - 同时替换栈中cookie和.data区域的备份
在堆中申请数组空间,通过数组越界篡改.data区域的cookie备份,而栈中用到的cookie又是从.data区取的,所以此突破方式简直神不知鬼不觉啊。。。不过不同环境下,.data区的cookie位置可能有所不同。。。同时要注意cookie不是直接存于栈区、直接和备份进行比对的,参见上图中的cookie装载、cookie检测过程