Windows内核开发-5-调试

 

开发内核也是会有调试的,但是它的调试和普通的我们在User下正常开发的调试不一样,它是直接把整个机器拿来调试,User下通常是把进程附加然后拿来调试。调试也有很多方法,比较常用的是用vs来双机调试和Windbg来双机调试,这里采用Windbg来双机调试,因为就算是vs它内部也是用的windbg来调试。

主要内容:

1:windows调试工具包

2:WinDbg

3:内核调试

4:完整的内核调试

5:内核驱动调试教程

 

1 Windows调试工具

Windows调试工具包里面包含了一组关于调试的调试器、调试工具和文档。整个工具包在你安装了SDK或WDK后会自动安装的,当然你也可以只安装这一套工具包。工具包包含了四个exe:Cdb.exe, Ntsd.Exe, Kd.exe和WinDbg.exe.

工具包作用
Cdb和Ntsd Cdb和Ntsd是用户模式,基于命令行的调试器,可以像用户模式调试器一样附加到进程里面。两个都有控制台UI。唯一的区别就是如果从命令行启动Cdb继续使用该控制台,而Ntsd会打开一个新的控制台。其它部分没区别。
Kd kd是带有命令行用户界面的内核调试器,可以附加到本地内核,或另外一台机器。
WinDbg WinDbg是唯一一个有图形用户解密的调试器,可以在User和Kernel中进行调试。

实际上用户模式和内核调试器本质上是相同的,都是基于DbgEng.dll实现的单个调试器引擎。

WinDbg的预览版也很好用,使用WinDbg Preview还是WinDbg经典界面都可以。

还有一些比较有用的工具:

Global Flag:可以设置内核和模块标记。

 

2 WinDbg

还得WinDbg才是最厉害的。其实WinDbg也就是一个图形化的可以调试内核的调试器,别的原理都一样。

WinDbg是基于命令构建的,输入命令调试器响应一个结果,使用图形化界面使得结果在一些专用窗口中显示(例如:局部变量、堆栈、线程等等)。

WinDbg支持三种命令:

命令描述
Intrinsic commands内部命令 这些命令内置于调试器里,在被调试的目标上运行。
Meta commands元命令 以英文句号(.)开头,在调试过程上运行,并不在调试的目标上运行。
Bang (extension) commands 扩展命令 以感叹号(!)开头,提供了调试器的大部分功能,所有的扩展命令都是由扩展dll来实现的,默认情况下,调试器会加载一组预定义的扩展DLL,但还可以从调试器目录或者其它来源来加载更多DLL。

2.1 用户模式调试

通常有两种办法可以启动用户模式调试:1,启动一个可执行程序并附加它,2 附加一个正在运行的程序。

实例:

1:启动notepad也就是记事本

2:启动WinDbg附加notepad(这次我用的是WinDbg 的预览版但是经典版也差不多)。

img

 

 

img

 

 

这个界面以后就是老朋友了,经常会和它一起玩的。

2.1.1 线程相关

第一个指令采用 "~"来查看被调试的进程中的所有线程信息:

0:007> ~
  0 Id: 2714.4660 Suspend: 1 Teb: 00000077`2a3a8000 Unfrozen
  1 Id: 2714.91c Suspend: 1 Teb: 00000077`2a3aa000 Unfrozen
  2 Id: 2714.57ec Suspend: 1 Teb: 00000077`2a3ac000 Unfrozen
  3 Id: 2714.4c20 Suspend: 1 Teb: 00000077`2a3ae000 Unfrozen
  4 Id: 2714.5838 Suspend: 1 Teb: 00000077`2a3b0000 Unfrozen
  5 Id: 2714.5ba4 Suspend: 1 Teb: 00000077`2a3b2000 Unfrozen
  6 Id: 2714.31ec Suspend: 1 Teb: 00000077`2a3b4000 Unfrozen
. 7 Id: 2714.53d4 Suspend: 1 Teb: 00000077`2a3b6000 Unfrozen
查看所有模块信息

第二个指令:"lm"输入来查看所有加载到这个进程的模块信息(包括DLL和exe的信息):

0:011> lm
start             end                 module name
00007ff6`f97b0000 00007ff6`f97e2000   notepad   (pdb symbols)        
00007ffa`35ff0000 00007ffa`36083000   DUser     (deferred)            
00007ffa`38220000 00007ffa`383d3000   DUI70     (deferred)            
00007ffa`42250000 00007ffa`424c0000   UIAutomationCore   (deferred)            
00007ffa`450a0000 00007ffa`45105000   oleacc     (deferred)            
00007ffa`4b240000 00007ffa`4b2ff000   msctfuimanager   (deferred)            
00007ffa`4b300000 00007ffa`4b3d7000   efswrt     (deferred)            
00007ffa`4ee90000 00007ffa`4eeab000   MPR       (deferred)            
00007ffa`51340000 00007ffa`5163e000   dwrite     (deferred)            
00007ffa`51760000 00007ffa`51a06000   iertutil   (deferred)            
00007ffa`52c60000 00007ffa`52cfe000   TextInputFramework   (deferred)            
00007ffa`52e60000 00007ffa`52f72000   MrmCoreR   (deferred)            
00007ffa`53810000 00007ffa`53a95000   COMCTL32   (deferred)            
00007ffa`56910000 00007ffa`56957000   UIAnimation   (deferred)            
00007ffa`56e80000 00007ffa`56fd3000   wintypes   (deferred)            
00007ffa`57180000 00007ffa`574aa000   CoreUIComponents   (deferred)            
00007ffa`58d90000 00007ffa`58e64000   CoreMessaging   (deferred)            
00007ffa`59230000 00007ffa`592c9000   uxtheme   (deferred)            
00007ffa`59320000 00007ffa`5957a000   twinapi_appcore   (deferred)            
00007ffa`59750000 00007ffa`59779000   RMCLIENT   (deferred)            
00007ffa`59f30000 00007ffa`59f61000   ntmarta   (deferred)            
00007ffa`5ad20000 00007ffa`5adbd000   sxs       (deferred)            
00007ffa`5aed0000 00007ffa`5aee0000   UMPDC     (deferred)            
00007ffa`5aee0000 00007ffa`5aeff000   profapi   (deferred)            
00007ffa`5af00000 00007ffa`5af4a000   powrprof   (deferred)            
00007ffa`5af50000 00007ffa`5af61000   kernel_appcore   (deferred)            
00007ffa`5af90000 00007ffa`5afb1000   win32u     (deferred)            
00007ffa`5aff0000 00007ffa`5b007000   cryptsp   (deferred)            
00007ffa`5b160000 00007ffa`5b1aa000   cfgmgr32   (deferred)            
00007ffa`5b1b0000 00007ffa`5b453000   KERNELBASE   (deferred)            
00007ffa`5b460000 00007ffa`5b5f4000   gdi32full   (deferred)            
00007ffa`5b600000 00007ffa`5b6fa000   ucrtbase   (deferred)            
00007ffa`5b700000 00007ffa`5be7f000   windows_storage   (deferred)            
00007ffa`5be80000 00007ffa`5bf00000   bcryptPrimitives   (deferred)            
00007ffa`5bf00000 00007ffa`5bf9e000   msvcp_win   (deferred)            
00007ffa`5c260000 00007ffa`5c286000   GDI32     (deferred)            
00007ffa`5c720000 00007ffa`5c7d2000   KERNEL32   (pdb symbols)          
00007ffa`5c7e0000 00007ffa`5c974000   USER32     (deferred)            
00007ffa`5ca50000 00007ffa`5cb14000   OLEAUT32   (deferred)            
00007ffa`5cb20000 00007ffa`5cb72000   shlwapi   (deferred)            
00007ffa`5cbe0000 00007ffa`5cd16000   MSCTF     (deferred)            
00007ffa`5cd20000 00007ffa`5ce40000   RPCRT4     (deferred)            
00007ffa`5ce40000 00007ffa`5cf96000   ole32     (deferred)            
00007ffa`5cfa0000 00007ffa`5d037000   sechost   (deferred)            
00007ffa`5d1d0000 00007ffa`5d272000   clbcatq   (deferred)            
00007ffa`5d280000 00007ffa`5d5b6000   combase   (export symbols)       C:\Windows\System32\combase.dll
00007ffa`5d5c0000 00007ffa`5dca5000   SHELL32   (deferred)            
00007ffa`5dcb0000 00007ffa`5dcde000   IMM32     (deferred)            
00007ffa`5dd60000 00007ffa`5de03000   advapi32   (deferred)            
00007ffa`5de70000 00007ffa`5df0e000   msvcrt     (deferred)            
00007ffa`5df10000 00007ffa`5dfb9000   shcore     (deferred)            
00007ffa`5e000000 00007ffa`5e1f0000   ntdll     (pdb symbols)          

该指令可以显示虚拟地址空间的起始和结束地址,还可以看到模块的名字的标记。标记一般分为一下4中情况:

标记作用
deferred 该标记表明在当前调试情况下从未不需要调用该模块,但是后续会用到。
pdb symbols 该标记表示该模块已经被加载了,并且会把地址展示到后面。
export symbols 表示对于该模块还没有符号来形容或者还没有被发现,相当于一个保留内容。
no symbols 试图定位该模块,但是啥也没找到。

该标记描述符有可能不一样,通常是采用微软官方的内容:,可以通过在WinDbg的Setting下的Symbol path中输入以下内容来指定:

img

 

 

 

查看当前的线程

在命令行的前面有编号,冒号右侧的数字是当前线程索引。

比如:

img

 

 

 

查看当前线程栈空间

输入k指令可以查看当前线程的栈空间:

//例如:
0:007> k
# Child-SP         RetAddr               Call Site
00 00000088`6b57f808 00007ffa`9f65ca4e     ntdll!DbgBreakPoint
01 00000088`6b57f810 00007ffa`9ef27034     ntdll!DbgUiRemoteBreakin+0x4e
02 00000088`6b57f840 00007ffa`9f5e2651     KERNEL32!BaseThreadInitThunk+0x14
03 00000088`6b57f870 00000000`00000000     ntdll!RtlUserThreadStart+0x21

这里可以看到用户模式下当前线程的调用列表。地址名字格式一般是 模块名字+!+函数调用名字+距离函数的偏移地址(偏移地址可以为0)。通过栈可以看到DbgBreakPoint函数被DbgUiRemoteBreakin调用,然后DbgUiRemoteBreakin又被BaseThreadInitThunk等等依此类推。

顺带解释下,这个Dbg相关的线程是由调试器注入的,目的是为了注入到这个进程来方便调试。

切换线程

采用 ~ns指令,n表示线程标记符,从0开始。

0:000> ~0s
win32u!NtUserGetMessage+0x14:
00007ffa`9ccd1104 c3             ret
0:000> k
# Child-SP         RetAddr               Call Site
00 00000088`6afefcd8 00007ffa`9de01b3e     win32u!NtUserGetMessage+0x14
01 00000088`6afefce0 00007ff7`c101c3ac     USER32!GetMessageW+0x2e
02 00000088`6afefd40 00007ff7`c10359b6     notepad!wWinMain+0x2b4
03 00000088`6afefdf0 00007ffa`9ef27034     notepad!__scrt_common_main_seh+0x106
04 00000088`6afefe30 00007ffa`9f5e2651     KERNEL32!BaseThreadInitThunk+0x14
05 00000088`6afefe60 00000000`00000000     ntdll!RtlUserThreadStart+0x21

这是进程的主线程,也就是第一个线程。

栈顶函数表明了线程在等待Message。

查看别的线程栈

在当前线程下查看其它线程的栈也是可行的:

~nk //n表示线程的标识符


0:000> ~3k
# Child-SP         RetAddr               Call Site
00 00000088`6b37fb98 00007ffa`9f5e2dc7     ntdll!NtWaitForWorkViaWorkerFactory+0x14
01 00000088`6b37fba0 00007ffa`9ef27034     ntdll!TppWorkerThread+0x2f7
02 00000088`6b37fea0 00007ffa`9f5e2651     KERNEL32!BaseThreadInitThunk+0x14
03 00000088`6b37fed0 00000000`00000000     ntdll!RtlUserThreadStart+0x21
查看线程列表信息

在当前命令行下查看所有线程的基本信息:

~ //直接显示当前进程的所有线程信息

0:000> ~
. 0 Id: 41c4.360c Suspend: 1 Teb: 00000088`6b119000 Unfrozen
  1 Id: 41c4.4f8 Suspend: 1 Teb: 00000088`6b11b000 Unfrozen
  2 Id: 41c4.1a14 Suspend: 1 Teb: 00000088`6b11d000 Unfrozen
  3 Id: 41c4.1348 Suspend: 1 Teb: 00000088`6b11f000 Unfrozen
  4 Id: 41c4.fbc Suspend: 1 Teb: 00000088`6b121000 Unfrozen
  5 Id: 41c4.3e74 Suspend: 1 Teb: 00000088`6b123000 Unfrozen
  6 Id: 41c4.f48 Suspend: 1 Teb: 00000088`6b125000 Unfrozen
# 7 Id: 41c4.1534 Suspend: 1 Teb: 00000088`6b127000 Unfrozen

有#的线程是最后一个断点的线程(在这里是调试器自己加的)。

img

 

 

线程基本信息由以上格式形成。

查看线程模块信息

teb是线程环境块,保存了线程中的各种信息。

采用 !teb 指令可以查看当前线程的teb信息:

0:003> !teb
TEB at 000000886b11f000
  ExceptionList:       0000000000000000
  StackBase:           000000886b380000
  StackLimit:           000000886b36f000
  SubSystemTib:         0000000000000000
  FiberData:           0000000000001e00
  ArbitraryUserPointer: 0000000000000000
  Self:                 000000886b11f000
  EnvironmentPointer:   0000000000000000
  ClientId:             00000000000041c4 . 0000000000001348
  RpcHandle:           0000000000000000
  Tls Storage:         0000000000000000
  PEB Address:         000000886b118000
  LastErrorValue:       0
  LastStatusValue:     0
  Count Owned Locks:   0
  HardErrorMode:       0

采用 !teb + 线程teb地址可以显示指定的teb:

0:003> ~
  0 Id: 41c4.360c Suspend: 1 Teb: 00000088`6b119000 Unfrozen
  1 Id: 41c4.4f8 Suspend: 1 Teb: 00000088`6b11b000 Unfrozen
  2 Id: 41c4.1a14 Suspend: 1 Teb: 00000088`6b11d000 Unfrozen
. 3 Id: 41c4.1348 Suspend: 1 Teb: 00000088`6b11f000 Unfrozen
  4 Id: 41c4.fbc Suspend: 1 Teb: 00000088`6b121000 Unfrozen
  5 Id: 41c4.3e74 Suspend: 1 Teb: 00000088`6b123000 Unfrozen
  6 Id: 41c4.f48 Suspend: 1 Teb: 00000088`6b125000 Unfrozen
# 7 Id: 41c4.1534 Suspend: 1 Teb: 00000088`6b127000 Unfrozen
0:003> !teb 00000088`6b11b000
TEB at 000000886b11b000
  ExceptionList:       0000000000000000
  StackBase:           000000886b280000
  StackLimit:           000000886b26f000
  SubSystemTib:         0000000000000000
  FiberData:           0000000000001e00
  ArbitraryUserPointer: 0000000000000000
  Self:                 000000886b11b000
  EnvironmentPointer:   0000000000000000
  ClientId:             00000000000041c4 . 00000000000004f8
  RpcHandle:           0000000000000000
  Tls Storage:         0000000000000000
  PEB Address:         000000886b118000
  LastErrorValue:       0
  LastStatusValue:     0
  Count Owned Locks:   0
  HardErrorMode:       0

teb的信息类型:

类型作用
StackBase和StackLimit 用户模式下的栈基址和线程栈的上限
ClientId 进场和线程DI
LastErrorValue 错误代码
TlsStorage 线程本地存储数组
PEB Address 进场环境块的地址
   

这种!teb命令以及类似的命令只能查看到结构体的一部分内容。

查看结构体

可以采用dt命令来查看完整的内容,比如:

0:003> dt ntdll!_teb
  +0x000 NtTib           : _NT_TIB
  +0x038 EnvironmentPointer : Ptr64 Void
  +0x040 ClientId         : _CLIENT_ID
  +0x050 ActiveRpcHandle : Ptr64 Void
  +0x058 ThreadLocalStoragePointer : Ptr64 Void
  +0x060 ProcessEnvironmentBlock : Ptr64 _PEB
  +0x068 LastErrorValue   : Uint4B
  ...
  +0x1818 ReservedForWdf   : Ptr64 Void
  +0x1820 ReservedForCrt   : Uint8B
  +0x1828 EffectiveContainerId : _GUID

WinDbg当涉及符号的时候会忽略掉大小写,Windows中的结构体基本上是以下划线:_开头,Windows喜欢这样定义。

如果查看那个模块定义了我们想要的结构体,如果有文档记录了该结构体,可以在文档中看。也可以尝试指定不带模块名称的结构,强制调试器来搜素我们想要的结构体。

如果在上一个dt ntdll!_teb 指令中指定线程的地址,就可以查看该线程对应的TEB的具体值:

0:003> ~
  0 Id: 41c4.360c Suspend: 1 Teb: 00000088`6b119000 Unfrozen
  1 Id: 41c4.4f8 Suspend: 1 Teb: 00000088`6b11b000 Unfrozen
  2 Id: 41c4.1a14 Suspend: 1 Teb: 00000088`6b11d000 Unfrozen
. 3 Id: 41c4.1348 Suspend: 1 Teb: 00000088`6b11f000 Unfrozen
  4 Id: 41c4.fbc Suspend: 1 Teb: 00000088`6b121000 Unfrozen
  5 Id: 41c4.3e74 Suspend: 1 Teb: 00000088`6b123000 Unfrozen
  6 Id: 41c4.f48 Suspend: 1 Teb: 00000088`6b125000 Unfrozen
# 7 Id: 41c4.1534 Suspend: 1 Teb: 00000088`6b127000 Unfrozen
0:003> dt ntdll!_teb 00000088`6b119000
  +0x000 NtTib           : _NT_TIB
  +0x038 EnvironmentPointer : (null)
  +0x040 ClientId         : _CLIENT_ID
  +0x17ee ClonedThread     : 0y0
  ...
  +0x1808 LockCount       : 0
  +0x180c WowTebOffset     : 0n0
  +0x1810 ResourceRetValue : 0x00000247`1aab1d30 Void
  +0x1818 ReservedForWdf   : (null)
  +0x1820 ReservedForCrt   : 0
  +0x1828 EffectiveContainerId : _GUID {00000000-0000-0000-0000-000000000000}

每个值都有距离结构体的偏移地址、名字和值的内容。简单的值直接显示,比较复杂的类似于结构体里定义结构体的就是一个超链接,点进去查看具体的内容。

 

2.1.2进制转换

Windbg下默认是采用16进制,如果要查看十进制的内容或者要把值转换成十进制可以使用 ? 问号来获取。比如:

0:003> ?  188C
Evaluate expression: 6284 = 00000000`0000188c

也可以把十进制转换为十六进制:

0:003> ? 0n34636
Evaluate expression: 34636 = 00000000`0000874c

//在十进制前面添加? 和On关键字

 

2.1.3 断点

打断点

输入以下内容给CreateFile API打一个断点:

bp kernel32!createFilew
查看断点

输入bl指令来查看断点:

0:000> bl
    0 e Disable Clear 00007ffa`9ef34b60     0001 (0001) 0:**** KERNEL32!CreateFileW

这里第一个0表示断点的位置,e表示enable可用(有enable和disable),可用点击Disable或clear的超链接来关闭(bd指令)或删除(bc指令)断点。

让程序继续运行,直到运行到断点

相当于继续让程序跑起来,和普通的ide调试是一个道理。

这里可用采用好几种办法:

1: 输入g指令来让程序跑起来

2: 单机WinDbg 的Go按钮

3: 键盘按F5

当notepad触犯到和createFileW函数有关时就会使得notepad断下来, 当程序启动后只有在遇到断点才会停下来:

Breakpoint 0 hit
KERNEL32!CreateFileW:
00007ffa`9ef34b60 ff25eac50500   jmp     qword ptr [KERNEL32!_imp_CreateFileW (00007ffa`9ef91150)] ds:00007ffa`9ef91150={KERNELBASE!CreateFileW (00007ffa`9d1e9c30)}

需要注意触发断点是在那个线程里面触发的,查看该位置的栈空间:

 

还有很多WinDbg指令,可以看WinDbg里面帮助。

** **

2.2 内核模式调试

普通用户模式的调试,采取的是给进程添加一个线程来挂起断点,作为一个调试器的线程在进程中使用。照这样来类推,对操作系统调试相当于添加一个进程来限制操作系统,所以操作系统是会被冻结的。这样的话就不能直接在本机电脑上进行调试了,不然电脑就卡住了。而且还容易出现问题。最好的办法是创建一个虚拟机,用一台主力机给一台专门用来测试的计算机调试。

img

 

 

测试机和主力机必须通过一种方式来连接,选择有很多,最好的选择是通过网络连接,但是网络连接要求target和host至少是Win8以上,Win7就很尴尬了,但是由于这个很多搞安全的要求兼容,就会很麻烦。以至于很多采用串口连接,这里我们将如何建立串口连接。

还有需要注意的就是这个操作系统的版本和位数。你要开发什么样子的就用什么样子的操作系统。比如说:你要开发一个在Win10 64位计算机上用的东西,你就装一个Win10 64位虚拟机。虽然大部分都有兼容的效果,比如在64位上能跑32位的程序,但是还是建议采用对应的操作系统版本。

搭建内核模式调试环境

这里我采用的是Win10 32位虚拟机,和Win10 64位本机,采用串口连接。

下载操作系统

这个在msdn上下载你需要的对应版本的操作系统就好:

https://msdn.itellyou.cn/,安装虚拟机如果不清楚就自行百度一下。

添加启动引导

给测试机添加启动引导,需要使用管理员模式启动命令行:

cmd管理员身份运行bcdedit

bcdedit /copy {current} /d Sna1lGo//这里的Sna1lGo是引导名称,自己随意设置。

img

 

 

bcdedit /displayorder {xxxxx-xxxx-xxx} /addlast //这里的xxx是你在设置了引导名称后的一长条字符串
                                              //这里的addlast是引导的位置,last表示是最后
           

bcdedit /dbgsettings SERIAL DEBUGPORT:1 BAUDRATE:115200 //设置调试端口波特率
bcdedit /debug {xxxxx-xxxx-xxx} ON//对新加的启动项增加调试功能
bcdedit /timeout 20//选择等待时长
bcdedit /default {xxxxx-xxxx-xxx} //选择默认引导
bcdedit /? //获得bcdedit的帮助

 

img

 

 

设置完成后,再开机就有系统启动引导了。

虚拟机设置:

添加串行端口:(如果你的虚拟机里面有打印机,打印机默认会占用一个名为//./pipe/com_1的串行端口)

img

 

 

命名随意,只要是//./pipe/xxx就行,这里我采用的是把打印机移除掉,然后命名为//./pipe/com_1。

 

WinDbg配置

给WinDbg配置当启动的时候自动连接到测试机。首先根据测试机的位数选择同样的WinDbg,这里我选择32位的WinDbg,然后弄了一个快捷方式在桌面。

img

 

 

快捷方式这里有一个目标,在这个目标的后面添加参数:

-b -k com:port=//./pipe/com_1,baud=115200,pipe

这里的参数是和前面一一对应的,如果有问题就自己找找前面的定义。然后就可以直接运行WinDbg来调试Windows了。

给WinDbg配置符号

符号就是说函数名称的配置,如果不配置函数名称不完整,大概可以先这样理解。

在WinDbg中选择Symbol Search Path(快捷键Ctrl+S):

然后输入:

srv*D:\symbol*http://msdl.microsoft.com/download/symbols

这段指令的意思是,会在D:\symbol中存储符号表,如果没有就到后面那个http:地址下载,然后存放到前面那个地址,这个存放的地址可以自己选择。然后就完美了,大功告成。

   也可以像前面那个串口一样,添加一个 -y 指令配置在快捷方式里:

-y srv*D:\symbol*http://msdl.microsoft.com/download/symbols -b -k com:port=//./pipe/com_1,baud=115200,pipe

开始内核调试:

这里采用之前在第四章写的简单完整驱动来调试。

Windows内核驱动--实现修改线程优先级demo - Sna1lGo - 博客园 (cnblogs.com)这里下载例子。

然后将生成的sys和pdb文件一起复制到虚拟机里面,然后安装驱动但是不加载驱动。参考该博客来安装驱动:Windows内核开发-2-开始内核开发-2-内核开发入门 - Sna1lGo - 博客园 (cnblogs.com)

进入WinDbg后,给我们自己写的入口函数打一个断点:

 bu prioritybooster!driverentry

这里采用的是bu断点:bu 命令是针对某个符号下断点。 比如 bu MyApp!SomeFunction 。 在代码被修改之后, 该断点可以随着函数地址改变而自动更新到最新位置。 而且bu 断点会保存在WinDbg工作空间中, 下次启动 Windbg 的时候该断点会自动设置上去。另外,在模块没有被加载的时候,bp 断点会失败(因为函数地址不存在),而bu 断点则可以成功。 新版的WinDBG中 bp失败后会自动被转成bu 。

打下断点后,我们在WinDbg命令行中输入g,让系统跑起来,然后再在系统中去加载该驱动。

如果一切顺利的话会出现下面这样:

img

 

 

img

 

 

就可以像一些平常的调试器一样来调试东西了。

输入k指令来查看堆栈:

1: kd> k
# ChildEBP RetAddr  
00 8985fad8 81dac709 PriorityBooster!DriverEntry [D:\ProjectSum\Driver\PriorityBooster\PriorityBooster.cpp @ 14]
01 8985fbc8 81e3552c nt!IopLoadDriver+0x443
02 8985fbe8 81aefa6a nt!IopLoadUnloadDriver+0x42
03 8985fc38 81abe0f0 nt!ExpWorkerThread+0xea
04 8985fc70 81b9a18d nt!PspSystemThreadStartup+0x4a
05 8985fc7c 00000000 nt!KiThreadStartup+0x15

如果断点似乎无法设置,则可能是符号问题。 执行 .reload 命令重新加载符号来查看问题是否已解决。 在用户空间设置断点也是可能的,但首先执行 .reload /user 来解决这个问题。

还可以在命令中添加-p来限制给某某进程打断点,总之WinDbg非常强大,好好学习吧。

 

总结:

WinDbg其实就是一个调试器,只不过可以调试内核。对于调试用户层的代码来说和别的没什么区别只是可能更强大官方一点。而内核模式就最好还是双机调试。搭建内核调试环境是非常重要的,因为调试可以让我们更加深入的,一步一步的看完所有代码非常好用。至于WinDbg的使用方法,大家多尝试,熟练了就好了。可以看看这本书来熟悉WinDbg:http://www.windbg.info/download/doc/pdf/WinDbg_A_to_Z_bw2.pdf