于博的技术Blog

Just For Fun!

导航

统计

公告

Windows内核初始化阶段的蓝屏故障调试一例

     蓝屏 (Blue Screen) 是Windows中用于提示严重的系统级错误的一种方式,因其出现时整个屏幕都被涂以蓝色而得名,如图1所示。

图1 0x7B蓝屏
 

     因为蓝屏一旦出现,Windows系统便宣告终止,只有重新启动才能恢复到桌面环境,所以蓝屏又称为蓝屏终止 (Blue Screen Of Death), 简称为BSOD。

     今天我模拟出一个在Windows初始化时的蓝屏错误,并利用WinDbg的双机内核调试功能,根据错误信息找出问题所在。

     首先要配置实验环境,这里我使用了在Virtual PC上安装的Windows2003作为debuggee,而真实的机器作为debugger,使用Virtual PC进行Windows内核调试的详细过程,请参见 http://advdbg.org/blogs/advdbg_system/articles/130.aspx

     在环境配置好后,我们可以利用Windows的注册表“制造”出一个蓝屏,具体操作请参见图2。

图2 ATAPI驱动程序的注册表值
 

     这里我解释一下ATAPI这个驱动程序,当操作系统内核被加载程序 (OS Loader) 加载至内存空间后,加载程序会把执行权移交给内核模块的入口函数,于是内核模块就开始执行了。此时,内核模块需要用ATAPI.SYS这个驱动程序来访问磁盘。由于ATAPI.SYS是由内核加载程序加载的,并且它是根据注册表中的信息来确定加载这个驱动程序的,即当内核加载程序看到Start的键值为0后,就会将这个驱动程序加载到内存,而我们将这个键值改为3,这样ATAPI.SYS就不会被内核加载程序加载了。而这里也就是蓝屏出现的地方。

     重启机器后,在Widnows初始化的时候就会发生蓝屏了,如图1所示。(从Windows XP开始,发生蓝屏后,系统默认会重启。但这是可以配置的,如果不希望重启,那么操作步骤为My Computer->Properties->Advanced->Setting for startup and Recovery,然后清除对话框中的"Automatically restart"选项)。

     问题出现了,下面我们就要解决它,这也是本文的重点。根据蓝屏停止代码0x7B查阅WinDbg的帮助文件了解它的含义:

     Bug Check 0x7B: INACCESSIBLE_BOOT_DEVICE

     The INACCESSIBLE_BOOT_DEVICE bug check has a value of 0x0000007B. This bug check indicates that the Microsoft Windows operating system has lost access to the system partition during startup.

     这正好和我们制造的蓝屏的初衷相吻合:因为ATAPI.SYS没有被OS Loader加载,内核模块当然不会正确的存取系统磁盘分区的内容了。

     在进一步,我们可以通过刚刚配置的debugger机器来作进一步的分析。

     成功建立内核调试Session后,在出现蓝屏前,调试器会收到通知:

     *** Fatal System Error: 0x0000007b
                            (0xFA0D3A98,0xC0000034,0x00000000,0x00000000)

     此时观察栈信息,便可以看到发生蓝屏的过程:

     kd> k
     ChildEBP RetAddr  
     fa0d3648 80874ad9 nt!RtlpBreakWithStatusInstruction
     fa0d3694 808758f6 nt!KiBugCheckDebugBreak+0x19
     fa0d3a2c 80875d0e nt!KeBugCheck2+0x5b2
     fa0d3a4c 80a2c178 nt!KeBugCheckEx+0x1b
     fa0d3bb4 80a15c61 nt!IopMarkBootPartition+0xf7
     fa0d3c04 80a28341 nt!IopInitializeBootDrivers+0x4bb
     fa0d3c74 80a26d66 nt!IoInitSystem+0x61b
     fa0d3da0 808ca01e nt!Phase1InitializationDiscard+0x9cf
     fa0d3dac 80905d2c nt!Phase1Initialization+0xd
     fa0d3ddc 80828499 nt!PspSystemThreadStartup+0x2e
     00000000 00000000 nt!KiThreadStartup+0x16

     这个栈的调用过程表明这个系统线程正在做执行体的初始化工作,大都是关于I/O的系统调用,在调用IopMarkBootPartition时失败了,于是IopMarkBootPartition调用KeBugCheckEx发起蓝屏,报告错误。

     蓝屏停止码的第一个参数表示引导设备的路径,使用dS命令可以显示其内容:

     kd> dS 0xfa0d3a98
     e15090b0  "\ArcName\multi(0)disk(0)rdisk(0)"
     e15090f0  "partition(1)"

     蓝屏停止码的第二个参数是IopMarkBootPartition调用ZwOpenFile打开引导设备失败的返回值。使用!error命令可以显示其含义:

     kd> !error c0000034
     Error code: (NTSTATUS) 0xc0000034 (3221225524) - Object Name not found.

     也就是没有这样的设备对象存在,无法打开,这是因为没有加载ATAPI.SYS驱动程序。

     观察系统中的进程列表,看到此时系统中只有System进程:

     kd> !process 0 0
     **** NT ACTIVE PROCESS DUMP ****
     PROCESS 81fac9e8  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
     DirBase: 00039000  ObjectTable: e1001d00  HandleCount:  33.
     Image: System

     使用lm观察模块列表,发现还没有加载普通的驱动程序,因为系统必须等到引导类型的驱动程序加载成功后,才能加载其他驱动程序。

     总结:

     分析上面这个例子,原因是由于注册表的异常,而导致加载不了ATAPI.SYS驱动程序。知道了原因后,在系统启动的过程中,按F8进入Advancd Options Menu, 选择Last known good configuration, 一般来说系统就会正常被引导了。如果还不行,可以尝试ERD Commander等工具引导和修复。

     本文得益于张银奎先生的大作《软件调试》和刊登在《程序员》杂志09年第二期上的文章《百废待兴--如何调试内核初始化阶段的故障》,在此表示感谢。

     由于工作的原因,我经常要调试一些内核态的错误,调试软件的过程,也是学习进步的过程,我不但没有感到枯燥乏味,相反从中享受到了程序无穷的乐趣,谨以此文对自己的工作和学习做个小结,并希望在此结识真正热爱软件的朋友们,我的联系方式如下:

Email & MSN :yubo_ccu@yahoo.com.cn
QQ : 253985493

  对于文章有任何不妥或有疑问, 可以发表评论, 我会竭尽所能答复各位的,谢谢.

     

 

 

 

 

posted on 2009-02-24 17:00 于博 阅读(...) 评论(...) 编辑 收藏