当用户异常产生后,内核函数KiDispatchException并不是像处理内核异常那样在0环直接进行处理,而是修正3环EIP为KiUserExceptionDispatcher函数后就结束了。
这样,当线程再次回到3环时,将会从KiUserExceptionDispatcher函数开始执行。
(ntdll.dll) KiUserExceptionDispatcher函数分析
- 调用 RtIDispatchException 查找并执行异常处理函数
- 如果 RtIDispatchException 返回真,调用 ZwContinue 再次进入0环,但线程再次返回3环时,会从修正后的位置开始执行。
- 如果 RtIDispatchException 返回假,调用 ZwRaiseException 进行第二轮异常分发
RtlDispatchException是个库函数,内核调用与用户调用的是不一样的。
RtIDispatchException函数分析
- 查找VEH链表 (全局链表) ,如果有则调用0
- 查找SEH链表 (局部链表,在堆栈中) ,如果有则调用
无论那一条线程出现了异常都会先找VEH因为他是全局的,如果VEH链表中没有才会找SEH。
VEH异常的处理流程
- CPU捕获昇常信息
- 通过KiDispatchException进行分发(EIP=KiUserExceptionDispatcher)
- KiUserExceptionDispatcher调用RtIDispatchException.
- RtIDispatchException查找VEH处理函数链表并调用相关处理函数
- 代码返回到KiUserExceptionDispatcher
- 调用ZwContinue再次进入0环(ZwContinue调用NtContinue,主要作用就是恢复 TRAPFRAME然后通过_KiServiceExit返回到3环)。
- 线程再次返回3环后,从修正后的位置开始执行
参数代码
typedef PVOID(NTAPI *FnAddVectoredExceptionHandler)(ULONG, _EXCEPTION_POINTERS*);
FnAddVectoredExceptionHandler MyAddVectoredExceptionHandler;
LONG NTAPI VectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
MessageBox(NULL,L"VEH异常处理函数执行了...",L"VEH异常",MB_OK);
if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xC0000094)//除0异常
{
//修改发生异常的代码的Eip
pExcepInfo->ContextRecord->Eip = pExcepInfo->ContextRecord->Eip + 2;//产生异常的代码2字节
pExcepInfo->ContextRecord->Edx = 100;
return EXCEPTION_CONTINUE_EXECUTION;//已处理
}
return EXCEPTION_CONTINUE_SEARCH;//未处理
}
int main()
{
//动态获取AddVectoredExceptionHandler函数地址
HMODULE hModule = GetModuleHandle(L"Kernel32.dll");
MyAddVectoredExceptionHandler = (FnAddVectoredExceptionHandler)::GetProcAddress(hModule,"AddVectoredExceptionHandler");
//参数1表示插入VEH链的头部, 0插入到VEH链的尾部
MyAddVectoredExceptionHandler(0, (_EXCEPTION_POINTERS *)&VectExcepHandler);
//结构除0异常
int val = 0;
_asm
{
xor edx, edx
xor ecx, ecx
mov eax, 100
idiv ecx //edx = eax / ecx
mov val, edx
}
printf("val = %d\n",val);
getchar();
}