Windows内核调试器_第1页
Windows内核调试器_第2页
Windows内核调试器_第3页
Windows内核调试器_第4页
Windows内核调试器_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Windows 内核调试器Windows 内核调试 器原理浅析SoBeIt前段时间 忽然对内核调试器实现原来发生 了兴趣,于是简单分析了一 下当前 windows 下主流内核调试器原理,并模 仿原理自己也写了个极其简 单的调试器 :WinDBGWinDBG 和用户调试器一点很大不 同是内核调试器在一台机器上 启动, 通过串口调试 另一个相联系的以 Debug 方式启 动的系统,这个系统可以是 虚拟机上的系 统,也可以是另一台机器上的 系统 (这只是微 软推荐和实现的 方法,其实象 SoftICE 这类 内核调试器可以实现单机调试 。很多人认为主 要功能都是 在 WinDBG 里实现 ,事实上并

2、不是那么一回事, windows 已经把 内核调试的机 制集成进了内核, WinDBG 、 kd 之类的内核调试器要做 的仅仅 是通过串行发 送特定格式数据包来进行联系, 比如中断系统、下断点、显 示内存数据等 等。然后把收到的数据包经 过 WinDBG 处理显示 出来。在进一步 介绍 WinDBG 之 前,先介绍两个函数:KdpTrace 、 KdpStub , 我在 windows 异常处理流程 一文里简单提过这两个函 数。 现在再提一下 , 当异常发生于 内核态下,会调用 KiDebugRoutine 两次,异常发生于用户态 下,会调用 KiDebugRoutine 一次,而且第一次调用

3、都是刚 开始处理异常的 时候。当 WinDBG 未被加载 时 KiDebugRoutine 为 KdpStub , 处理也很简单 , 主 要是对 由 int 0x2d引起的异 常如 DbgPrint 、 DbgPrompt 、加载卸载 SYMBOLS(关 于 int 0x2d 引起的异常将 在后面详细介绍 等,把 Context.Eip 加 1,跳过 int 0x2d后面跟着 的 int 0x3指令。真正实现 了 WinDBG 功能 的函数是 KdpTrap ,它负责处理所有STATUS_BREAKPOINT和 STATUS_SINGLE_STEP(单步 异常。STATUS_BREAKPOIN

4、T的 异常包括 int 0x3、 DbgPrint 、 DbgPrompt 、加载卸 载 SYMBOLS 。 DbgPrint 的处理最 简单, KdpTrap 直接向调试 器发含有字符串 的包 。 DbgPrompt 因为是要输出并接收字 符串, 所以先将含 有字符串的包发 送出去,再陷入循环等待接 收来自调试器的含有回复字符串 的包。 SYMBOLS 的加载和卸载 通过调用 KdpReportSymbolsStateChange , int 0x3断点异常 和 int 0x1单 步异常 (这两个 异常基本上是内核调试器处理得 最多的异常 通过调 用 KdpReportExceptionSt

5、ateChange , 这两个函数很相似 , 都是通过 调用 KdpSendWaitContinue 函数。 KdpSendWaitContinue 可 以说是内核调试 器功能的大管 家,负责各个功能的分派。这个 函数向内核调试器发送要发 送的信息,比 如当前所有寄存器状态,每次单 步后我们都可以发现寄存器 的信息被更新 ,就是内核调试器接受它发出的 包含最新机器状态的包;还有 SYMBOLS 的 状态,这样加载和卸载 了 SYMBOLS 我们都能在内核调试器里 看到相应的反 应。然后 KdpSendWaitContinue 等待从内核调试器发来的包 含命令的包, 决定下一步该干什么。让我们来

6、 看看 KdpSendWaitContinue 都能干些什么 :case DbgKdReadVirtualMemoryApi:KdpReadVirtualMemory(&ManipulateState,&MessageData,ContextRecord ;break;case DbgKdReadVirtualMemory64Api:KdpReadVirtualMemory64(&ManipulateState,&MessageData,ContextReco rd;break;case DbgKdWriteVirtualMemoryApi:KdpWriteVi

7、rtualMemory(&ManipulateState,&MessageData,ContextRecor d;break;case DbgKdWriteVirtualMemory64Api:KdpWriteVirtualMemory64(&ManipulateState,&MessageData,ContextRec ord;break;case DbgKdReadPhysicalMemoryApi:KdpReadPhysicalMemory(&ManipulateState,&MessageData,ContextRecor d;break

8、;case DbgKdWritePhysicalMemoryApi:KdpWritePhysicalMemory(&ManipulateState,&MessageData,ContextReco rd;break;case DbgKdGetContextApi:KdpGetContext(&ManipulateState,&MessageData,ContextRecord; break;case DbgKdSetContextApi:KdpSetContext(&ManipulateState,&MessageData,ContextReco

9、rd; break;case DbgKdWriteBreakPointApi:KdpWriteBreakpoint(&ManipulateState,&MessageData,ContextRecord; break;case DbgKdRestoreBreakPointApi:KdpRestoreBreakpoin(&ManipulateState,&MessageData,ContextRecord ;break;case DbgKdReadControlSpaceApi:KdpReadControlSpace(&ManipulateState,&a

10、mp;MessageData,ContextRecord ;break;case DbgKdWriteControlSpaceApi:KdpWriteControlSpace(&ManipulateState,&MessageData,ContextRecord ;break;case DbgKdReadIoSpaceApi:KdpReadIoSpace(&ManipulateState,&MessageData,ContextRecord; break;case DbgKdWriteIoSpaceApi:KdpWriteIoSpace(&Manipul

11、ateState,&MessageData,ContextRecord; break;case DbgKdContinueApi:if (NT_SUCCESS(ManipulateState.u.Continue.ContinueStatus != FALSE return ContinueSuccess; else return ContinueError;break;case DbgKdContinueApi2:if (NT_SUCCESS(ManipulateState.u.Continue2.ContinueStatus != FALSE KdpGetStateChange(&

12、amp;ManipulateState,ContextRecord;return ContinueSuccess; else return ContinueError;break;case DbgKdRebootApi:KdpReboot(;break;case DbgKdReadMachineSpecificRegister:KdpReadMachineSpecificRegister(&ManipulateState,&MessageData,Con textRecord;break;case DbgKdWriteMachineSpecificRegister:KdpWri

13、teMachineSpecificRegister(&ManipulateState,&MessageData,Co ntextRecord;break;case DbgKdSetSpecialCallApi:KdSetSpecialCall(&ManipulateState,ContextRecord;break;case DbgKdClearSpecialCallsApi:KdClearSpecialCalls(;break;case DbgKdSetInternalBreakPointApi:KdSetInternalBreakpoint(&Manipul

14、ateState;break;case DbgKdGetInternalBreakPointApi:KdGetInternalBreakpoint(&ManipulateState;break;case DbgKdGetVersionApi:KdpGetVersion(&ManipulateState;break;case DbgKdCauseBugCheckApi:KdpCauseBugCheck(&ManipulateState;break;case DbgKdPageInApi:KdpNotSupported(&ManipulateState;break;

15、case DbgKdWriteBreakPointExApi:Status = KdpWriteBreakPointEx(&ManipulateState,&MessageData,ContextRecord;if (Status ManipulateState.ApiNumber = DbgKdContinueApi;ManipulateState.u.Continue.ContinueStatus = Status;return ContinueError;break;case DbgKdRestoreBreakPointExApi:KdpRestoreBreakPoint

16、Ex(&ManipulateState,&MessageData,ContextReco rd;break;case DbgKdSwitchProcessor:KdPortRestore (;ContinueStatus = KeSwitchFrozenProcessor(ManipulateState.Processor;KdPortSave (;return ContinueStatus;case DbgKdSearchMemoryApi:KdpSearchMemory(&ManipulateState, &MessageData, ContextRecor

17、d;break;读写内存、搜索 内存、设置 /恢复断 点、继续执行、重启等等, WinDBG 里的功能是不 是都能实现了 ? 呵呵。每次内核 调试器接管系统是通过调用 在 KiDispatchException 里调用 KiDebugRoutine(KdpTrace,但我们知道要让 系统执行到 KiDispatchException 必须是系统发生了异常。 而内核调试 器与被调试系统 之间只是通过 串口联系,串口只会发生中断, 并不会让系统引发异常。那 么是怎么让系 统产生一个异常呢 ? 答案就在 KeUpdateSystemTime 里,每当 发生时钟中断 后在 HalpClockInter

18、rupt 做了一些底层处理 后就会跳转到这 个函数来更新 系统时间 (因为 是跳转而不是调用,所以 在 WinDBG 断下来后回溯堆栈是不 会发现 HalpClockInterrupt 的地址的 ,是系统中调用最频 繁的几个函数 之一。在 KeUpdateSystemTime 里会判 断 KdDebuggerEnable 是否为 TRUE , 若为 TRUE 则 调用 KdPollBreakIn 判断是否有 来自内核调试器 的包含中断信 息的包,若有则调用 DbgBreakPointWithStatus ,执行一个 int 0x3指令,在异常 处理流程进入了 KdpTrace 后将根据处理不同

19、 向内核 调试器发包并 无限循环等待内核调试的回应。 现在能理解为什么在 WinDBG 里中断系统后 堆栈回溯可以依次发现KeUpdateSystemTime->RtlpBreakWithStatusInstruction, 系统停在了 int 0x3指令上 (其实 int 0x3已经执行过了,只不过 Eip 被减 了 1而已 ,实际 已经进 入 KiDispatchException->KdpTrap,将控制权交给了内核调 试器。 系统与调 试器交互的方法除了 int 0x3外 , 还有 DbgPrint 、 DbgPrompt 、 加载和卸 载 symbols , 它们共同通过

20、调用 DebugService 获得服务。NTSTATUS DebugService(ULONG ServiceClass,PVOID Arg1,PVOID Arg2NTSTATUS Status;_asm mov eax, ServiceClassmov ecx, Arg1mov edx, Arg2int 0x2dint 0x3mov Status, eaxreturn Status;ServiceClass 可以是 BEAKPOINT_PRINT(0x1、BREAKPOINT_PROMPT(0x2、 BREAKPOINT_LOAD_SYMBOLS(0x3、 BREAKPOINT_UNLOA

21、D_SYMBOLS(0x4。为什么后面要跟 个 int 0x3, M$的说法 是为了 和 int 0x3共享代码 (我没弄明白啥意思 -_-,因 为 int 0x2d的陷 阱处理程序是 做些处理后跳到 int 0x3的陷阱 处理程序中继续处理。但事 实上对这 个 int 0x3指令并没有任何处理,仅 仅是把 Eip 加 1跳过它。所 以这个 int 0x3可以换成任何字节。int 0x2d和 int 0x3生成的异常 记录结(EXCEPTION_RECORDExceptionRecord.ExceptionCode都是STATUS_BREAKPOINT(0x80000003,不同是 int 0

22、x2d产生的异常的 ExceptionRecord.NumberParameters>0且 ExceptionRecord.ExceptionInformation 对应相应的 ServiceClass 比如 BREAKPOINT_PRINT等。事实 上,在内核调试器被挂 接后,处理 DbgPrint 等 发送字符给内 核调试器不再是通过 int 0x2d陷阱服务,而 是直接发包。用 M$的话说 , 这样更安全 , 因为不用调用 KdEnterDebugger 和 KdExitDebugger 。 最后说一 下被调试系统和内核调试器之间 的通信。被调试系统和内核 调试器之间通 过串口发数

23、据包进行通信 , Com1的 IO 端口地 址为 0x3f8, Com2的 IO 端口地址为 0x2f8。在被调试系 统准备要向内核调试器发包之 前先会 调用 KdEnterDebugger 暂停其它处理器的运行 并获取 Com 端口自旋锁 (当 然,这都是对多处理器 而言的 ,并设置端口标志 为保存状态。发包结束后 调用 KdExitDebugger 恢复。每个包就象网络 上的数据包一样,包含包头和 具体内容。包 头的格式如下:typedef struct _KD_PACKET ULONG PacketLeader;USHORT PacketType;USHORT ByteCount;ULO

24、NG PacketId;ULONG Checksum; KD_PACKET, *PKD_PACKET;PacketLeader 是四个相同字节的 标识符标识发来的包,一般的 包是 0x30303030, 控制包是 0x69696969, 中断被调试系统的包是 0x62626262。 每次读一个字 节,连续读 4次来识别出包。中 断系统的包很特殊,包里数 据只有 0x62626262。包标识符后是包的大 小、类型、包 ID 、检测码等,包 头后面就是跟 具体的数据。这点和网络上传输 的包很相似。还有一些相似 的地方比如每 发一个包给调试器都会收到一 个 ACK 答复包,以确定调试器 是否收到。若

25、收到的是一 个 RESEND 包或者很 长时间没收到回应,则会再发 一次。对于向调试器发送输 出字符串、报告 SYMBOL 情况等 的包都是一接收 到 ACK 包就立 刻返回,系统恢复执行,系统的表现就是会卡那么短短一下 。 只有报告状态 的包才会等待内核调试器的每个 控制包并完成对应功能,直 到发来的包包 含继续执行的命令为止。无论发 包还是收包,都会在包的末 尾加一 个 0xaa ,表示结束。现在我们 用几个例子来看看调试流程。记得我以 前问过 jiurl 为什么 WinDBG 的单步那么慢 (相对 softICE , 他居然说没觉 得慢 ?*$&$(&(&(我 f

26、t 。现在可 以理解为什么 WinDBG 的单步和从操 作系统正常执行中断下来为什么 那么慢了。单步慢是因为每 单步一次除了 必要的处理外,还得从串行收发 包,怎么能不慢。中断系统 慢是因为只有 等到时钟中断发生执行到 KeUpdateSystemTime 后被调试系统才会接受来 自 WinDBG 的中断 包。现在我们研究一下为什么在 KiDispatchException 里不能下断点却可以用单步跟 踪KiDispatchException 的原因。 如果在 KiDispatchException 中某处下了断 点,执行到 断点时系统发生异常又重新回 到 KiDispatchExceptio

27、n 处 , 再执 行到 int 0x3,如此往复造成了死循 环,无法不能恢复原来被断 点 int 0x3所修改的代码 。但对于 int 0x1,因为它的引起是因 为 EFLAG 寄存中 TF 位 被置位 , 并且每次都自动被复 位,所以系统可以被继续执行而 不会死循环。 现在我们知道 了内部机制, 我 们就可以调用 KdXXX 函数实现一个类似 WinDBG 之类的内核调 试器, 甚至可以替换 KiDebugRoutine(KdpTrap为自己的函数 来自己实现一 个功能更强大的调试器,呵呵。SoftICESoftICE 的原理 和 WinDBG 完全不 一样。它通过替换正常系统中 的中断 处

28、理程序来获 得系统的控制权,也正因为这样 它才能够实现单机调试。它 的功能实现方 法很底层,很少依赖与 windows 给的接口函数,大部分功能 的实现都是 靠 IO 端口读写等 来完成的。SoftICE 替换 了 IDT 表中以下的中断 (陷阱 处理 程序:0x1:单步陷阱处理程 序0x2: NMI不可屏蔽中断0x3:调试陷阱处理程 序0x6:无效操作码陷阱 处理程序0xb :段不存在陷阱处 理程序0xc :堆栈错误陷阱处 理程序0xd :一般保护性错误 陷阱处理程序0xe :页面错误陷阱处 理程序0x2d :调试服务陷阱处理程序0x2e :系统服务陷阱处理程序0x31:8042键盘控制器中

29、断处 理程序0x33:串口 2(Com2中断处理程序0x34:串口 1(Com1中断处理程序0x37:并口中断处理程序0x3c :PS/2鼠标中断处理程序0x41:未使用(这是在 PIC 系统上更换 的中断。如果是 APIC 系统的话更换的中断号 有不同,但同 样是更换这些中断处理程序 其中关键 是替换了 0x3 调试陷 阱处理程序和 0x31 i8042键盘中断处理 驱动程序 (键盘是由 i8042芯 片控制的 , SoftICE 从这两个 地方获取系统的 控制权。启 动 softICE 服务后 SoftICE 除了更换 了 IDT 里的处理程序,还有几 点重要的,一 是 HOOK 了 i8

30、042prt.sys 里 的 READ_PORT_UCHAR函数,因为 在对 0x60端口读后,会改 变 0x64端口对应控 制寄存器的状态。所以在 SoftICE 的键盘中断控 制程序读了 0x60端口后并返回控制权给正常 的键盘 中断控制程序 后,不要让它再读一次。还有就 是把物理内存前 1MB 的地址 空间通过调 用 MmMapIoSpace 映射到虚拟的地址空间里,里 面包括显存物理 地址,以后重 画屏幕就通过修改映射到虚拟地 址空间的这段显存内容就行 了。如果显示 模式是彩色模式,那么显存起始地址 是 0xb8000, CRT 索引寄 存器端 口 0x3d4, CRT 数据寄 存器端

31、口 0x3d5。如果显示模式是单色 模式, 那么显存起始 地址是 0xb0000, CRT 索引寄存器端口 0x3b4, CRT 数据寄存 器端口 0x3b5。 首先写索引 寄存器选择要进行设置的显示控 制内部寄存器之 一 (r0-r17, 然后将参数写到其数据寄存器端 口。i8042键盘控制器 中断控制驱动程序在每按下一 个键和弹起一个键都 会被触发。 SoftICE 在 HOOK 了正常的键盘中 断控制程序获得系统控制权后 , 首先从 0x60端口读出按下键的 扫描码然后向 0x20端口发送通用 EOI(0x20表示中断已结 束,如果没有按下激活热键 (ctrl+d,则返回正常键盘中断 处

32、理程序。如果是按下热键则会判断控制台 (就是那个等待 输入命令的显示 代码的黑色屏 幕 是否被激活 ,未被激活的话则先激活。然后 设置 IRQ1键 盘中断的优先 级为最高,同时设置两 个 8259A 中断控制器里的中断屏蔽寄 存器 (向 0x21和 0xa1发中断 掩码, 要屏蔽哪个 中断就把哪一位设为 1 , 只 允许 IRQ1(键盘中断 、 IRQ2(中断控制器 2级联中断 ,因为 PS/2鼠 标中断 是归 8259A-2中断控制器管的,只有开 放 IRQ2才能响应来 自 8259A-2管理 的中断 、 IRQ12(PS/2鼠标中断,如果有的话 ,使系统这时只响应 这 3个 中断。新的键 盘和鼠标中断处理程序会建立一 个缓冲区,保存一定数量的 输入扫描信息 。当前面的工作都完成后会进入 一段循环代码,负责处理键 盘和鼠标输入 的扫描码缓冲区,同时不断地更 新显存的映射地址缓冲区重 画屏幕 (这

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论