Win32结构化异常处理(SEH)探秘(下)
2010-10-15 09:07:35 来源:Web开发网未处理异常
在文章的前面,我并没有全面描述 UnhandledExceptionFilter 这个 API。通常情况下你并不直接调用它(尽管你可以这么做)。大多数情况下它都是由 KERNEL32 中进行默认异常处理的过滤器表达式代码调用。前面 BaseProcessStart 函数的伪代码已经表明了这一点。
图十三是我为 UnhandledExceptionFilter 函数写的伪代码。这个API有点奇怪(至少在我看来是这样)。如果异常的类型是 EXCEPTION_ACCESS_VIOLATION,它就调用_BasepCheckForReadOnlyResource。虽然我没有提供这个函数的伪代码,但可以简要描述一下。如果是因为要对 EXE 或 DLL 的资源节(.rsrc)进行写操作而导致的异常,_BasepCurrentTopLevelFilter 就改变出错页面正常的只读属性,以便允许进行写操作。如果是这种特殊的情况,UnhandledExceptionFilter 返回 EXCEPTION_CONTINUE_EXECUTION,使系统重新执行出错指令。
图十三 UnHandledExceptionFilter 函数的伪代码
UnhandledExceptionFilter( STRUCT _EXCEPTION_POINTERS *pExceptionPtrs )
{
PEXCEPTION_RECORD pExcptRec;
DWORD currentESP;
DWORD retValue;
DWORD DEBUGPORT;
DWORD dwTemp2;
DWORD dwUseJustInTimeDebugger;
CHAR szDbgCmdFmt[256]; // 从AeDebug这个注册表键值返回的字符串
CHAR szDbgCmdLine[256]; // 实际的调试器命令行参数(已填入进程ID和事件ID)
STARTUPINFO startupinfo;
PROCESS_INFORMATION pi;
HARDERR_STRUCT harderr; // ???
BOOL fAeDebugAuto;
TIB * pTib; // 线程信息块
pExcptRec = pExceptionPtrs->ExceptionRecord;
if ( (pExcptRec->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
&& (pExcptRec->ExceptionInformation[0]) )
{
retValue=BasepCheckForReadOnlyResource(pExcptRec->ExceptionInformation[1]);
if ( EXCEPTION_CONTINUE_EXECUTION == retValue )
return EXCEPTION_CONTINUE_EXECUTION;
}
// 查看这个进程是否运行于调试器下
retValue = NtQueryInformationProcess(GetCurrentProcess(), ProcessDebugPort,
&debugPort, sizeof(debugPort), 0 );
if ( (retValue >= 0) && debugPort ) // 通知调试器
return EXCEPTION_CONTINUE_SEARCH;
// 用户调用SetUnhandledExceptionFilter了吗?
// 如果调用了,那现在就调用他安装的异常处理程序
if ( _BasepCurrentTopLevelFilter )
{
retValue = _BasepCurrentTopLevelFilter( pExceptionPtrs );
if ( EXCEPTION_EXECUTE_HANDLER == retValue )
return EXCEPTION_EXECUTE_HANDLER;
if ( EXCEPTION_CONTINUE_EXECUTION == retValue )
return EXCEPTION_CONTINUE_EXECUTION;
// 只有返回值为EXCEPTION_CONTINUE_SEARCH时才会继续执行下去
}
// 调用过SetErrorMode(SEM_NOGPFAULTERRORBOX)吗?
{
harderr.elem0 = pExcptRec->ExceptionCode;
harderr.elem1 = pExcptRec->ExceptionAddress;
if ( EXCEPTION_IN_PAGE_ERROR == pExcptRec->ExceptionCode )
harderr.elem2 = pExcptRec->ExceptionInformation[2];
else
harderr.elem2 = pExcptRec->ExceptionInformation[0];
dwTemp2 = 1;
fAeDebugAuto = FALSE;
harderr.elem3 = pExcptRec->ExceptionInformation[1];
pTib = FS:[18h];
DWORD someVal = pTib->pProcess->0xC;
if ( pTib->threadID != someVal )
{
__try
{
char szDbgCmdFmt[256];
retValue = GetProfileStringA( "AeDebug", "Debugger", 0,
szDbgCmdFmt, sizeof(szDbgCmdFmt)-1 );
if ( retValue )
dwTemp2 = 2;
char szAuto[8];
retValue = GetProfileStringA( "AeDebug", "Auto", "0",
szAuto, sizeof(szAuto)-1 );
if ( retValue )
if ( 0 == strcmp( szAuto, "1" ) )
if ( 2 == dwTemp2 )
fAeDebugAuto = TRUE;
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
ESP = currentESP;
dwTemp2 = 1;
fAeDebugAuto = FALSE;
}
}
if ( FALSE == fAeDebugAuto )
{
retValue=NtRaiseHardError(STATUS_UNHANDLED_EXCEPTION | 0x10000000,
4, 0, &harderr,_BasepAlreadyHadHardError ? 1 : dwTemp2,
&dwUseJustInTimeDebugger );
}
else
{
dwUseJustInTimeDebugger = 3;
retValue = 0;
}
if (retValue >= 0 && (dwUseJustInTimeDebugger == 3)
&& (!_BasepAlreadyHadHardError)&&(!_BaseRunningInServerProcess))
{
_BasepAlreadyHadHardError = 1;
SECURITY_ATTRIBUTES secAttr = { sizeof(secAttr), 0, TRUE };
HANDLE hEvent = CreateEventA( &secAttr, TRUE, 0, 0 );
memset( &startupinfo, 0, sizeof(startupinfo) );
sprintf(szDbgCmdLine, szDbgCmdFmt, GetCurrentProcessId(), hEvent);
startupinfo.cb = sizeof(startupinfo);
startupinfo.lpDesktop = "Winsta0Default"
CsrIdentifyAlertableThread(); // ???
retValue = CreateProcessA( 0, // 应用程序名称
szDbgCmdLine, // 命令行
0, 0, // 进程和线程安全属性
1, // bInheritHandles
0, 0, // 创建标志、环境
0, // 当前目录
&statupinfo, // STARTUPINFO
&pi); // PROCESS_INFORMATION
if ( retValue && hEvent )
{
NtWaitForSingleObject( hEvent, 1, 0 );
return EXCEPTION_CONTINUE_SEARCH;
}
}
if ( _BasepAlreadyHadHardError )
NtTerminateProcess(GetCurrentProcess(), pExcptRec->ExceptionCode);
}
return EXCEPTION_EXECUTE_HANDLER;
}
LPTOP_LEVEL_EXCEPTION_FILTER
SetUnhandledExceptionFilter(
LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter )
{
// _BasepCurrentTopLevelFilter是KERNEL32.DLL中的一个全局变量
LPTOP_LEVEL_EXCEPTION_FILTER previous= _BasepCurrentTopLevelFilter;
// 设置为新值
_BasepCurrentTopLevelFilter = lpTopLevelExceptionFilter;
return previous; // 返回以前的值
}
赞助商链接