尽管本书中的很多内容都称之为“ Undocumented ”,但其中的一些内容只能通过挖掘的代码才能获取。 Windows 2000 DDK ( Device Driver Kit )提供了一个强大的调试器可以出色的完成这方面的工作。本章将从建立一个完善的调试环境开始介绍。在阅读随后的章节时,你会经常的使用内核调试器来挖掘内部的各种特性。如果你对内核调试器很是厌烦,或许你需要制作一个自己的调试工具了。因此,本章还将介绍有关 Windows 2000 调试接口的文档化和未文档化的资料,包括微软符号文件( Symbol File )的详细信息。 It features two sample libraries with companion applications that list processes, process and system modules, and various kinds of symbol information buried inside the Windows 2000 symbol files 。做为一个特殊收获,在本章结束时,你将得到首份有关 PDB ( Microsoft Program Database )的公开文档。
建立一个调试环境
“嗨,我不想调试 Windows 2000 程 序。在此之前,我想自己写一个先!”当你读到这个标题时你可能会这样大喊出来。“很对!”我说“这就是你该去做的!”但是为什么你要以建立一个调试环境开 始这次旅行呢?答案很简单:调试器是进入系统的后门。当然,这并不是调试器开发人员的主要目的。然而,当你跟踪代码的执行过程或者你的程序意外的玩完时, 任何优秀的调试器都须能够告诉你一些有用的系统信息。仅仅报告一个指向 4GB 地址空间某处的 8 位 崩溃地址,然后让你独自一人去寻找到底发生了什么,真是无法让人接受。调试器至少应该告诉你最后执行的引发错误的代码是哪个模块中的代码,而且,在理想情 况下,它还应该告诉你让你的程序玩完的那个函数的名称。因此,调试器通常必须知道比编程手册还要多的系统信息,你可以利用这些信息来研究系统的内部情况。
Windows 2000 提供了两个调试器: WinDbg.exe (发音很像“ WindBag ”,译注: WindBag 在俚语中指空话连篇的人)一个 Win32 GUI 程序和 i386kd.exe 一个提供与之等价功能的命令行模式程序。我曾经同时使用过这两个程序,最后确定 i386kd.exe 是最好的一个,因为它有一组非常强大的选项。不过,最近看来 WinDbg.exe 似乎有所改进。不过,本书中的所有例子都是与 i386kd.exe 相关的。就像你猜想的那样, i386 前缀表示目标平台( Intel 386 处理器家族,也包括 Pentium ) kd 是 Kernel Debugger (内核调试器)的缩写。 Windows 2000 内核调试器是一个非常强大的工具。比如,他知道如何使用 Windows 2000 安装光盘中的符号文件( Symbol files ),因此,可以给出系统内存中几乎任何地址的相关符号信息(这非常有价值)。而且,它还可以反编译二进制代码、将内存信息的 16 进制转储数据以多种格式显示,甚至还能显示一些内核关键结构的布局。在调试器的在线帮助中有其命令行接口的详细文档。
准备一次崩溃转储( Crash Dump )
这 些都是好消息。坏消息是你在内核调试器顺从你之前,必须做一些准备工作。第一个障碍是调试通常涉及两台独立的计算机(通过线路连接在一起),其中一台运行 调试器,另一台用于被调试。然而,如果并不需要实时调试,那么有一个简单的方法,可以不需要第二台机器。例如,如果一个有错误的程序抛出了一个未处理的异 常而引发了声名狼藉的 NT 蓝屏死机( Blue Screen Of Death, BSOD ),你可以选择保存崩溃前的内存映像到一个文件中,在重新启动后,检查这个崩溃转储( Crash Dump )文件。这项技术通常被叫做 post mortem (事后检查)在拉丁文中, post mortem 意思是“ after death ”。这种方式是本书首选方法之一。在这里,我们的主要任务是研究系统内存,在大多数情况下,内存数据是来自还在工作的系统或者来自系统崩溃前内存的一个快照( snapshot )都并不重要。然而,一些有趣的信息则需要通过内核模式的驱动程序深入正在工作的系统的内部才能观察到,这一主题被保留在后面的章节中。
一个崩溃转储( Crash Dump )只是简单将当前内存数据写入一个磁盘文件而已。因此,一个完整的崩溃转储( crash dump )文件的大小通常与系统物理内存一样大(事实上,会略微小些)。崩溃转储( Crash dump )是由内核中的一个特殊程序在处理致命错误过程中生成的。然而,这个例程( handler )并不是立即将内存数据写入目标文件中。这是个不错的处理方式,因为在系统崩溃后,磁盘文件系统可能也不能正常工作。因此,内存映像首先被复制到页面文件存储器( page file storage ),这是系统内存管理器的一部分。因此,你应该将你的页面文件大小增加到至少两倍于物理内存。两倍?一样大还不够吗?当然 — 那只够存放崩溃转储( crash dump )。要知道,在启动时,系统会尝试将崩溃转储( crash dump ) 映像复制到实际的磁盘文件,这意味着,如果系统不能及时的释放映像数据占用的页面文件,它就可能用尽所有的虚拟内存。通常,系统会处理这种情况,它会疯狂 的读写磁盘并向你抛出一个惹人厌的“虚拟内存不足”的警告。只要你预料蓝屏的概率会增大时,将页面文件设置的足够大,这将会为你节省很多时间。
到这儿,你应该打开 Windows 2000 的控制面板,改变如下的设置:
l 增加页面文件到至少两倍物理内存的大小。
l 接下来,配置系统以便当蓝屏发生时生成一个 崩溃转储( crash dump )文件。在系统属性对话框里,选择高级页,然后单击启动和恢复按钮,检查写入调试信息选项。你应该在下拉列表中选择完成内存转储选项。在转储文件对话框中输入一个文件名和路径,转储文件将会从页面文件中复制到你指定的这个文件中。 %SystemRoot%\MEMORY.DMP 是默认设置。