顺晟科技
2021-08-28 09:39:10
203
很多软件通过设置自己的异常捕捉功能来捕捉未处理的异常,并生成报告或日志(例如迷你转储文件),从而达到在Release版本下追踪bug的目的。但是在VS2005 (VC8)中,微软对CRT(C Runtime Library)的一些安全相关代码做了一些改动,比如增加了缓冲区溢出检查。在新的CRT版本中,当出现错误时,异常被强制抛出到默认调试器(如果没有配置,默认为Watson博士),而不是通知应用程序设置的异常捕获函数。这种行为主要发生在以下三种情况。
(1)调用中止函数并设置_CALL_REPORTFAULT选项(该选项在Release版本中默认设置)。
(2)启用运行时安全检查选项,在软件运行时检测到安全错误,如缓存溢出。(默认情况下,安全检查选项/GS也会打开)
(3)遇到_无效_参数错误,但应用程序没有主动调用_set_invalid_parameter_handler来设置错误捕获函数。
因此,结论是使用VS2005(VC8)编译的程序中的许多错误在SetUnhandledExceptionFilter中无法捕获。这是CRT相对于之前版本的一个很大的变化,但遗憾的是,微软并没有在相应的文档中明确指出。
解决办法
方法一:当windows进程崩溃时,禁止弹出错误对话框
程序初始化时添加以下代码。如果程序中的SetUnhandledExceptionFilter捕获到异常,则需要在SetUnhandledExceptionFilter之后添加以下代码。
set error mode(SEM _ failcriticallerrors | SEM _ nogpfaulterrobox);
_set_abort_behavior(0,_ WRITE _ ABORT _ MSG | _ CALL _ report fault);
或者
//关闭微软堆转储的噪音
_ crtstreportmode(_ CRT _ WARN,_ CRTDBG _ MODE _ FILE);
_ crtstreportfile(_ CRT _ WARN,CreateFileA('NUL ',GENERIC_WRITE,0,nullptr,OPEN_EXISTING,0,0));
set error mode(SEM _ failcriticallerrors | SEM _ nogpfaulterrobox);
//中止时禁用令人困惑的“有用”文本消息
_set_abort_behavior(0,_ WRITE _ ABORT _ MSG | _ CALL _ report fault);
_ CrtSetReportMode:将开发编译环境的报表类型设置为警告,报表的输出方式为文件输出。
_ CrtSetReportFile:创建一个空文件,并向该文件输出警告消息。警告信息已关闭。
_set_abort_behavior:处理VS环境中只有异常被强制抛出到默认调试器的问题,使用此函数将异常抛出到异常陷阱函数。SetErrorMode:控制指定类型的严重错误是否由windows或应用程序处理。
方法二:截取调用SetUnhandledExceptionFilter函数的CRT,使其无效。
应用程序无法捕获这些异常的原因是新版本的CRT实现强制删除了应用程序先前在异常处理中设置的所有捕获函数,如下所示:
/*确保删除任何已有的过滤器。*/
SetUnhandledExceptionFilter(空);
UnhandledExceptionFilter(异常指针);
解决方法是阻止CRT调用SetUnhandledExceptionFilter函数并使其无效。在X86平台下,可以使用以下代码。
#ifndef _M_IX86
#错误“以下代码仅适用于x86!”
#endif
void disablesetunhandledexception filter()
{
void * addr=(void *)GetProcAddress(LoadLibrary(_ T(' kernel 32 . dll '),' SetUnhandledExceptionFilter ');
if (addr)
{
无符号字符代码[16];
int size=0;
代码[size]=0x 33;
代码[size]=0xc 0;
代码[大小]=0xc 2;
代码[大小]=0x 04;
代码[大小]=0x 00;
DWORD dwOldFlag,dwTempFlag
VirtualProtect(addr,size,PAGE_READWRITE,dwOldFlag);
WriteProcessMemory(GetCurrentProcess(),addr,代码,大小,NULL);
VirtualProtect(addr,size,dwOldFlag,dwTempFlag);
}
}
WIN8.1上VirtualProtect报告的错误C0000005,解决方法如下:
bool AdjustPrivileges()
{
HANDLE hToken
TOKEN _ PRICES TP;
TOKEN _ PRIVILEGES oldtp
DWORD DWSize=sizeof(TOKEN _ PRIORIES);
标识符流体;
if(!OpenProcessToken(GetCurrentProcess(),TOKEN _ ADJUST _ priorities | TOKEN _ QUERY,hToken))
{
if(GetLastError()==ERROR _ CALL _ NOT _ IMPLEMENTED)返回真实的
否则返回错误的
}
if(!LookupPrivilegeValue(空,SE_DEBUG_NAME,luid))
{
关闭手柄(HToken);
返回错误的
}
ZeroMemory(tp,sizeof(TP));
tp .PrivilegeCount=1;
tp .特权[0]。流体=流体
tp .特权[0]。属性=SE _ PRIVILEGE _ ENABLED
/*调整令牌权限*/
if(!AdjustTokenPrivileges(hToken,FALSE,tp,sizeof(TOKEN_PRIVILEGES),oldtp,dwSize))
{
关闭手柄(HToken);
返回错误的
}
//关闭手柄
关闭手柄(HToken);
返回真;
}
void disablesetunhandledexception筛选器()
{
void * addr=(void *)GetProcAddress(LoadLibrary(_ T)()内核32。dll '),
setunhandledexception筛选器');
if (addr)
{
无符号字符代码[16];
int size=0;
//www.oicqzone.com
代码[大小]=0x 33;
代码[size]=0xc 0;
代码[大小]=0xc 2;
代码[大小]=0x 04;
代码[大小]=0x 00;
DWORD dwOldFlag,dwTempFlag
//提升调试权限
VirtualProtect(addr,size,PAGE_EXECUTE_READWRITE,dwOldFlag);
WriteProcessMemory(GetCurrentProcess(),addr,代码,大小,NULL);
VirtualProtect(addr,size,dwOldFlag,dwTempFlag);
}
}
在设置自己的异常处理函数后,调用disablesetunhandledexception筛选器禁止同阴极射线管设置即可。虽然也可以通过_set_abort_behavior(0,_ WRITE _ ABORT _ MSG | _ CALL _ report fault),信号(SIGABRT,), 和_set_invalid_parameter_handler(.)解决(1)(3),但是对于(2),设置美国石油学会(American Petroleum Institute)钩子是的方式。
方法三:禁止弹出"停止工作"对话框
在Win7及以后的系统中,如果一个程序发生了奔溃,系统会弹出一个“XX已停止工作"的对话框,如果不去这个窗口上点击"关闭程序",那么这个窗口会一直存在,最为关键的是,奔溃的进程并没有真正结束,还一直挂起在那里。这在自动化无人值守程序开发中是不允许的,有时候有的程序只能运行一个实例,如果奔溃的这个进程一直没有真正结束,新进程就无法启动。根据网上资料,在Windows操作系统操作系统服务管理器中关闭这个错误报告服务,仍然会弹出停止运行的对话框。最后在微软官方上找到了Windows操作系统操作系统的错误报告服务配置说明:https://msdn。微软。com/en-us/library/windows/desktop/bb 513638(v=vs . 85).文件
那如何禁止werfault窗口的弹出呢?
在栈溢出上找到一个方法,可以通过修改注册表,抑制这个错误窗口的弹出。
具体方法如下所示:
[HKEY _当前_用户\软件\微软\视窗\视窗错误报告]
Disabled '=dword:00000001
don tshowui '=dword :0000000001
[HKEY _ LOCAL _ MACHINE \ SOFTWARE \ Microsoft \ Windows \ Windows错误报告]
Disabled '=dword:00000001
don tshowui '=dword :0000000001
修改注册表后应该就可以了。
如果还有问题,可能需要重新加载注册表
1.进程中关闭explorer.exe
2.运行explorer.exe
29
2021-08
29
2021-08
28
2021-08
28
2021-08
28
2021-08
28
2021-08