Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html
INT 3 中断调试处理流程
一、调试器如何下INT 3 断点
1)首先,调试器使用 ReadProcessMemory,读取断点内存地址的字节。
2)之后,调试器再使用 WriteProcessMemory,将指定的内存地址指令写为0xCC。
3)之后,当程序执行到这里,遇到CC指令,会从IDT表中查03号段描述符,其会定向到Trap03函数。
4)经过一系列操作(下面会介绍),此时调试器就会接收到INT 3中断,程序暂停,用户可以用来进行一些处理。
5)恢复时,将1)读取的字节再重新写入到CC处,这样就可以保证程序的正常执行。
二、INT 3 异常指令如何派发到调试器
我们在 一、3) 中,其会经过 Trap03,因此INT3异常信息肯定是在这里发送的。
我们前面学过异常等派发,其检测到存在三环调试器时会发送到三环调试器,因此其派发也就是在这时进行的。
派发流程大体如下:
三、上面产生的两个疑问:
1)在 _kiDispatchException 中存在 dec eip,为什么调试器还需要修复 eip
注意:这个是Context.eip--,而三环与零环走的是 TrapFrame 结构。因此即使在这里 Context.eip--,调试器接收调试信息是关于TrapFrame的。
如果调试器没有处理,则异常处理回重新回到三环,调用SEH异常处理结构,这时Context.eip就起作用了。
因此,调试器手动修复 INT 3 断点时,必须将eip--,回到程序执行前。
2)为什么INT 3异常时不会走内核调试器
我们假设这种情况,利用windbg调试虚拟机,在此基础上再虚拟机中开启OD调试程序,这时你会发现,是OD接收异常,Windbg不会接收异常。
具体情况可以查看KiDispatchException函数,其调用内核调试器的代码如下
1 if ((KiDebugRoutine != NULL) && 2 ((PsGetCurrentProcess()->DebugPort == NULL && 3 !KdIgnoreUmExceptions) || 4 (KdIsThisAKdTrap(ExceptionRecord, &ContextFrame, UserMode)))) { 5 // 6 // Now dispatch the fault to the kernel debugger. 7 // 8 9 if ((((KiDebugRoutine) (TrapFrame, 10 ExceptionFrame, 11 ExceptionRecord, 12 &ContextFrame, 13 PreviousMode, 14 FALSE)) != FALSE)) { 15 16 goto Handled1; 17 } 18 }
可以查看 _KdIsThisAdktrap函数,当存在三环调试器时,对于INT 3 异常不会走内核调试器。
四、异常处理的整体流程