Visual Studio 2013如何检测缓冲区溢出

Blu*_*kMN 9 c++ memory buffer-overflow visual-c++

Visual Studio 2013 C++项目有一个 /GS切换到在运行时启用缓冲区安全检查验证.自升级到VS 2013以来,我们遇到了更多STATUS_STACK_BUFFER_OVERRUN错误,并怀疑它与改进的新编译器中缓冲区溢出检查有关.我一直在尝试验证这一点,并更好地了解如何检测缓冲区溢出.即使由语句更新的内存仅更改同一范围内堆栈上另一个局部变量的内容,也会报告缓冲区溢出这一事实让我感到困惑!因此,它必须不仅检查更改不会破坏本地变量"不拥有"的内存,而且还要检查更改不会影响除分配给单个更新语句引用的那个之外的任何本地变量.这是如何运作的?自VS 2010以来它有变化吗?

编辑: 这是一个例子,说明了Mysticial的解释未涉及的案例:

void TestFunc1();

int _tmain(int argc, _TCHAR* argv[])
{
   TestFunc1();
   return 0;
}

void TestFunc1()
{
   char buffer1[4] = ("123");
   char buffer2[4] = ("456");
   int diff = buffer1 - buffer2;
   printf("%d\n", diff);
   getchar();
   buffer2[4] = '\0';
}
Run Code Online (Sandbox Code Playgroud)

输出4指示要覆盖的内存在buffer1(紧接着之后buffer2)的范围内,但随后程序以缓冲区溢出终止.从技术上讲,它应该被认为是缓冲区溢出,但我不知道它是如何被检测到的,因为它仍然在局部变量的存储内,并没有真正破坏局部变量之外的任何东西.

带有内存布局的屏幕截图证明了这一点.步进一行后,程序因缓冲区溢出错误而中止. 带内存布局的调试器屏幕截图

我只是想相同的代码在VS 2010中,虽然调试模式陷入缓冲区溢出(带缓冲12的偏移量),在发布模式下它并没有抓住它(带缓冲的8偏移).所以我认为VS 2013收紧了/GS交换机的行为.

编辑2: 我设法用这段代码偷偷溜过VS 2013范围检查.它仍然没有检测到更新一个局部变量的尝试实际更新了另一个:

void TestFunc()
{
   char buffer1[4] = "123";
   char buffer2[4] = "456";
   int diff;
   if (buffer1 < buffer2)
   {
      puts("Sequence 1,2");
      diff = buffer2 - buffer1;
   }
   else
   {
      puts("Sequence 2,1");
      diff = buffer1 - buffer2;
   }

   printf("Offset: %d\n", diff);
   switch (getchar())
   {
   case '1':
      puts("Updating buffer 1");
      buffer1[diff] = '!';
      break;
   case '2':
      puts("Updating buffer 2");
      buffer2[diff] = '!';
      break;
   }
   getchar(); // Eat enter keypress
   printf("%s,%s\n", buffer1, buffer2);
}
Run Code Online (Sandbox Code Playgroud)

Han*_*ant 2

您会看到 /GS 机制的改进,该机制首先添加到 VS2012 中。最初 /GS 可以检测缓冲区溢出,但仍然存在一个漏洞,攻击代码可以破坏堆栈但绕过 cookie。大致是这样的:

void foo(int index, char value) {
   char buf[256];
   buf[index] = value;
}
Run Code Online (Sandbox Code Playgroud)

如果攻击者可以操纵索引的值,那么 cookie 就无济于事了。该代码现在重写为:

void foo(int index, char value) {
   char buf[256];
   buf[index] = value;
   if (index >= 256) __report_rangefailure();
}
Run Code Online (Sandbox Code Playgroud)

只是简单的索引检查。当触发时,如果没有附加调试器,会立即使用 __fastfail() 终止应用程序。背景资料在这里