一、CPU异常记录
1)CPU产生的异常 2)软件模拟产生的异常
CPU指令检测到异常(例:除0) –>查IDT表,执行中断处理函数 –>CommonDispatchException–>KiDispatchException
_KiTrap00函数分析: 1)保存现场(将寄存器堆栈位置等信息保存到_trap_frame中) 2)调用CommonDispatchException函数(分析参数EAX,EBX中的值) 为什么不把异常直接处理掉?
该函数构造一个_EXCEPTION_RECORD结构体并赋值
讯享网<span class="n">DWORD</span> <span class="n">ExceptionCode</span><span class="p">;</span> <span class="c1">//异常代码
DWORD ExceptionFlags; //异常状态 struct_EXCEPTION_RECORD* ExceptionRecord; //下一个异常 PVOID ExceptionAddress; //异常发生地址 DWORD NumberParameters; //附加参数指针 ULONG_PTR ExceptionInformation
<span class="p">[</span><span class="n">EXCEPTION_MAXIMUM_PARAMETERS</span><span class="p">];</span> <span class="c1">//附加参数个数
}
CxxThrowException –> (KERNEL32.DLL)RaiseException(DWORD dwExceptionCode, DWORD dwExceptionFlags, DWORD nNumberOfArguments, const ULONG_PTR *IpArguments) –> NTDLL.DLLIRtIRaiseException() –> NTINtRaiseException –> NT!KiRaiseException
1)填充ExceptionRecord结构体
讯享网 type struct_EXCEPTION_RECORD {
DWORD ExceptionCode; //异常代码 DWORD ExceptionFlags; //异常状态 struct_EXCEPTION_RECORD* ExceptionRecord; //下一个异常 PVOID ExceptionAddress; //异常发生地址 DWORD NumberParameters; //附加参数个数 ULONG_PTR ExceptionInformation [EXCEPTION_MAXIMUM_PARAMETERS]; //附加参数指针
}
- 调用Ntdll.dll!RtIRaiseException函数
讯享网
异常可以发生在用户空间,也可以发生在内核空间。 无论是CPU异常还是模拟异常,是用户层异常还是内核异常,都要通过 KiDispatchException函数进行分发。理解这个函数是学好异常的关键,这个函数比较复杂,本节课我们主要分析内核异常是如何分发,如何处理的。
VOID KiDispatchException(ExceptionRecord, ExceptionFrame, TrapFrame,PeviousMode,FirstChance) 1)_KeContextFromKframes将Trap_frame备份到context为返回3环做准备 2)判断先前模式PeviousMode 0是内核调用1是用户层调 3)是否是第一次机会 4)是否有内核调试器 5)如果没有或者内核调试器不处理 6)调用RtIDispatchException

讯享网typedef struct_EXCEPTION_REGISTRATION_RECORD {
<span class="n">struct_EXCEPTION_REGISTRATION_RECORD</span> <span class="o">*</span><span class="n">Next</span><span class="p">;</span> <span class="n">PEXCEPTION_ROUTINE</span> <span class="n">Handler</span><span class="p">;</span>
}EXCEPTION_REGISTRATION_RECORD;
RtIDispatchException的作用就是: 遍历异常链表,调用异常处理函数,如果异常被正确处理了,该函数返回1. 如果当前异常处理函数不能处理该异常,那么调用下一个,以此类推。 如果到最好也没有人处理这个异常,返回0。
VOID KiDispatchException(ExceptionRecord, ExceptionFrame, TrapFrame,PeviousMode,FirstChance) 1)_KeContextFromKframes将Trap_frame备份到context为返回3环做准备 2)判断先前模式PeviousMode 0是内核调用1是用户层调 3)是否是第一次机会 4)是否有内核调试器 5)发送给3环调试 6)如果3环调试器没有处理这个异常修正EIP为KiUserExceptionDispatcher 7)KiDispatchException函数执行结束: CPU异常与模拟异常返回地点不同 CPU异常: CPU检测到异常→查IDT执行处理函数–> CommonDispatchException->KiDispatchException 通过IRETD返回3环 模拟异常: CxxThrowException-RaiseException->RtiRaiseException NTINtRaiseException→>NTIKiRaiseException–>KiDispatchException 通过系统调用返回3环 8)无论通过那种方式,但线程再次回到3环时,将执行KiUserExceptionDispatcher函数
1)调用RtIDispatchException查找并执行异常处理函数 2)如果RtIDispatchException返回真,调用ZwContinue再次进入0环,但线程再次返回3环时,会从修正后的位置开始执行。 3)如果RtIDispatchException返回假,调用ZwRaiseException进行第二轮异常分发
1)查找VEH链表(全局链表),如果有则调用 2)查找SEH链表(局部链表,在堆栈中),如果有则调用 注意:与内核调用时的区别!
1)CPU捕获异常信息 2)通过KiDispatchException进行分发(EIP=KiUserExceptionDispatcher) 3)KiUserExceptionDispatcher调用RtIDispatchException 4)RtIDispatchException查找VEH处理函数链表并调用相关处理函数 5)代码返回到KiUserExceptionDispatcher 6)调用ZwContinue再次进入0环(ZwContinue调用NtContinue,主要作用就是恢复TRAP-FRAME然后通过-KiServiceExit返回到3环)。 7)线程再次返回3环后,从修正后的位置开始执行
1)FS:[0]指向SEH链表的第一个成员 2)SEH的异常处理函数必须在当前线程的堆栈中 3)只有当VEH中的异常处理函数不存在或者不处理才会到SEH链表中查找
描述:当我们程序刚开始执行时,编译器已经替我们注册了一个异常处理程序,因此叫做最后一道防线
UnhandledExceptionFilter 描述:
最后一道防线调用的异常处理程序的过滤函数 只有当它的返回值为EXCEPTION_CONTINUE_SEARCH(0)时,当前线程不存在对应的SEH 只有在当前程序处于调试状态时,才会发生上述情况 执行流程:
通过NtQueryInformationProcess查询当前进程是否正在被调试,如果是,返回EXCEPTION_CONTINUE_SEARCH,此时会进入第二轮分发 如果没有被调试: 1)查询是否通过SetUnhandledExceptionFilter注册处理函数 如果有就调用 2)如果没有通过SetUnhandledExceptionFilter注册处理函数,弹出窗口,让用户选择终止程序还是启动即时调试器 3)如果用户没有启用即时调试器,那么该函数返回EXCEPTION_EXECUTE_HANDLER,此时会执行except中的代码
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/197976.html