本文为看雪论坛优秀文章
看雪论坛作者ID:pyikaaaa
一
CPU异常记录
① CPU指令检测到异常(例:除零)
② 调用CommonDispatchException函数
(2)这样设计异常的目的是为了程序员有机会对异常进行处理。
type struct _EXCEPTION_RECORD{DWORD ExceptionCode; //异常代码DWORD ExceptionFlags; //异常状态struct _EXCEPTION_RECORD* ExceptionRecord; //下一个异常PVOID ExceptionAddress; //异常发生地址DWORD NumberParameters; //附加参数个数ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针}
(1)CPU指令检测到异常
(2)查IDT表,执行中断处理函数
(3)调用CommonDispatchException(构建EXCEPTION_RECORD结构体)
(4)KiDispatchException(分发异常:目的是为了找到异常处理函数)
二
模拟异常记录
(1)把EXCEPTION_RECORD结构体的ExceptionCode最高位清零,用于区分CPU异常。
三
内核层用户处理流程
VOID KiDispatchException(ExceptionRecord, ExceptionFrame,TrapFrame,PreviousMode, FirstChance)KiDispatchException函数执行流程总结:
typedef struct _EXCEPTION_REGISTRATION_RECORD{struct _EXCEPTION_REGISTRATION_RECORD *Next;PEXCEPTION_ROUTINE Handler;} EXCEPTION_REGISTRATION_RECORD;
四
用户层异常处理流程
(1)上述内核层异常处理中,异常处理函数也在0环,不用切换堆栈,用户层异常发生在三环,异常处理函数也在3环,所以要切换堆栈(因为KiDispatchException在内核,从0环返到三环)回到3环执行异常处理函数。
(2)切换堆栈的处理方式与用户APC的执行过程几乎是一样的,惟一的区别就是执行用户APC时返回3环后执行的函数是KiUserApcDispatcher,而异常处理时返回3环后执行的函数是KiUserExceptionDispatcher。
(3)理解用户APC的执行过程是理解3环异常处理的关键。
(1)_KeContextFromKframes将Trap_frame被分到context为返回3环做准备
五
VEH(向量化异常处理)
作用:
typedef PVOID(NTAPI *FnAddVectoredExceptionHandler)(ULONG, _EXCEPTION_POINTERS*);FnAddVectoredExceptionHandler MyAddVectoredExceptionHandler;// VEH异常处理只能返回2个值// EXCEPTION_CONTINUE_EXECUTION 已处理// EXCEPTION_CONTINUE_SEARCH 未处理//定义VEH的异常处理函数VectExcepHandler,这个函数只能有两个返回值LONG NTAPI VectExcepHandler(PEXCEPTION_POINTERS pExcepInfo){MessageBox(NULL,L"VEH异常处理函数执行了...",L"VEH异常",MB_OK);if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xC0000094)//异常被触发,判断是否是除0异常{//1.修改发生异常的代码的Eip idiv ecx长度2字节 从下一行开始执行pExcepInfo->ContextRecord->Eip = pExcepInfo->ContextRecord->Eip + 2;//2.将除数修改为1//pExcepInfo->ContextRecord->Ecx = 1;return EXCEPTION_CONTINUE_EXECUTION;//已处理}return EXCEPTION_CONTINUE_SEARCH;//未处理}int main(){//动态获取AddVectoredExceptionHandler函数地址//AddVectoredExceptionHandler:将异常处理函数插入到VEH全局链表中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, edxxor ecx, ecxmov eax, 100idiv ecx //edx = eax / ecx//异常触发,执行写入的异常处理函数mov val, edx}printf("val = %d\n",val);getchar();}
typedef struct _EXCEPTION_POINTERS{PEXCEPTION_RECORD ExceptionRecord;//异常发生时的信息PCONTEXT ContextRecord;//异常发生时的上下文环境} EXCEPTION_POINTERS, *PEXCEPTION_POINTERS;
① CPU捕获异常
六
SEH(结构化异常处理)
typedef struct _EXCEPTION_REGISTRATION_RECORD{struct _EXCEPTION_REGISTRATION_RECORD* Next; //下一个节点,-1就是没有下一个节点了PEXCEPTION_ROUTINE Handler; //指向下一个SEH异常处理函数} EXCEPTION_REGISTRATION_RECORD;
/*SEH处理函数是提供给RtlDispatchException调用的,所以需要遵循一定的格式。EXCEPTION_DISPOSITION _cdecl MyEexception_handler(struct _EXCEPTION_RECORD *ExceptionRecord, //异常结构体PVOID EstablisherFrame, //SEH结构体地址struct _CONTEXT *ContextRecord, //存储异常发生时的各种寄存器的值 栈位置等PVOID DispatcherContext)*/
/*//0环异常处理时讲过这个结构体typedef struct _EXCEPTION_REGISTRATION_RECORD{struct _EXCEPTION_REGISTRATION_RECORD *Next;PEXCEPtiON_ROUTINE Handler;}*/struct MyException{struct MyException *prev;DWORD handler;};/*SEH处理函数是提供给RtlDispatchException调用的,所以需要遵循一定的格式。EXCEPTION_DISPOSITION _cdecl MyEexception_handler(struct _EXCEPTION_RECORD *ExceptionRecord, //异常结构体PVOID EstablisherFrame, //SEH结构体地址struct _CONTEXT *ContextRecord, //存储异常发生时的各种寄存器的值 栈位置等PVOID DispatcherContext)*/EXCEPTION_DISPOSITION __cdecl MyExceptionHandler(struct _EXCEPTION_RECORD *ExceptionRecord, //ExceptionRecord存储异常信息:什么类型、异常产生位置void * EstablisherFrame, //MyException结构体地址struct _CONTEXT *ContextRecord, //Context结构体,存储异常发生时各种寄存器的值,堆栈位置等void * Dispatchercontext){MessageBox(NULL,L"SEH异常处理函数执行了...",L"SEH异常",NULL);if (ExceptionRecord->ExceptionCode == 0xC0000094){ContextRecord->Eip = ContextRecord->Eip + 2;ContextRecord->Ecx = 100;return ExceptionContinueExecution;}return ExceptionContinueSearch;}void TestException(){DWORD temp;//插入异常,必须在当前线程的堆栈当中//若定义成全局变量则无效MyException myException;__asm{mov eax, FS:[0]mov temp, eaxlea ecx, myExceptionmov FS:[0], ecx}//链表插入操作,将原来的也挂入到插入的SEH后面myException.prev = (MyException*)temp;myException.handler = (DWORD)&MyExceptionHandler;//构造除0异常__asm{xor edx, edxxor ecx, ecxmov eax, 0x10idiv ecx //EDX:EAX 除以 ECX}//处理完成,摘掉异常__asm{mov eax, tempmov FS:[0], eax}printf("函数执行完毕\n");}int main(){TestException();return 0;}
七
编译器扩展的SEH
_try // 挂入链表{}_except(过滤表达式) // 异常过滤{异常处理程序}
except里的过滤表达式用于异常过滤,只能有以下三个值:
EXCEPTION_EXECUTE_HANDLER(1) 异常已经被识别,控制流将进入到 _except模块中运行异常处理代码。
EXCEPTION_CONTINUE_SEARCH(0) 异常不被识别,也即当前的这个 _except模块不是这个异常错误所对应的正确的异常处理模块。系统将继续到上 _try except域中继续查找一个恰当的 _except模块。
EXCEPTION_CONTINUE_EXECUTION(-1) 异常被忽略,控制流将在异常出现的点之后,继续恢复运行。
过滤表达式只能有三种写法:
_asmmov eax,FS:[0]mov temp,eaxlea ecx,myExceptionmov FS:[0],ecx}
看雪ID:pyikaaaa
https://bbs.pediy.com/user-home-921642.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!