本帖最后由 Print动 于 2021-1-25 20:50 编辑
****************本帖为个人学习笔记,若有错误内容,请各位大佬指点*************** 异常与调试是紧密相连的。 异常记录:1.要记录异常信息 2.异常是什么类型 3.异常是在什么位置发生的 异常的分发:上面的信息记录下来以后,寻找处理异常的函数。 这个过程为异常的分发 异常处理:最后找到异常处理函数并调用(异常处理) 异常记录,异常分发,异常处理。
异常的分类:(本质上大体的分类) 1. CPU产生的异常 一定是CPU发现的 2. 软件模拟产生的异常 高级语言模拟产生的异常 CPU异常的产生:CPU指令检测到异常(例如:除0) --> 查IDT表,执行中断处理函数 --> CommonDispatchException(把异常相关的信息存到一个结构体) --> KIDispatchException (3环进0环,先保存现场) 异常信息存储到了下面这个结构体里面: typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; //异常代码 DWORD ExceptionFlags; //异常状态 struct _EXCEPTION_RECORD *ExceptionRecord; //下一个异常 PVOID ExceptionAddress; //异常发生地址 DWORD NumberParameters; //附加参数个数 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针 } EXCEPTION_RECORD; ExceptionFlags: CPU产生的异常,这个值是0,软件产生的异常,这里存的值是1 _EXCEPTION_RECORD *ExceptionRecord: 通常是空的,出现嵌套异常的情况下,就是这个异常的处理程序执行的时候,又发生异常了,会通过这个指针指向下一个异常。 DWORD NumberParameters; //附加参数个数 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针 用来进一步描述异常的信息,用的不多。
在上面那个除0异常例子中: CommonDispatchException(它要做的事就事把这些值存到在堆栈中构建的结构体)的反汇编中 Mov ebx,[ebp+68h]: [ebp+68h]指向的是eip,eip记录的就是异常发生的地址ExceptionAddress Mov eax,0C0000094h: 0C0000094h代表的是异常的类型,不同的异常有不同的值,这个值是微软定义的。 总结:CPU异常执行的流程: 1. CPU指令检测到异常 2. 查IDT表,执行中断处理程序(它并不会直接处理异常,把机会先留给程序员了) 3. 调用CommonDispatchException(构建EXCEPTION_RECORD) 4. KIDispatchException(分发异常:目的找到异常处理的函数) 模拟异常的产生:CxxThrowException --> (KERNEL32.DLL)RaiseException(DWORD dwExceptionCode,DWORD dwExceptionFlags,DWORD nNumberOfArguments,const ULONG_PTR *lpArfuments) --> NTDLL.DLL!RtlRaiseException() --> NT!NtRaiseException --> NT!KiRaiseException
(KERNEL32.DLL)RaiseException分析:1.将参数中的值填充到EXCEPTION_RECORD这个结构体中 typedef struct _EXCEPTION_RECORD { DWORD ExceptionCode; //异常代码 DWORD ExceptionFlags; //异常状态 struct _EXCEPTION_RECORD *ExceptionRecord; //下一个异常 PVOID ExceptionAddress; //异常发生地址 DWORD NumberParameters; //附加参数个数 ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针 } EXCEPTION_RECORD; 与CPU异常产生流程的两点不同: 1. CPU中ExceptionCode每种不同类型的异常,都对应不同的32位的值。软件模拟异常中的ExceptionCode 和当前的编译环境有关。相同编译环境下的值也是相同的。 2. CPU中ExceptionAddress异常会记录异常的位置,记录的是真正的异常时哪里发生的。软件模拟异常中的ExceptionAddress 存储的是一个固定的值,而这个值是RaiseException 这个函数的地址。 KiRaiseException分析:1.EXCEPTION_RECORD.ExceptionCode 最高位清0(目的是区分CPU异常与软件模拟异常) 2.调用KIDispatchException ,开始分发异常 总结:异常类型不同,仅仅是异常记录过程不同,异常进入分发阶段,就完全相同了 CPU异常 软件模拟异常 | | 查IDT表,执行中断处理函数 CxxThrowException | | CommonDispatchException (KERNEL32.DLL)RaiseException (填充ExceptionRecord结构体) (填充ExceptionRecord结构体) | | | NTDLL.DLL!RtlRaiseException() | | | NT!NtRaiseException | | | NT!KiRaiseException | ExceptionCode 最高位清0 \ /-------------------------------------/ \ / \ / \ / \ / \ / \ / \/ KIDispatchException ,开始分发异常
|