{
// 生成一个异常
excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_INVALID_UNWIND_TARGET; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; RtlRaiseException( &exceptRec2 ); } }
PVOID pStack = pExcptRegHead + 8; // 8 = sizeof(EXCEPTION_REGISTRATION)
// 确保pExcptRegHead在堆栈范围内,并且是4的倍数 if ( (stackUserBase <= pExcptRegHead ) && (stackUserTop >= pStack ) && (0 == (pExcptRegHead & 3)) ) {
DWORD pNewRegistHead; DWORD retValue;
&pNewRegistHead, pExceptRegHead->handler ); if ( retValue != DISPOSITION_CONTINUE_SEARCH ) {
if ( retValue != DISPOSITION_COLLIDED_UNWIND ) {
excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_INVALID_DISPOSITION; excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; RtlRaiseException( &excptRec2 ); } else
pExcptRegHead = pNewRegistHead; }
PEXCEPTION_REGISTRATION pCurrExcptReg = pExcptRegHead; pExcptRegHead = pExcptRegHead->prev; RtlpUnlinkHandler( pCurrExcptReg ); }
else // 堆栈已经被破坏!生成一个异常 {
excptRec2.ExceptionRecord = pExcptRec; excptRec2.NumberParameters = 0;
excptRec2.ExceptionCode = STATUS_BAD_STACK;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE; RtlRaiseException( &excptRec2 ); }
tehandlerForUnwind(pExcptRec, pExcptRegHead, &context,
}
// 如果执行到这里,说明已经到了EXCEPTION_REGISTRATION // 结构链表的末尾,正常情况下不应该发生这种情况。
// (因为正常情况下异常应该被处理,这样就不会到链表末尾) if ( -1 == pRegistrationFrame ) NtContinue( &context, 0 ); else
NtRaiseException( pExcptRec, &context, 0 ); }
RtlUnwind 函数的伪代码到这里就结束了,以下是它调用的几个函数的伪代码: PEXCEPTION_REGISTRATION RtlpGetRegistrationHead( void ) {
return FS:[0]; }
RtlpUnlinkHandler( PEXCEPTION_REGISTRATION pRegistrationFrame ) {
FS:[0] = pRegistrationFrame->prev; }
void RtlpCaptureContext( CONTEXT * pContext ) {
pContext->Eax = 0; pContext->Ecx = 0; pContext->Edx = 0; pContext->Ebx = 0; pContext->Esi = 0; pContext->Edi = 0; pContext->SegCs = CS; pContext->SegDs = DS; pContext->SegEs = ES; pContext->SegFs = FS; pContext->SegGs = GS; pContext->SegSs = SS;
pContext->EFlags = flags; // 它对应的汇编代码为__asm{ PUSHFD / pop [xxxxxxxx] } pContext->Eip = 此函数的调用者的调用者的返回地址 // 读者看一下这个函数的 pContext->Ebp = 此函数的调用者的调用者的EBP // 汇编代码就会清楚这一点 pContext->Esp = pContext->Ebp + 8; }
虽然RtlUnwind函数的规模看起来很大,但是如果你按一定方法把它分开,其实并不难理解。它首先从FS:[4]和FS:[8]处获取当前线程堆栈的界限。它们对于后面要进行的合法性检查非常重要,以确保所有将要被展开的异常帧都在堆栈范围内。
RtlUnwind 接 着在堆栈上创建了一个空的EXCEPTION_RECORD结构并把STATUS_UNWIND赋给它的ExceptionCode域,同时把 EXCEPTION_UNWINDING标志赋给它的ExceptionFlags域。指向这个结构的指针作为其中一个参数被传递给每个异常回调函数。然 后,这个函数调用
RtlCaptureContext函数来创建一个空的CONTEXT结构,这个结构也变成了在展开阶段调用每个异常回调函数时传递给它 们的一个参数。
RtlUnwind 函 数的其余部分遍历EXCEPTION_REGISTRATION结构链表。对于其中的每个帧,它都调用 RtlpExecuteHandlerForUnwind函数,后面我会讲到这个函数。正是这个函数带EXCEPTION_UNWINDING标志调用了 异常处理回调函数。每次回调之后,它调用RtlpUnlinkHandler移除相应的异常帧。
RtlUnwind 函 数的第一个参数是一个帧的地址,当它遍历到这个帧时就停止展开异常帧。上面所说的这些代码之间还有一些安全性检查代码,它们用来确保不出问题。如果出现任 何问题,RtlUnwind就引发一个异常,指示出了什么问题,并且这个异常带有
EXCEPTION_NONCONTINUABLE标志。当一个进程被设 置了这个标志时,它就不允许再运行,必须终止。
未处理异常
在 文章的前面,我并没有全面描述UnhandledExceptionFilter这个API。通常情况下你并不直接调用它(尽管你可以这么做)。大多数情 况下它都是由KERNEL32中进行默认异常处理的过滤器表达式代码调用。前面BaseProcessStart函数的伪代码已经表明了这一点。
图 13是我为UnhandledExceptionFilter函数写的伪代码。这个API有点奇怪(至少在我看来是这样)。如果异常的类型是 EXCEPTION_ACCESS_VIOLATION,它就调用
_BasepCheckForReadOnlyResource。虽然我没有提供这个函 数的伪代码,但可以简要描述一下。如果是因为要对EXE或DLL的资源节(.rsrc)进行写操作而导致的异 常,
_BasepCurrentTopLevelFilter就改变出错页面正常的只读属性,以便允许进行写操作。如果是这种特殊的情 况,UnhandledExceptionFilter返回EXCEPTION_CONTINUE_EXECUTION,使系统重新执行出错指令。
图13 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( \ szDbgCmdFmt, sizeof(szDbgCmdFmt)-1 ); if ( retValue )
dwTemp2 = 2; char szAuto[8];
retValue = GetProfileStringA( \ szAuto, sizeof(szAuto)-1 );
if ( retValue )
if ( 0 == strcmp( szAuto, \ 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 = \
CsrIdentifyAlertableThread(); // ???
= CreateProcessA( 0, // 应用程序名称
szDbgCmdLine, // 命令行
0, 0, // 进程和线程安全属性 1, // bInheritHandles 0, 0, // 创建标志、环境 0, // 当前目录 &statupinfo, // STARTUPINFO &pi); // PROCESS_INFORMATION if ( retValue && hEvent ) {
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说教育文库深入探索Win32结构化异常处理 - 图文(6)在线全文阅读。
相关推荐: