首页 > 代码库 > 内核调试学习笔记

内核调试学习笔记

内核调试学习笔记

 第一次调用KdInitSystem会初始化一下全局变量:

 

1.KdPitchDebugger:布尔类型,用来表示是否显式抑制内核调试。当启动选项中包含/DEBUG选项时,这个变量会被设置为真。

2.KdDeBuggerEnable:布尔类型,用来表示内核调试是否被启用。当启动选项中包含/DEBUG或者/DEBUGPORT 而且不包含/NODEBUG时,这个变量会被设置为真。

3.KiDebugRoutine:函数指针类型,用来记录内核调试引擎的异常处理回调函数,当内核调试引擎活动时,只想KdpTrap函数,否则指向KdpStub函数。

4.KdpBreakpointTable:结构数组类型,用来记录代码断点,每一个元素为BREAKPOINT_ENTRY结构,用来描述一个断点,包括断点地址。

 

说明:1.只有当0CPU执行KiSystemStartup函数时才会调用KdInitSystem,所以他不会被KiSystemStartup多次调用。

2.不管系统是否启用内核调试,调用都会发生。

 

 

 

 

中断到调试器:

在调试引擎向调试器报告状态变化信息之前,他会调用KdEnterDebugger函数来冻结内核。直到收到调试器的恢复继续执行命令(DbgContinueApi 和 DbgContinueApi2 )后,在调用KdExitDebugger恢复内核运行

 

KdEnterDebugger调用过程:

 

 

 

 

执行KeFreezeExecutiong会调用KdDisableInterrupts禁止系统中断,如果是多核CPU,则会将当前CPU IRQL提高到HIGH_LEVEL,并且冻结所有其他CPU

 

退出调试器:

在调试引擎收到调试器的恢复继续执行命令(DbgContinueApi 和 DbgContinueApi2 )后,会调用KdExitDebugger回复内核运行。

KdExitDebugger

 

 

 

 

调试引擎如何检测到调试器发送的中断包的?

当每次按下Ctrl+BreakWinDbg),栈信息如下:

 

 

系统的KeUpdateSystemTime函数在每次更新系统时间时(时钟中断 0号中断)会检查全局变量 KdDebuggerEnable来判断内核调试引擎是否被启用,如果为真,调用KdPollBreakIn函数来查看调试器是否发送了中断命令,如果是,便调用DbgBreakPointWithStatus触发断点异常。

 

 

接收和报告异常事件

与用户态调试类似,系统的异常分发函数通过内核调试引擎发送给内核调试器。KiDispatchException函数会调用全局变量KiDebugRoutine所指向的函数,当调试引擎被启用时,KiDebugRoutine指向KdpTrap的地址,如果调试引擎未被启用,iDebugRoutine指向KdpStub的地址。

调试引擎就是通过KdpTrap函数从系统内核接收异常事件的。

 

 

调试服务

通过异常分发函数和KdpTrap/KdpReport函数想内核调试器发送信息的方法,也被复用来实现以下调试功能:

1.打印调试信息

2.征求用户输入(Prompt)

3.报告模块加载事件

4.报告模块卸载事件

 

当需要执行以上任务时,系统触发一个软件异常(编号0x2D),称为调试服务异常。

 

 

可以发现这个异常处理函数是KiDebugService

 

 

 

KiDebugService 做了一些处理工作后,跳转到KiTrap03(断点异常处理函数)。

 

ExceptionRecord结构的ExceptionInformation[0] 字段标示了这个异常结构,记录的是一个真正的断点异常,还是调试服务请求。

 

 

为了方便的调用调试服务(INT 2D),内核设计了两个简单的函数DebugServiceDebugService2,这两个函数的实现是先把参数传递给寄存器,然后执行INT 2D指令。

内核调试学习笔记