VC++中利用GS开关防止缓冲区溢出

VC++中利用GS开关防止缓冲区溢出,第1张

VC++中利用GS开关防止缓冲区溢出,第2张

缓冲区溢出通常是当今软件中最常见的漏洞。黑客可以利用恶意输入改变程序的执行进程,从而入侵相应的进程、计算机或整个域。如果该进程在高度可信的帐户下运行,如管理员或本地系统帐户,黑客造成的损害将极其严重,并存在广泛传播的潜在风险。近年来,一些“众所周知”的病毒,如红队、冲击波、冲击波等。,都源于C/C++代码缓冲区溢出。

从程序的角度来看,缓冲区溢出只是一个简单的编程错误——都是把一个内存区域的内容复制到另一个,目标内存区域太小,容纳不下。以下代码做了一个简单的演示:

char* source = "一个相当长的字符串";
char dest基于堆栈的缓冲区溢出之所以能被如此简单地利用,是因为编译器生成的指令会将函数的返回地址存储在堆栈中,但要认识到编译器在这个问题中只起了很小的作用。从Visual C++开始。NET(7.0)中,Visual C++开发团队采用了一种方法,从编译器上降低了出现这类问题的概率。他们在堆栈中存储的函数返回地址的数据下插入一个已知值的cookie。因此,如果缓冲区溢出更改了函数的返回地址,它也会覆盖这个cookie。当函数返回时,通常会检测到这个cookie。如果检测到cookie已被修改,将引发安全异常。如果不处理这个异常,进程将被终止。下面的代码演示了一个带有安全异常处理方法的简单程序:;
::strcpy(dest,source);

在这个例子中,源字符串的长度是25个字符(包括空终止符),这对于目标内存块来说无疑太大了,目标内存块是在堆栈上声明的;当执行这段代码时,原始堆栈将被销毁,程序将由于访问冲突而崩溃。如果这个源内存块是由外部第三方提供的,则可能存在漏洞,因为它允许传入函数的内存块以特定的方式修改堆栈。

在C/C++中调用函数时,被调用函数的返回地址存储在堆栈中,所以当被调用函数结束时,执行过程可以回到原来的地方。如果调用了可能包含潜在缓冲区溢出的函数,则可能会修改返回地址,执行进程将跳转到缓冲区数据中的指定位置。通过更改函数的返回地址,攻击者可以在进程中的任何位置获取代码来执行。一般来说,可以通过两种方式利用它:

如果存在漏洞的程序是已知的,容易被访问,攻击者可以找到一个函数的地址,通常在所有流程实例的固定地址找到;并修改堆栈,等待这个函数被调用。

要执行的指令可以作为缓冲区的一部分传递给进程地址空,攻击者可以利用这一点来完成攻击。
防止缓冲区溢出

防止缓冲区溢出的最简单方法是限制复制数据的大小,使其不能大于目标缓冲区容量。虽然这种方法看似琐碎,但实际上经验证明,要彻底消除那些大型C/C++代码中的缓冲区溢出隐患,是一件非常困难的事情。此外,使用托管技术,如。NET或者Java也可以大大降低缓冲区溢出的风险,但是把大型项目移植到这种技术上不太可能,也不合适。

[10]

Void _ cdeclsec _ handler (intcode,void *)
{
if(code = = _ sec err _ buffer _ overrun)
{
printf("检测到缓冲区溢出。\ n ");
退出(1);
}
}

int main()
{
_ set _ security _ error _ handler(sec _ handler);

//这里省略了主程序代码。
}

Visual++。NET 2003 (7.1)通过将易受攻击的数据结构(如异常处理方法的地址)移动到堆栈中缓冲区下方的位置,加强了对缓冲区溢出的保护。在编译器7.0版本中,可以通过破坏缓冲区和cookie之间的敏感数据来绕过安全cookie提供的保护;但是,在新版本的编译器中,这些数据被移到了缓冲区下的一个区域。现在看来,修改这些数据溢出是不可能了。

在C++编译器6、7.0、7.1中,演示了堆栈的概念布局,堆栈从高地址向低地址空增长,这也是程序执行时堆栈增长的方向。堆栈向下增长是缓冲区溢出的主要原因,因为溢出会覆盖比缓冲区高的内存地址空,而这正是易受攻击的数据结构所在。

除了将异常处理方法等信息移动到堆栈中的数据缓冲区,Visual C++的链接器。NET 2003还将结构化异常处理方法的地址放在可执行文件的头中。当异常发生时,操作系统可以检查异常信息在堆栈中的地址是否符合文件头信息中记录的异常处理方法。如果情况不是这样,将不会执行异常处理方法。例如,Windows Server 2003可以检查结构化的异常信息,这项技术在Service Pack 2中被移植到了Windows XP中。

Visual C++ 2005(8.0)在此基础上更进一步。通常,当一个函数调用发生时,如果其中一个本地缓冲区超出限制,攻击者可能会覆盖堆栈中它上面的任何内容,包括异常处理、安全cookie、帧指针、返回地址和函数参数。这些值大部分都受到不同机制的保护(比如安全异常处理),但是对于一个以函数指针为参数的函数,还是有溢出的可能。如果一个函数接受一个函数指针(或者一个结构或类包含一个函数指针)作为参数,攻击者就有可能重写指针中的值,让代码执行他想要的任何函数。鉴于此,Visual C++ 2005编译器将分析所有可能存在该漏洞的函数参数,并复制一个函数参数——不使用原函数参数,而是放在堆栈中的局部变量下。如果原始函数参数被溢出覆盖,只要副本中的值保持不变,整个函数就不会被破坏。
应用缓冲保护

只需打开/GS编译器开关来启用缓冲区保护。在Visual Studio中,可以在“C/C++”选项页的“代码生成”选项中找到此开关。默认情况下,它在调试配置中是关闭的,在发布配置中是打开的。

如果使用最新版本的编译器来编译和生成结构化异常信息,默认情况下将打开安全结构化异常处理。此外,还可以使用/SAFESEH:NO命令行选项关闭安全结构化异常处理。在Visual Studio的工程设置中,没有办法关闭安全的结构化异常处理,但是您仍然可以在链接器中使用此命令行选项。

/GS及以上。

仅仅打开一个编译器开关并不能使程序完全安全,但在安全漏洞以各种形式出现的今天,它将有助于使程序更加安全。基于堆栈的缓冲区溢出是一大类安全漏洞,但随着黑客攻击技术的不断更新,相信它的谢幕还有很长的路要走。

在微软的官方术语中,/GS和SAGESEH都是软件强制的数据执行保护(DEP),软件强制的DEP也可以在硬件中实现。例如,在实现了该功能的CPU中,如果数据出现在标记为“不可执行”的内存页中,则不会执行该数据。Windows SP2和Windows Server 2003现在支持这些技术。目前市面上大部分32位CPU和所有64位CPU都支持No Execute(NX)等安全增强技术。
任何好的安全系统都有多层预防措施来应对安全威胁。本文所涉及的编译器开关可以防止或降低常见编码错误带来的安全风险,并且简单易用,成本低廉。在这场没有硝烟的战争中是一个很好的解决方案,绝对值得你的方案采用。

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » VC++中利用GS开关防止缓冲区溢出

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情