这次我们通过一个实际调试驱动的例子,来逐步体会Windbg在内核调试中的作用.
由于条件所限,大多数情况下,很多人都是用VMware+Windbg调试内核(VMware的确是个好东西).但这样的调试需要占用大量的系统资源,对于和我一样急性子的朋友来说这是不可接受的:).利用双机调试就可以让你一边喝咖啡一边轻松的看结果,而不至于郁闷的等待每次长达数分钟的系统响应.有关双机调试的基本设置,请参考:http://www.cnblogs.com/Sonic2007/archive/2008/03/20/1114807.html
本次调试驱动所构建的环境如下:
host computer: WinXP+Windbg
Target computer: Vista SP1
driver object: sys
connect setting: 1394数据线
说明: 1.1394卡在很多机器上都已经没有了,Vista也取消了1394的数据连接协议(调试还是可以的),但不可否认的是利用1394数据线连接调试要比COM口和USB速率快很多(为什么好用的东西却得不到支持!).2.本次调试的driver是公司开发的某个软件的驱动程序,拿来尝试在Vista SP1下track.由于涉及到商业机密,本驱动源代码不便公开.3.Vista SP1就没什么好说的了,前些天才发布,MS又一个失败的典型.
OK,Let's go!
该驱动是一个类型sr.sys(MS的System Restore驱动)的Filter Driver,属于文件系统过滤驱动,加载在文件系统驱动上层,由Filter Manager负责与用户层和底层通信.连接到目标机后,按下Ctrl+break中断当前状态.(注:你也可以进入到explorer之后再中断,为了了解驱动加载时的进入点,以及系统启动时内核的装态,我们中断到这里)
Microsoft (R) Windows Debugger Version 6.6.0007.5
Copyright (c) Microsoft Corporation. All rights reserved.
Using 1394 for debugging
Opened \.DBG1394_INSTANCE01
Waiting to reconnect...
Connected to Windows Vista 6000 x86 compatible target, ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: D:symbolslocal; D:IRSystemOKRestoreDriverobjchk_wlh_x86i386
Executable search path is:
*** ERROR: Symbol file could not be found. Defaulted to export symbols for ntkrnlmp.exe -
Windows Vista Kernel Version 6000 MP (1 procs) Free x86 compatible
Built by: 6000.16584.x86fre.vista_gdr.071023-1545
Kernel base = 0x81800000 PsLoadedModuleList = 0x81908ad0
System Uptime: not available
WARNING: Whitespace at start of path element
WARNING: Whitespace at start of path element
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*
* You are seeing this message because you pressed either
* CTRL+C (if you run kd.exe) or,
* CTRL+BREAK (if you run WinDBG),
* on your debugger machine's keyboard.
*
* THIS IS NOT A BUG OR A SYSTEM CRASH
*
* If you did not intend to break into the debugger, press the "g" key, then
* press the "Enter" key now. This message might immediately reappear. If it
* does, press "g" and "Enter" again.
*
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
818355e8 cc int 3
然后,在Command line里键入lm,查看当前系统加载的模块和驱动(会发现我们的driver列在其中):
kd> lm
start end module name
80404000 80412000 PCIIDEX (deferred)
80412000 80419000 intelide (deferred)
80419000 80429000 mountmgr (deferred)
80429000 80438000 volmgr (deferred)
80438000 8045d000 pci (deferred)
8045d000 80465000 msisadrv (deferred)
80465000 8046e000 WMILIB (deferred)
8046e000 804b1000 acpi (deferred)
804b1000 804be000 WDFLDR (deferred)
804be000 80539000 Wdf01000 (deferred)
80539000 8061a000 CI (deferred)
8061a000 80655000 CLFS (deferred)
80655000 8065d000 BOOTVID (pdb symbols)
8065d000 80666000 PSHED (deferred)
80666000 806c6000 mcupdate_GenuineIntel (deferred)
806c6000 806ce000 kdcom (deferred)
81800000 81b95000 nt (pdb symbols)
81b95000 81bc9000 hal (pdb symbols)
81c06000 81c0e000 spldr (deferred)
81c0e000 81c44000 volsnap (deferred)
81c44000 81cae000 ksecdd (deferred)
81cae000 81db6000 Ntfs (deferred)
81db6000 81def000 NETIO (deferred)
81def000 81e1a000 msrpc (deferred)
81e1a000 81f1e000 ndis (deferred)
81f1e000 81f270c0 PxHelp20 (deferred)
81f28000 81f4f000 sys32v (private pdb symbols)
81f4f000 81f5f000 fileinfo (deferred)
81f5f000 81f90000 fltmgr (deferred)
81f90000 81fae000 ataport (deferred)
81fae000 81fb6000 atapi (deferred)
81fb6000 82000000 volmgrx (deferred)
8234f000 82358000 crcdisk (deferred)
82358000 82368000 agp440 (deferred)
82368000 82389000 CLASSPNP (deferred)
82389000 8239a000 disk (deferred)
8239a000 823bd000 fvevol (deferred)
823bd000 823e2000 ecache (deferred)
823e2000 823f1000 mup (deferred)
823f1000 82400000 partmgr (deferred)
注: 若符号文件没有加载成功,Windbg会提示响应的符号找不到,不过一般Windbg会自己寻找符号文件路径.实在找不到时,就包含
srv*c:symbols*http://msdl.microsoft.com/download/symbols, 然后reload一下(!reload).
另外,键入lm t n, 我们可以查看更为详细的模块及驱动信息.
然后,键入!thread和Kp,查看当前的线程详细信息和堆栈(或者Alt+6也可以看stack).注意当前thread的ID:
kd> !thread
THREAD 84254ae8 Cid 0004.0008 Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0
Not impersonating
Owning Process 84254d90 Image: System
Wait Start TickCount 0 Ticks: 1 (0:00:00:00.015)
Context Switch Count 1
UserTime 00:00:00.0000
KernelTime 00:00:00.0015
Win32 Start Address nt!Phase1Initialization (0x819433ae)
Stack Init 81c06000 Current 81c05db8 Base 81c06000 Limit 81c03000 Call 0
Priority 31 BasePriority 8 PriorityDecrement 0
ChildEBP RetAddr Args to Child
81c05af0 818aa92c 00000001 81867999 0002625a nt!RtlpBreakWithStatusInstruction (FPO: [1,0,0])
81c05af8 81867999 0002625a 00000000 00000001 nt!KdCheckForDebugBreak+0x22 (FPO: [0,0,0])
81c05b18 81836cfd 81928100 000000d1 81c05b9c nt!KeUpdateRunTime+0x270
81c05b18 81ba4130 81928100 000000d1 81c05b9c nt!KeUpdateSystemTime+0xed (FPO: [0,2] TrapFrame @ 81c05b28)
81c05b9c 81ba3fd0 81bb28a0 8181dced 81c05bc8 hal!XmGetCodeByte+0x30 (FPO: [Non-Fpo])
81c05bac 81ba40c5 81bb28a0 0000c000 00001da4 hal!XmEmulateStream+0x88 (FPO: [Non-Fpo])
81c05bc8 81ba374d 00000010 81c05c0c 8181dced hal!XmEmulateInterrupt+0x80 (FPO: [Non-Fpo])
81c05bdc 81ba0a1c 00000010 81c05c0c 00000000 hal!x86BiosExecuteInterruptShadowed+0x43 (FPO: [Non-Fpo])
81c05bf8 81ba0a5b 00000010 81c05c0c 00000000 hal!x86BiosCall+0x22 (FPO: [Non-Fpo])
81c05c2c 80656697 80806ae0 8080f438 00000000 hal!HalpBiosDisplayReset+0x25 (FPO: [Non-Fpo])
81c05c58 81b2cd6d 00000001 81b0ab01 80806ae0 BOOTVID!VidInitialize+0x135 (FPO: [Non-Fpo])
81c05c7c 81b3f098 00000001 80806ae0 00000007 nt!InbvDriverInitialize+0x81
81c05d74 819433bb 81c05dc0 819afbad 80806ae0 nt!Phase1InitializationDiscard+0xd0
81c05d7c 819afbad 80806ae0 81c0e680 00000000 nt!Phase1Initialization+0xd
81c05dc0 8189a346 819433ae 80806ae0 00000000 nt!PspSystemThreadStartup+0x9d
00000000 00000000 00000000 00000000 00000000 nt!KiThreadStartup+0x16
kd> kp
ChildEBP RetAddr
81c05af0 818aa92c nt!RtlpBreakWithStatusInstruction
81c05af8 81867999 nt!KdCheckForDebugBreak+0x22
81c05b18 81836cfd nt!KeUpdateRunTime+0x270
81c05b18 81ba4130 nt!KeUpdateSystemTime+0xed
81c05b9c 81ba3fd0 hal!XmGetCodeByte+0x30
81c05bac 81ba40c5 hal!XmEmulateStream+0x88
81c05bc8 81ba374d hal!XmEmulateInterrupt+0x80
81c05bdc 81ba0a1c hal!x86BiosExecuteInterruptShadowed+0x43
81c05bf8 81ba0a5b hal!x86BiosCall+0x22
81c05c2c 80656697 hal!HalpBiosDisplayReset+0x25
81c05c58 81b2cd6d BOOTVID!VidInitialize+0x135
81c05c7c 81b3f098 nt!InbvDriverInitialize+0x81
81c05d74 819433bb nt!Phase1InitializationDiscard+0xd0
81c05d7c 819afbad nt!Phase1Initialization+0xd
81c05dc0 8189a346 nt!PspSystemThreadStartup+0x9d
00000000 00000000 nt!KiThreadStartup+0x16
键入!process [PID] 0, 查到当前进程:
kd> !process 0004.0008 0
PROCESS 84254d90 SessionId: none Cid: 0004 Peb: 00000000 ParentCid: 0000
DirBase: 00122000 ObjectTable: 830001d0 HandleCount: 1.
Image: System
VadRoot 00000000 Vads 0 Clone 0 Private 0. Modified 0. Locked 0.
DeviceMap 00000000
Token 83003830
ElapsedTime 00:00:00.015
UserTime 00:00:00.000
KernelTime 00:00:00.000
QuotaPoolUsage[PagedPool] 0
QuotaPoolUsage[NonPagedPool] 0
Working Set Sizes (now,min,max) (4, 0, 0) (16KB, 0KB, 0KB)
PeakWorkingSetSize 0
VirtualSize 0 Mb
PeakVirtualSize 0 Mb
PageFaultCount 0
MemoryPriority BACKGROUND
BasePriority 8
CommitCharge 0
THREAD 84254ae8 Cid 0004.0008 Teb: 00000000 Win32Thread: 00000000 RUNNING on processor 0
在lm命令列出的信息中,start是模块的起始地址,通过键入"u 驱动起始地址",我们可以反汇编出它的代码:
kd> u 81f28000
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x0):
81f28000 4d dec ebp
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x1):
81f28001 5a pop edx
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x2):
81f28002 90 nop
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x3):
81f28003 0003 add byte ptr [ebx],al
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x5):
81f28005 0000 add byte ptr [eax],al
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0x7):
81f28007 000400 add byte ptr [eax+eax],al
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0xa):
81f2800a 0000 add byte ptr [eax],al
sys32v!SysAllocatePostCopyWorkItem <PERF> (sys32v+0xc):
81f2800c ff ???
逐步查找(enter),最终我们可以发现driver的入口点.这个过程其实是非常慢的,因为系统内核在加载驱动实际代码的过程中进行了N多次调用.如果我们本身有驱动的代码,也可以直接包含源代码路径,通过在实际代码中设置断点,让Windbg自己中断到相应的代码位置(在实际调试内核的过程中,这几乎是不可能的,因为你不会得到Windows内核或某个驱动程序的源代码.Linux系列的某些driver们又另当别论).这里为了方便,我包含了sys的源代码,增加断点直接走到DriverEntry例程:
kd> u 81f42780
sys32v!DriverEntry [隐藏了address]:
81f42780 8bff mov edi,edi
81f42782 55 push ebp
81f42783 8bec mov ebp,esp
81f42785 51 push ecx
81f42786 c745fc010000c0 mov dword ptr [ebp-4],0C0000001h
81f4278d a1749cf481 mov eax,dword ptr [sys32v!SysDbgFlags (81f49c74)]
81f42792 83e004 and eax,4
81f42795 7424 je sys32v!DriverEntry+0x3b (81f427bb)
其中显示的汇编代码,是内核调用驱动是进行的操作,其实也和实际代码相对应.
在driver代码中,如果要查看当前参数值,用dv命令:
kd> dv
DriverObject = 0x84663730
RegistryPath = 0x8084b560
status = 8
dontload = 0
另外,用"dt 参数名"可以看某个参数的当前值.
kd> dt DriverObject
Local var @ 0x81c05af8 Type _DRIVER_OBJECT*
0x84663730
+0x000 Type : 4
+0x002 Size : 168
+0x004 DeviceObject : (null)
+0x008 Flags : 2
+0x00c DriverStart : 0x81f28000
+0x010 DriverSize : 0x27000
+0x014 DriverSection : 0x84230a68
+0x018 DriverExtension : 0x846637d8 _DRIVER_EXTENSION
+0x01c DriverName : _UNICODE_STRING "FileSystemSys"
+0x024 HardwareDatabase : 0x81af6ed8 _UNICODE_STRING "REGISTRYMACHINEHARDWAREDESCRIPTIONSYSTEM"
+0x028 FastIoDispatch : (null)
+0x02c DriverInit : 0x81f4a005 sys32v!GsDriverEntry+0
+0x030 DriverStartIo : (null)
+0x034 DriverUnload : (null)
+0x038 MajorFunction : [28] 0x8189a5c1 nt!IopInvalidDeviceRequest+0
这样,我们就可以通过上述的这些命令,逐步分析一个驱动程序在加载和执行过程中的情况。另外,Windbg还有众多内核调试命令,如!irp可以查看一个对象的数据结构,!devobj可以查看设备对象等,在今后的操作系统内核学习过程中会不断用到这些命令。
从上面的操作过程我们可以看出,利用Windbg调试驱动程序,或者说进行内核调试,是非常方便的。如果我们对Windows内核有一定的了解,同时拥有一定汇编语言的功底,就可以有Windbg进行简单的系统排障(比如系统加载是出现蓝屏,或是某个系统模块出现问题)、驱动学习等。同时,这个过程也可以让我们更深入的理解操作系统原理。另外,Windbg也可以进行系统服务(service)的调试,这就是User mode的调试过程了。