您的位置:首页 > 其它

当出现ntdll!KiUserExceptionDispatcher时,如何用windbg 定位正确堆栈

2015-07-16 11:59 507 查看
某些情况下,当程序崩溃时异常没有被程序捕获,就会出现ntdll!KiUserExceptionDispatcher这种情况,这时就需要通过其他方式获取产生异常时的正确堆栈



下面为KiUserExceptionDispatcher 函数和一些相关函数写的伪代码。这个函数在NTDLL.DLL中,它是异常处理执行的起点

KiUserExceptionDispatcher( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext )
{
DWORD retValue;

// Note: If the exception is handled, RtlDispatchException() never returns
if ( RtlDispatchException( pExceptRec, pContext ) )
retValue = NtContinue( pContext, 0 );
else
retValue = NtRaiseException( pExceptRec, pContext, 0 );

EXCEPTION_RECORD excptRec2;

excptRec2.ExceptionCode = retValue;
excptRec2.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.NumberParameters = 0;

RtlRaiseException( &excptRec2 );
}

int RtlDispatchException( PEXCEPTION_RECORD pExcptRec, CONTEXT * pContext )
{
DWORD   stackUserBase;
DWORD   stackUserTop;
PEXCEPTION_REGISTRATION pRegistrationFrame;
DWORD hLog;

// Get stack boundaries from FS:[4] and FS:[8]
RtlpGetStackLimits( &stackUserBase, &stackUserTop );

pRegistrationFrame = RtlpGetRegistrationHead();

while ( -1 != pRegistrationFrame )
{
PVOID justPastRegistrationFrame = &pRegistrationFrame + 8;
if ( stackUserBase > justPastRegistrationFrame )
{
pExcptRec->ExceptionFlags |= EH_STACK_INVALID;
return DISPOSITION_DISMISS; // 0
}

if ( stackUsertop < justPastRegistrationFrame )
{
pExcptRec->ExceptionFlags |= EH_STACK_INVALID;
return DISPOSITION_DISMISS; // 0
}

if ( pRegistrationFrame & 3 )   // Make sure stack is DWORD aligned
{
pExcptRec->ExceptionFlags |= EH_STACK_INVALID;
return DISPOSITION_DISMISS; // 0
}

if ( someProcessFlag )
{
// Doesn't seem to do a whole heck of a lot.
hLog = RtlpLogExceptionHandler( pExcptRec, pContext, 0,
pRegistrationFrame, 0x10 );
}

DWORD retValue, dispatcherContext;

retValue= RtlpExecuteHandlerForException(pExcptRec, pRegistrationFrame,
pContext, &dispatcherContext,
pRegistrationFrame->handler );

// Doesn't seem to do a whole heck of a lot.
if ( someProcessFlag )
RtlpLogLastExceptionDisposition( hLog, retValue );

if ( 0 == pRegistrationFrame )
{
pExcptRec->ExceptionFlags &= ~EH_NESTED_CALL;   // Turn off flag
}

EXCEPTION_RECORD excptRec2;

DWORD yetAnotherValue = 0;

if ( DISPOSITION_DISMISS == retValue )
{
if ( pExcptRec->ExceptionFlags & EH_NONCONTINUABLE )
{
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.ExceptionNumber = STATUS_NONCONTINUABLE_EXCEPTION;
excptRec2.ExceptionFlags = EH_NONCONTINUABLE;
excptRec2.NumberParameters = 0
RtlRaiseException( &excptRec2 );
}
else
return DISPOSITION_CONTINUE_SEARCH;
}
else if ( DISPOSITION_CONTINUE_SEARCH == retValue )
{
}
else if ( DISPOSITION_NESTED_EXCEPTION == retValue )
{
pExcptRec->ExceptionFlags |= EH_EXIT_UNWIND;
if ( dispatcherContext > yetAnotherValue )
yetAnotherValue = dispatcherContext;
}
else    // DISPOSITION_COLLIDED_UNWIND
{
excptRec2.ExceptionRecord = pExcptRec;
excptRec2.ExceptionNumber = STATUS_INVALID_DISPOSITION;
excptRec2.ExceptionFlags = EH_NONCONTINUABLE;
excptRec2.NumberParameters = 0
RtlRaiseException( &excptRec2 );
}

pRegistrationFrame = pRegistrationFrame->prev;  // Go to previous frame
}

return DISPOSITION_DISMISS;
}

_RtlpExecuteHandlerForException:    // Handles exception (first time through)
MOV     EDX,XXXXXXXX
JMP     ExecuteHandler

RtlpExecutehandlerForUnwind:        // Handles unwind (second time through)
MOV     EDX,XXXXXXXX

int ExecuteHandler( PEXCEPTION_RECORD pExcptRec
PEXCEPTION_REGISTRATION pExcptReg
CONTEXT * pContext
PVOID pDispatcherContext,
FARPROC handler ) // Really a ptr to an _except_handler()

// Set up an EXCEPTION_REGISTRATION, where EDX points to the
// appropriate handler code shown below
PUSH    EDX
PUSH    FS:[0]
MOV     FS:[0],ESP

// Invoke the exception callback function
EAX = handler( pExcptRec, pExcptReg, pContext, pDispatcherContext );

// Remove the minimal EXCEPTION_REGISTRATION frame
MOV     ESP,DWORD PTR FS:[00000000]
POP     DWORD PTR FS:[00000000]

return EAX;
}

Exception handler used for _RtlpExecuteHandlerForException:
{
// If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
// assign pDispatcher context and return DISPOSITION_NESTED_EXCEPTION

return pExcptRec->ExceptionFlags & EXCEPTION_UNWIND_CONTEXT
? DISPOSITION_CONTINUE_SEARCH
: *pDispatcherContext = pRegistrationFrame->scopetable,
DISPOSITION_NESTED_EXCEPTION;
}

Exception handler used for _RtlpExecuteHandlerForUnwind:
{
// If unwind flag set, return DISPOSITION_CONTINUE_SEARCH, else
// assign pDispatcher context and return DISPOSITION_COLLIDED_UNWIND

return pExcptRec->ExceptionFlags & EXCEPTION_UNWIND_CONTEXT
? DISPOSITION_CONTINUE_SEARCH
: *pDispatcherContext = pRegistrationFrame->scopetable,
DISPOSITION_COLLIDED_UNWIND;
}
由此可知,KiUserExceptionDispatcher 函数第一个参数为异常类型,第二个参数为产生异常时的上下文记录,对应dump中的地址分别为

6b68e5d4 00000000 6b68e5ec 6b68e63c 6b68e5ec ntdll!KiUserExceptionDispatcher+0xf中的6b68e5ec 和6b68e63c ,

用.exr分析异常类型,如下所示,可知类型为com异常



接着用.cxr恢复上下文到寄存器



再用kv命令则可以看到出现异常时候的正确堆栈



参考https://support.microsoft.com/en-gb/kb/313109
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: