/GS(缓冲区安全检查)

更新:2007 年 11 月

检测某些覆盖返回地址的缓冲区溢出,这是一种利用不强制缓冲区大小限制的代码的常用技术。这是通过将安全检查插入到已编译代码中完成的。

/GS[-]

备注

默认情况下,/GS 处于打开状态。如果希望应用程序不出现安全漏洞,请使用 /GS

有关 /GS 的其他信息,请参见 Compiler Security Checks In Depth(深入介绍编译器安全检查)。

编译器在包含本地字符串缓冲区的函数中或(在 x86 上)包含异常处理功能的函数中插入检查。字符串缓冲区被定义为元素大小为一两个字节的数组(整个数组的大小至少为五个字节),或定义为使用 _alloca 分配的任何缓冲区。

在所有的平台上,编译器都插入一个 Cookie,以便在函数具有本地字符串缓冲区时保护函数的返回地址。在以下情况下检查此 Cookie:在退出函数时;在 64 位操作系统或 x86 上针对具有某种异常处理的函数展开帧的过程中。在 x86 上,编译器还插入一个 Cookie 以保护函数的异常处理程序的地址。在展开帧的过程中会检查此 Cookie。

/GS 主要试图检测进入返回地址的直接缓冲区溢出。通过调用约定将函数调用的返回地址存储到堆栈上,可以更容易地在计算机上利用缓冲区溢出。例如,x86 使用调用约定将函数调用的返回地址存储到堆栈上。

对于编译器认为容易出现缓冲区溢出问题的函数,编译器将在堆栈上返回地址之前分配空间。在进入函数时,用安全 Cookie(它在模块加载时计算一次)加载分配的空间。然后,在退出函数时,将调用 helper 函数,以确保 Cookie 值仍保持不变。

如果值不相同,则可能已覆盖堆栈,该进程也只能结束。在 Visual C++ 2005 之前,将出现一个对话框,报告堆栈覆盖情况。

/GS 还可以防止向函数传入易受攻击的参数。易受攻击的参数可以是一个指针、C++ 引用、或包含指针、字符串缓冲区或 C++ 引用的 C 结构(C++ POD 类型)。

易受攻击的参数在 Cookie 和局部变量之前分配。缓冲区溢出可以覆盖这些参数。使用这些参数的函数中的代码可能在函数返回前就导致攻击,从而避开安全检查。若要尽量降低这种危险,编译器需要在函数 prolog 期间复制易受攻击的参数,并将它们置于所有缓冲区存储区域的下方。

在以下情况中,编译器不会对易受攻击的参数提供安全保护:

  • 函数不包含缓冲区。

  • 如果未启用优化 ( /O 选项(优化代码))。

  • 函数具有可变参数列表 (...)。

  • 函数标记为 naked (C++)

  • 函数的第一行语句包含内联程序集代码。

  • 如果仅通过在缓冲区溢出事件中不太可能利用的方式使用参数。

/GS 需要初始化安全 Cookie。在运行使用此 Cookie 的任何函数之前,必须初始化此 Cookie。在进入 EXE 或 DLL 时,安全 Cookie 必须进行初始化。在使用默认的 CRT 入口点(mainCRTStartup、wmainCRTStartup、WinMainCRTStartup、wWinMainCRTStartup 或 _DllMainCRTStartup)时,初始化是自动完成的,但是,如果使用备用入口点,则必须通过调用 __security_init_cookie 来手动完成初始化。

使用 /clr(公共语言运行库编译) 进行编译时,托管函数支持 /GS

/GS 不能抵御所有缓冲区溢出安全攻击。例如,如果对象中有缓冲区和 vtable,则缓冲区溢出可能损坏该 vtable,从而导致攻击的发生。

即使您使用 /GS,也应尽量编写安全的代码。也就是说,应确保代码没有缓冲区溢出。/GS 可以保护您的应用程序,使其避免出现代码中仍存在的缓冲区溢出。

在 Visual Studio 开发环境中设置此编译器选项

  1. 打开项目的“属性页”对话框。有关详细信息,请参见如何:打开项目属性页

  2. 单击“C/C++”文件夹。

  3. 单击“代码生成”属性页。

  4. 修改“缓冲区安全检查”属性。

以编程方式设置此编译器选项

示例

此示例溢出缓冲区,从而导致应用程序在运行时失败。

// GS_buffer_security_check.cpp
// compile with: /c
// C6204 expected
#include <cstring>
#include <stdlib.h>
#pragma warning(disable : 4996)   // for strcpy use

// Vulnerable function
void vulnerable(const char *str) {
   char buffer[10];
   strcpy(buffer, str); // overrun buffer !!!

   // use a secure CRT function to help prevent buffer overruns
   // truncate string to fit a 10 byte buffer
   // strncpy_s(buffer, _countof(buffer), str, _TRUNCATE);
}

int main() {
   // declare buffer that is bigger than expected
   char large_buffer[] = "This string is longer than 10 characters!!";
   vulnerable(large_buffer);
}

请参见

参考

编译器选项

设置编译器选项