首页
论坛
课程
招聘
[原创][分享]异常处理机制-用户异常的分发与处理学习笔记
2020-8-26 20:22 1279

[原创][分享]异常处理机制-用户异常的分发与处理学习笔记

2020-8-26 20:22
1279

一、用户异常分发

异常记录记录之后就是分发异常

最终会调用KiDispatchException来分发异常:



接上图


用户异常分发总结

异常如果发生在内核层,处理起来比较简单,因为异常处理函数也在0环,

        不用切换堆栈,但是如果异常发生在3环,就意味着必须要切换堆栈,回到3环执行处理函数。

切换堆栈的处理方式与用户APC的执行过程几乎是一样的,惟一的区别就是

    执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,

         而异常处理时返回3环后执行的函数是KiUserExceptionDispatcher。


二、用户异常处理之VEH

VEH是一个全局链表,里面存储了处理异常的函数


(Ntdll.dll)KiUserExceptionDispatcher 函数总结

1.调用ntdll的库函数RtlDispatchException查找并执行异常处理函数(内核异常处理的RtlDispatchException是ntoskrnl.exe模块的库函数,两个函数名一样,但是是不同库的库函数)

2.如果RtlDispatchException成功处理异常,返回真,调用ZwContinue再次进入0环,修正恢复3环EIP。

3.如果RtlDispatchException未处理异常,返回假,调用ZwRaiseException进行第二轮异常分发


VEH异常处理流程总结

1.CPU捕获到异常信息

2.通过KiDispatchException分发异常(EIP=KiUserExceptionDispatcher)

3.KiUserExceptionDispatcher调用RtlDispatchException

4-1 RtDispatchException调用RtlCallVectoredExceptionHandler查找VEH(全局异常处理链表),找到了处理函数就调用并返回真

        4-1-1 RtlDispatchException返回真之后,调用ZwContinue,修复3环EIP、TrapFrame。然后通过_KiServiceExit返回3环

        4-1-2 线程再回到3环的时候,EIP就是不再是KiUserExceptionDispatche,而是修正之后的EIP了

4-2 RtDispatchException调用RtlCallVectoredExceptionHandler,没有找到处理异常的函数,返回假

        4-2-1 调用RtlpGetStackLimits,在SEH中查找异常处理函数,如果有则调用


验证实验:

//定义函数指针
typedef PVOID (NTAPI *FnAddVectoredExceptionHandler) (UL0NG,_EXCEPTION_POINTERS*);
FnAddVectoredExceptionHandler MyAddVectoredExceptionHandler ;

LONG NTAPI VectExcepHandler(PEXCEPTION_POINTERS pExcepInfo){
    ::MessageBoxA(NULL ,"VEH异常处理函数执行了","VEH异常",MB_0K);
    if( pExcepInfo->ExceptionRecord->ExceptionCode == 0xC0000094 ){    
	 pExcepInfo->ContextRecord->Eip = pExcepInfo->ContextRecord->Eip+2;//直接修改EIP,idiv ecx指令长度是2
        //pExcepInfo->ContextRecord->Ecx = 1;//修改异常点,除数ECX修改成非0即可
        return EXCEPTION_CONTINUE_EXECUTI0N;//异常已处理,返回EXCEPTION_CONTINUE_EXECUTI0N=0
    }
	return EXCEPTION_CONTINUE_SEARCH;//异常未处理,返回EXCEPTION_CONTINUE_SEARCH=-1
   }
   
int main(){
    //1、动态获取 AddVectoredExceptionHandler 函数地址,该函数可以向VEH链表中插入一个异常处理函数
    HMODULE hMyModule = GetModuleHandle("kerne132.d11");
    MyAddVectoredExceptionHandler = (FnAddVectoredExceptionHandler)::GetProcAddress(hMyModule ,"AddVectoredExceptionHandler");
    //2、参数1表示插入VEH链的头部,表示插入到VEH链的尾部
    MyAddVectoredExceptionHandler( 0, (_EXCEPTION_POINTERS*)&VectExcepHandler );
    //3、构造除零异常
    __asm{
        xor edx,edx
        xor ecx,ecx
        mov eax,0x10
        idiv ecx    //EDX:EAX除以ECX.
        }
   printf("恢复正常执行");
   getcher();
   return 0;
   }

三、用户异常之SEH

首先回顾一下TEB结构体和KPCR,他们的0x00偏移指向的都是一个NT_TIB结构体

FS在0环时指向KPCR,在3环时指向TEB,NT_TIB结构体指向的就是SEH链入口,是一个_EXCEPTION_REGISTRATION结构

了解了NT_TIB后,继续分析


验证实验:

//自己构造一个_EXCEPTION_REGISTRATION结构体:(最简单的一种,只要符合第一个成员是Next第二个成员是Handler就可以了,结构体的名字随便起,只要结构是相符的就行)
struct MyException{
    struct MyException *pNext;
    DWORD dwHandle;
    }

//Handler指向的异常处理函数原型格式如下,创建一个异常处理函数:
EXCEPTION_DISPOSITION __cdecl MyEexception_handler(
    struct _EXCEPTION_RECORD *ExceptionRecord,     //ExceptionRecord 存储异常信息:什么类型  异常产生位置
    void* EstablisherFrane,                       //MyException 结构体地址(堆栈中的_EXCEPTION_REGISTRATION结构)
    struct _CONTEXT *ContextRecord,                //Context结构体,存储了异常发生时的各种寄存器值,堆栈位置等
    void*DispatcherContext)                        //参数4先不用管
{
      ::MessageBoxA(NULL ,"SEH异常处理函数执行了" ,"SEH异常",MB_0K);
      if( ExceptionRecord->ExceptionCode == 0xC0000694 ){
          ContextRecord->Eip = ContextRecord->Eip+2;    //或者ContextRecord->Ecx = 1; 
          return ExceptionContinueExecution ;//异常成功处理,返回EExceptionContinueExecution=-1
    }
    return ExceptionContinueSearch;//异常未处理,返回ExceptionContinueSearch = -1
}


void TestException(){
    DWORD dwTemp;
    //插入异常必须在当前线程的堆栈中,在本线程的堆栈中创建_EXCEPTION_REGISTRATION结构体
    MyException nyException ;
    _asm{
        mov eax,FS:[0]
        mov dwTemp,eax
        lea ecx,nyException
        mov FS:[0],ecx
        }
    myException.pNext = (MyException*)dwTemp;
    myException.dwHandle = (DWORD)&MyEexception_handler;

    //创造异常
    asm{
        xor edx,edx
        xor ecx,ecx
        mov eax,0x10
        idiv ecx      //EDX:EAX 除以ECX
	}
    //摘除异常
    __asm{
	  mov eax,dwTemp
	  mov FS:[0],eax
	 }
    }
    
    int main(){		
	TestException();	
	getchar();	
	return 0;	
    }

总结

结合之前的内容,屡一下上面触发异常以及处理异常的步骤

1.执行到TestException函数的时候,创造了除零异常,被CPU捕捉到

2.调用0号中断处理函数,执行CommonDispatchException函数记录异常信息,如:异常发生位置,异常类型等

3.调用KiDispatchException分发异常(备份TrapFrame),此处是3环异常,会将EIP修改为KiUserExceptionDispatcher

4.返回3环,执行KiUserExceptionDispatcher:

      调用RtlDispatchException:先找VEH,VEH中没有处理函数再通过FS:[0]在当前线程堆栈中找SEH

5.通过SEH调用异常处理函数,修正ConText错误点(或修改EIP,直接跳转到正常执行点)

6.调用ZwContinue进入0环,使用ConText修复TrampFrame结构体

6.返回3环,恢复正常执行






看雪侠者千人榜,看看你上榜了吗?

最后于 2020-11-29 12:50 被三一米田编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回