如何在Windows上报告堆栈缓冲区溢出

How to report a stack buffer overrun on Windows?

本文关键字:堆栈 缓冲区 溢出 报告 Windows      更新时间:2023-10-16

在下面显示的代码中,我使用了所有记录在案的方法来检测异常并生成诊断。它使用C++try/catch关键字,用__try/__catch扩展关键字捕获SEH异常,使用Windows的AddVectoredExceptionHandler()和SetUnhandledExceptionFilter()winapi函数安装VEH/SEH筛选器。

使用Visual C++2003运行此程序:
/GS:输出"你好,世界!"并以退出代码0终止。
-GS-:输出"你好!世界!",并以退出码0终止

使用Visual C++2013运行此程序:
/GS:无输出,以退出代码1073740791
-GS-:输出"你好,世界!"并以0退出结束。

在/GS有效的情况下,如何在VS2013编译程序中生成诊断?

#include "stdafx.h"
#include <Windows.h>
#define CALL_FIRST 1  
#define CALL_LAST 0
LONG WINAPI MyVectoredHandler(struct _EXCEPTION_POINTERS *ExceptionInfo)
{
UNREFERENCED_PARAMETER(ExceptionInfo);
printf("MyVectoredHandlern");
return EXCEPTION_CONTINUE_SEARCH;
}
LONG WINAPI MyUnhandledExceptionFilter(_In_ struct _EXCEPTION_POINTERS *ExceptionInfo)
{
printf("SetUnhandledExceptionFiltern");
return EXCEPTION_CONTINUE_SEARCH;
}
void f()
{
__try
{
char p[20] = "hello,world!";
p[24] = '!';
printf("%sn", p);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
printf("f() exceptionn");
}
}
int _tmain(int argc, _TCHAR* argv[])
{
AddVectoredExceptionHandler(CALL_FIRST, MyVectoredHandler);
SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
try{
f();
}
catch (...){
printf("catched f exceptionn");
}
return 0;
}

处理堆栈缓冲区溢出检测的CRT函数__report_gsfailure()假设堆栈帧损坏是由恶意软件攻击引起的。传统上,这种恶意软件会破坏fs:[0]SEH异常过滤器(存储在堆栈帧上),以获得触发恶意软件有效载荷的异常处理程序。将数据转换为可执行代码的方法之一。

因此,CRT函数不能假设抛出异常是安全的。并且在VS2013附带的CRT中不再这样做,可以追溯到~VS2005。如果操作系统支持它,它将快速故障,如果不支持,它将确保注册的VEH/SEH异常处理程序也看不到异常。Kaboom,除非你连接了调试器,否则在没有诊断的情况下崩溃到桌面。

/SAFESEH选项可以挫败这种恶意软件攻击,因此它不像以前那么严重。如果你的代码仍处于堆栈损坏错误的阶段,并且你的应用程序不够流行,无法成为恶意软件的目标,那么你可以考虑更换CRT功能。

一定要和你的主管谈谈,考虑到你的客户要承担巨大的责任,你永远不想为此承担个人责任。历史很少告诉一个程序员的遭遇,他的代码让整个公司停业一个月。但肯定不是什么好看的东西。

将此代码粘贴到靠近main()函数的位置:

__declspec(noreturn) extern "C"
void __cdecl __report_gsfailure() {
RaiseException(STATUS_STACK_BUFFER_OVERRUN, EXCEPTION_NONCONTINUABLE, 0, nullptr);
}

并计划很快再次删除它。

所问问题没有解决方案。

在标准C++中,重写数组会导致未定义的行为,因此不能保证特定的结果。未能给出可靠的结果不是编译器的问题,这是允许的行为。

我知道没有任何实现可以保证任何特定的行为来响应溢出——VS当然不能。这并不奇怪,因为编译器不需要这样做(本质上就是未定义行为的含义)。之所以会出现这种情况,是因为通常很难可靠或一致地检测到这种情况。

这意味着检测数组溢出的唯一一致方法是在使用数组索引访问数组元素并采取适当的操作(例如,抛出一个可能被捕获的异常,而不是执行错误的操作)之前,检查数组索引是否有效。缺点是,它没有提供一种简单或可靠的方法来捕捉任意代码中的错误——除了修改所有代码以进行所需的检查之外。

我本想对接受的答案发表评论,但我刚刚加入,还没有足够的声誉。

我在Visual Studio 2017中尝试了该解决方案,并且必须进行一些更改才能编译该解决方案。

首先,我必须更改__report_gsfailure的签名,以匹配Microsoft的一个头文件,从而修复编译错误。

__declspec(noreturn) extern "C" void __cdecl __report_gsfailure(_In_ uintptr_t _StackCookie)
{
RaiseException(STATUS_STACK_BUFFER_OVERRUN, EXCEPTION_NONCONTINUABLE, 0, nullptr);
}

接下来,我遇到了一个LNK2005错误,我可以通过在项目属性的Linker->命令行中添加/FORCE:MULTIPLE来纠正这个错误。