您的位置:首页 > 博客中心 > 数据库 >

使用WinDbg内核调试[转]

时间:2022-03-10 17:04

Technorati 标签: windbg,内核调试

WINDOWS调试工具很强大,但是学习使用它们并不容易。特别对于驱动开发者使用的WinDbg和KD这两个内核调试器(CDB和NTSD是用户态调试器)。

本教程的目标是给予一个已经有其他调试工具使用经验的开发者足够信息,使其能通过参考WINDOWS调试工具的帮助文件进行内核调试。

本文将假定开发者熟悉一般WINDOWS操作系统和进程的建立过程。

本文的重点是集成内核模式和用户态模式的图形化调试器WinDbg。KD在脚本和自动化调试中更有用,并且在资深程序员中拥有一定地位,但是本教程将集中讨论WinDbg,

只会偶尔提到KD。

本文讨论的是WindowsNT 4.0,Windows2000或以后的版本,而且目标电脑的处理器基于X86架构。对于64位平台,将不会特别提及。

总之,本教程由简单介绍调试器的安装开始,大体分成2部分,基础知识和选择技术。基础知识包括基本调试命令和常用调试命令。选择技术是其他命令和在很多情况下都

有用的调查方法。后者并不是调查象deadlocks, memory corruption或者resource leaks的唯一方法。第一次阅读本教程,你可能会跳过选择技术。你可以停止阅读本教程

而转向微软调试器讨论组,也可以通过调试器的反馈e-mai解决更多的问题。

安装程序
取得最新版!

取得最新版的调试器,并且有规律的更新它。这里并没有夸大最新版的价值,因为调试器会经常改进和修复错误。你将能在下面网址下载:

http://www.microsoft.com/whdc/devtools/debugging/default.mspx.

主机与目标之间的连接

调试器有使用null-modemcable 或者1394cable连接两台电脑的安装方案 。本教程不分析单操作系统的本地调试(即在调试器运行的电脑上进行分析)。

3台电脑(目标电脑,调试服务器,调试客户端)的调试将会被简要的讨论。

在主机调试软件(WinDbg或者KD)和目标操作系统之间,是一个协同处理的调试过程。每一部分都必须做些什么。更明确地,WinDbg 不是作为一个“管理操作系统”,

象客户和一个真正操作系统那样运行目标。WinDbg是一个调试软件,象目标操作系统的合作伙伴那样知道它在调试过程中的角色。在这种关系中,WinDbg从目标接收信息,

并且向目标发送信息。这是一种有效的通信机制。

serialprotocol是调试器与目标系统之间可靠的通信机制。你能通过null-modem cable使用COM端口连接主机和目标机器。另一个可供选择的通信机制是1394。

在调试工具的帮助文件中的“Configuring Software on the Target Computer.” 主题有关于它们的描述。

你的第一次session

假设你的主机使用WIN2K或以上的版本。主机的操作系统可以不同于目标电脑的操作系统。主机可以在你平常进行开发,维护或者故障诊断的地方。它应该与网络连接,

如果你希望访问symbol和source服务器(请看symbols和source)。

从命令提示窗口中,改变当前的目录到WINDOWS调试工具的安装目录。这是windbg.exe 和kd.exe 所在的位置。输入windbg,按下Enter。你将会看到:

gxlsystem.com,布布扣

分屏

在这里,你能重排你的窗口。下面的例子包括可移动的窗口。打开组合窗口并移到屏幕上方,单击“Command”标题栏并拖动它的窗口离开主框架。然后收缩主框架,

你可以使用键击代替直接使用菜单或者按钮。

然后使用FileàKernel Debug 以得到一个协议窗口,选择1394和channel1。到这里,你的桌面会象下图一样:

gxlsystem.com,布布扣

在KernelDebugging窗口中,点OK。

激活连接

现在你已经准备好在主机和目标之间建立连接。在目标机器以其中一个调试入口启动WINDOWS。立即回到主机系统,用鼠标激活WinDbg 的命令窗口,

按下CTRL+BREAK 。不久之后,你会看到:

gxlsystem.com,布布扣

现在不必担心关于symbols的信息。你已经将WinDbg连接到WIN 2003。你现在很忙!

你需要明白一件细小却至关重要的事:在命令窗口的底部显示“kd>”提示符。这代表WinDbg 已经准别好接受命令。如果没有提示符显示,这时WinDbg将不能处理命令,

尽管你输入的任何命令都将会被保存在缓冲区域并尽可能快的运行。你必须等待“kd>”出现,以确定WinDbg已经作好响应的准备。

因为有时它正在忙于做某些你看不见的事(例如从目标取得信息,该信息可能很庞大)。缺少“kd>”是WinDbg处于繁忙状态的唯一线索。

另一个可能是WinDbg试图解析symbol并且时间超过了你的预期。不幸地,WinDbg偶尔会等待一个永远不会响应的目标连接(可能boot.ini配置得不好,或者选择了错误的选项)。在等待足够时间之后,你必须决定采取激烈的措施例如按下 CTRL+BREAK,或者停止WinDbg重新开始。

查找symbols和source

现在你很可能渴望开始调试,但仍然有一些东西你必须去做,因为它们将会很好的改善你的调试体验。

首先确认WinDbg能找到你感兴趣模块的symbols。Symbols指出一个二进制命令与声明之间的联系和什么变量正在被转移。换句话说,就是Symbols表。

如果你在建立模块的地方,那么你将拥有有效的symbols和source文件。但是如果你需要单步调试其他很早以前建立代码呢?或者,在那种情况下,

如果你的代码不在它被建立的地方呢?

明确的设置symbols所在的地方,使用.sympath命令。在命令窗口中中断(CTRL-BREAK)然后输入:

.sympathSRV*<DownstreamStore>*http://msdl.microsoft.com/download/symbols

以便告诉WinDbg在Microsoft公开的symbols服务器上查找symbols。让WinDbg使用该服务以及在本地保存一份已下载的symbols。例如,在D:\DebugSymbols,你应该这么做:

.sympathSRV*d:\DebugSymbols*http://msdl.microsoft.com/download/symbols

你偶尔会在symbols服务器上获取symbols时遇到一些故障。在这个情况下,使用!sym noisy 命令以获得关于WinDbg尝试获取symbols的更多信息。

然后使用 !lmi 查看WinDbg知道多少关于ntoskrnl的信息。然后尝试取得ntoskrnl的symbols,使用.reload /f。因而:

kd> !sym noisy

noisy mode - symbol prompts on

 

kd>!lmi nt

LoadedModule Info: [nt]

Module: ntoskrnl

Base Address: 80a02000

Image Name: ntoskrnl.exe

Machine Type: 332 (I386)

Time Stamp: 3e80048b Mon Mar 24 23:26:032003

Size: 4d8000

CheckSum: 3f6f03

Characteristics:10e

DebugData Dirs: Type  Size     VA Pointer

codeVIEW    25, ee00,    e600 RSDS - GUID:(0xec9b7590, 0xd1bb, 0x47a6, 0xa6, 0xd5, 0x38, 0x35, 0x38, 0xc2, 0xb3, 0x1a)

Age: 1, Pdb: ntoskrnl.pdb

Image Type: MEMORY   - Image read successfully from loadedmemory.

Symbol Type: EXPORT   -PDB not found

Load Report: exportsymbols

在WINDOWS调试工具帮助文件中,有关于这里使用的命令及其语法的描述。

输出symbols通常很大。WINDOWS调试工具包括一个symbol服务器,以便连接到Microsoft的网络服务器保存这些公开的symbol。添加这些到你的symbol路径,

然后加载它们:

kd>.sympath SRV*d:\DebugSymbols*http://msdl.microsoft.com/download/symbols

Symbol search path is: SRV*d:\ DebugSymbols*http://msdl.microsoft.com/download/symbols

kd>.reload /f nt

SYMSRV:  \\symbols\symbols\ntoskrnl.pdb\EC9B7590D1BB47A6A6D5383538C2B31A1\file.ptr

SYMSRV:  ntoskrnl.pdb from \\symbols\symbols: 9620480bytes copied       

DBGHELP:nt - public symbols

d:\DebugSymbols\ntoskrnl.pdb\EC9B7590D1BB47A6A6D5383538C2B31A1\ntoskrnl.pdb

kd>!lmi nt

LoadedModule Info: [nt]

Module: ntoskrnl

Base Address: 80a02000

Image Name: ntoskrnl.exe

Machine Type: 332 (I386)

Time Stamp: 3e80048b Mon Mar 24 23:26:032003

Size: 4d8000

CheckSum: 3f6f03

Characteristics:10e

DebugData Dirs: Type  Size     VA Pointer

codeVIEW    25, ee00,    e600 RSDS - GUID:(0xec9b7590, 0xd1bb, 0x47a6, 0xa6, 0xd5, 0x38, 0x35, 0x38, 0xc2, 0xb3, 0x1a)

Age: 1, Pdb: ntoskrnl.pdb

Image Type: MEMORY   - Image read successfully from loadedmemory.

Symbol Type: PDB      -Symbols loaded successfullyfrom symbol server.

d:\DebugSymbols\ntoskrnl.pdb\EC9B7590D1BB47A6A6D5383538C2B31A1\ntoskrnl.pdb

Compiler: C - front end [13.10 bld 2179]- back end [13.10 bld 2190]

Load Report: public symbols

d:\DebugSymbols\ntoskrnl.pdb\EC9B7590D1BB47A6A6D5383538C2B31A1\ntoskrnl.pdb

symbols只会给你一些信息,而不会提供源代码。在最简单的情况下,在它们被建立的时候,source文件便在同一个地方(该位置包括2进制文件和symbol文件)。

但是在大多数情况下,你不能在那里找到它们(它们可能被移走了),你必须指定在哪里能找到它们。这时,你需要一个源路径,例如,

.srcpath e:\Win2003SP1

它的意思是:想要source文件,请查看e:\Win2003SP1目录。

另一个解决方案是命名一个source服务器,如果你有:

.srcpath\\MySrcServer

如果你曾经在获取source文件时遇到麻烦,使用.srcnoisy 1以取得更多关于调试器查找它们的信息。

Workspaces

目前你还不能开始调试,除非你已经准备好打很多字。很多设置都被保存在workspace中。所以你应该使用FileàSave 保存在workspace里面,例如,你将它保存为kernel1394Win2003。在这之后,你希望以这个workspace的设置启动WinDbg:

windbg -Wkernel1394Win2003 -k 1394:channel=1

–W指定一个workspace,而–k给出通信方式(祥见WINDOWS调试工具帮助文件中的“WinDbg Command-Line Options”)。注意:在WinDbg或者KD中,

你应该小心区分命令行可选项的大小写。

为了让事情变得简单,你可以在桌面建立快捷方式,以使用特定的workspace启动WinDbg,例如,使用1394连接:

gxlsystem.com,布布扣

上述文件中的内容:

cd /d "d:\ProgramFiles\Debugging Tools for Windows"

start windbg.exe -y SRV*d:\DebugSymbols*http://msdl.microsoft.com/download/symbols -W kernel1394Win2003

 

第一行将切换到WINDOWS调试工具的安装目录下面,确认调试器模块能在那里被找到。第二行启动WinDbg,指定symbo路径(-y)和workspace (-W)。

一个示例驱动

使用示例驱动IoCtl练习,这将会帮助你熟悉WinDbg。你能在WINDDK和它的后续产品,WDK中找到。安装它,你便能在src\general\Ioctl子目录下找到该驱动。

IoCtl的优点在于它是示例,而且是一个“legacy”驱动,由服务管理器(SCM)加载,而不是即插即用的一部分(这里并不关心PnP的输入和输出)。

你应该建立用户态程序(ioctlapp.exe),并在前者被加载之后建立内核态驱动程序(sioctl.sys)。

这里有些重要的事需要明白。在优化代码方面,建立程序的处理十分灵巧,优化会导致代码移动(当然,原逻辑会被保留),并且将一些变量单独保存在寄存器中。

为了确保更简单的调试体验,你应该在建立窗口或者源代码文件中使用这些编译指令建立一个调试版本:

MSC_OPTIMIZATION=/Od

(这是“Oh d”而不是“zerod.”)

有时上述的情况会引起内部函数的一些问题,例如memcmp。如果你碰上这个问题,尝试 :

MSC_OPTIMIZATION=/Odi

请明白阻止优化对于生成正式版产品来说,并不是一个好选择。使用上述的指令,你将不能建立或者测试正式版。尽管如此,这对于测试未经优化的版本来说,

是不错的练习。一旦你熟悉代码,排除简单的错误,正式产品便能得到提升。如果你需要处理已优化的代码,你将会在“处理优化代码”找到相关帮助。

开始调试示例驱动

在IoCtl 的DriverEntry设置断点。在启动驱动之前,中断在WinDbg的命令窗口,输入:

busioctl!DriverEntry

bu (“Breakpoint Unresolved”)命令将会延迟断点的设置时间,直到该模块被加载;也就是说WinDbg会探测“DriverEntry”。如果没有什么需要做,按下F5(你也可以输入g, “Go”)

接下来,复制ioctlapp.exe和sioctl.sys到目标系统,例如C:\Temp\IOCTL,以管理员权限登陆系统,在命令窗口中,切换到C:\Temp\IOCTL目录下。

(你不需要在WinDbg中将此路径设置为symbol路径和source路径。)在同样的命令窗口,输入ioctlapp按下Enter,在WinDbg中,你会看到:

gxlsystem.com,布布扣

如图,程序停在断点之后,!lmi 命令显示WinDbg从DDK中取得symbols。时间信息象你期望的一样,本地symbol文件也符合你的要求。

依赖于你的排列方案,它并不明显,当前窗口能被其他窗口隐藏,但是你能在某个地方使用源代码窗口(按键顺序‘alt-Keypad *’ ― 不用按单引号― 将会把窗口置前):

gxlsystem.com,布布扣

断点被设置,即运行停止的地方会以粉红色标记(WINDOWS调试工具帮助文件把它称为紫色)。当运行进IoCreateDevice(运行控制 描述如何熟练运用):

gxlsystem.com,布布扣

这里你能看到原始断点(高亮为红色,现在控制将停止在这里),你能看到当前声明被标记为深蓝色。

基础

在调试session中,这是一个“测试驱动”。这是一些基本的调试操作。

命令,扩展,等等。

命令来自几个系列:简单的(未修饰的),一些从句号(“.”)开始,一些从惊叹号(“!”)开始。WINDOWS调试工具帮助文件将它们分别描述为commands,

meta-commands and extension commands。以现在的效果来看,这些系列非常接近。

断点

在运行中产生中断,是调试器的功能之一。这是一些实现方法。

·        在操作系统启动时中断

为了在操作系统启动时尽早中断,请确认WinDbg 已经连接,重新按CTRL-ALT-K直到你看到:

gxlsystem.com,布布扣

在下次启动时,在ntoskrnl加载之后的一小段时间,这时所有驱动还没有被加载,操作系统将会挂起,而WinDbg将会取得控制权。在系统引导时间,

你可能会希望为驱动程序定义断点,这就是时机。

·        普通断点

最简单的设置断点的方法就是通过bp (“Breakpoint”)命令。例如:

bp MyDriver!xyz

bp f89adeaa

第一行,这个断点设在模块中的一个名字(<module>!<name>);第二行,它被设置在一个给出的地址。当运行到其中一个断点时,操作系统就会挂起,并且把控制权交给WinDbg。(你可以在“寻找名字”看看如何为第二个命令取得地址。)

注意:第一个命令的语法假定操作系统已经加载该模块,以及在symbol文件或者外部名定义有足够可用信息关于识别xyz。如果不能在模块中找到xyz

调试器会这么告诉你这些。

·        延迟断点

说到驱动程序没有被加载,你最初的哪个断点,使用bu(见上述开始调试示例驱动)设置的是一个“可延迟的”断点。Bu命令的参数是一个模块及它里面的名字,例如:

bu sioctl!SioctlDeviceControl

SioctlDeviceControl是一个入口点,或者其他在模块sioctl.sys中的名字。这个形式假定当模块被加载,足够有用的信息识别SioctlDeviceControl以便断点能够设置。

(如果模块已经加载名字被找到,那么断点将会立即被设置)。如果操作系统找不到SioctlDeviceControl,调试器会提示,另外将不会在SioctlDeviceControl处挂起。

延迟断点的一个有用的特性便是它对modules!names操作。相比之下,一般断点对地址或者立即将modules!names解释为地址。

延迟断点的另一个特性便是在引导的过程中会被记住(这不会影响明确地址的断点)。然而,延迟断点的另外一个特性使得即使关联模块被卸载,它仍然会被保留。

相同情况下,一般断点将会被移除。

·        另外一个设置一般断点的方法是通过source窗口。返回sioctl.sys。当你中断于DriverEntry,,你能向下滚动窗口到你希望停止地方,将光标移动到该行代码,按下F9:

gxlsystem.com,布布扣

红色的那一行便是通过F9设置的断点。

·        你可以使用bl(“Breakpoint List”)查看所有已设置的断点:

kd> bl

0e [d:\winddk\3790\src\general\ioctl\sys\sioctl.c @ 123]    0001 (0001) SIoctl!DriverEntry

1e [d:\winddk\3790\src\general\ioctl\sys\sioctl.c @ 338]    0001 (0001)Sioctl!SioctlDeviceControl+0x103

注意两件事:每个断点都有一个号码并且显示出断点状态,“e”是“enabled”,而“d”是“disabled”。

·        假设你希望临时停止使用某个断点。bd (“DisableBreakpoint”) 将会完成它。你只需指定断点号码:

kd> bd 1

kd> bl

0e [d:\winddk\3790\src\general\ioctl\sys\sioctl.c @ 123]    0001 (0001) SIoctl!DriverEntry

1 d[d:\winddk\3790\src\general\ioctl\sys\sioctl.c @ 338]    0001 (0001)SIoctl!SioctlDeviceControl+0x103

·        相似的方法,永久移除断点号码,使用bc 1 (“ClearBreakpoint”)。现在该断点将会从断点列表中消除。

·        然而,有时在操作系统或者驱动程序中,断点会被设置在一些频繁被激活的地方,你可能希望将它应用在一些环境或者条件操作,以便断点只在该情况下生效。

这是基本格式:

bp SIoctl!SioctlDeviceControl+0x103 "j (@@(Irp)=0xffb5c4f8) ‘‘; ‘g‘"

它的意思是:只有Irp=地址0xFFB5C4F8时才中断;如果条件不符合,继续运行。

更深入的探讨上述命令,并不是断点本身的状态。更准确的说,断点有一个操作项目(在双引号标记中);在该项目中,j (“Execute IF/ELSE”)命令是一个条件操作。

J 的函数运行于TRUE|FALSE项目(在单引号标记中)。如上述一样,TRUE项目(第一)为空,以便当断点激活和符合TRUE的条件出现时,

WinDbg除了挂起程序之外不会做其他的事。如果符合FALSE的条件出现,由于使用了g命令,程序讲会继续运行。一个或者其他操作会被完成,这依赖于实际情况。

思考这个比上述更详细的命令:

bp SIoctl!SioctlDeviceControl+0x103 "j (@@(Irp)=0xffb5c4f8) ‘.echo Found theinteresting IRP‘ ; ‘.echo Skipping an IRP of no interest; g‘ "

这里TRUE项目给出信息并停止。FALSE项目给出信息并继续(这个信息很有用,WinDbg计算出条件为FALSE,并且默默地继续)。

有时要注意:下面断点,EAX被检测(你能在寄存器中找到关于它们的处理方法),不会象你想的那样工作:

bp SIoctl!SioctlDeviceControl+0x103 "j (@eax=0xffb5c4f8) ‘.echoHere!‘ ; ‘.echo Skipping; g‘ "

原因是可能会将寄存器的值扩充到64位再计算,例如,扩充到0xFFFFFFFF`FFB5C4F8,这将不会与0x00000000`FFB5C4F8匹配。

这导致只有32位的最高位为1和一些其他条件(例如,一个32位寄存器)才适用。在WINDOWS调试工具帮助文件中的“SignExtension”有更详尽的资料

(也可以看看“Setting a Conditional Breakpoint”)。

断点可能包含一些条件式,附带或不附带条件操作。其中一个条件是激发“one-shot”:断点只激活一次(激活之后便清除)。假如你只对第一次激活感兴趣,

对于那些使用频繁的代码,这很便利。

bp /1 SIoctl!SioctlDeviceControl+0x103

另外一个有用的条件式测试一个进程或者线程:

bp /p 0x81234000 SIoctl!SioctlDeviceControl+0x103

bp /t 0xff234000 SIoctl!SioctlDeviceControl+0x103

它们分别代表,仅当进程块(EPROCESS)在0x81234000,才在指定的地方停止,以及仅当线程块(ETHREAD)在0xFF234000时才在指定地方停止。

该条件式能被组合为:

bp /1 /C 4 /p0x81234000 SIoctl!SioctlDeviceControl+0x103

这代表,当call stack深度大于4(这里的大写C很重要,因为“c”代表“少于”)和进程块在0x81234000时中断。

·        另外一种不同类型的断点,需要指定访问方式。例如:

ba w40xffb5c4f8+0x18+0x4

正如你所看到的,这个地址来自IRP,它的偏移0x18+0x4处即它的IoStatus.Information 成员。所以当某程序企图更新IRP中IoStatus.Information的这4个字节时,

断点会被激活。这种断点被称为数据断点(因为它们由数据访问触发)或者处理器断点(因为它们由处理器执行,而不是调试器自己)。

表达式:MASM与C++

在驱动程序之中使用变量提供参数,如进程地址。你或许同意那是很容易的一件事。然而,你需要理解一些调试器的表达式。

调试器有两种评价表达式的方法,参考“MASM” (MicrosoftMacro Assembler)和“C++”。引用WINDOWS调试工具帮助文件中的“MASMExpressions vs. C++ Expressions”:

在MASM的表达式中,任何符号的数值都在内存地址中。在C++表达式中,变量中的数值是它的真实数值,而不是它的地址。

阅读再阅读这部分,这将会节省你更多的时间。

一条表达式将会使用MASM,或者C++,或者它们的混合形式计算。

简要说明:

1.   默认表达式类型是MASM.

2.   你能使用.expr 改变默认类型(详见WINDOWS调试工具帮助文件)。

3.   某些命令总是使用C++的方式求值。

4.    一个特殊的表达式(或表达式的一部分)的赋值能通过前缀“@@”改成与一般表达式相反的方向。

这个摘要相当棘手,你应该参考WINDOWS调试工具帮助文件中的“Evaluating Expressions”。现在,这里有一些例子,给你一些关于赋值是如何工作的概念。

你之前已经停止在Sioctl!SioctlDeviceControl+0x103,所以使用dv 查看一个已知变量(查看dv 命令以获得更多信息):

kd> dv Irp

Irp = 0xff70fbc0

该响应的意思是,Irp 变量包含0xFF70FBC0。更多地,dv 解释C++语法中的参数。该响应基于变量内容,而不是地址。你可以确认它:

kd> ?? Irp

struct _IRP * 0xff70fbc0

 

?? 总是以C++ 为基础(详见??命令)。假如使用MASM类型的赋值,尝试? (详见? 命令):

kd> ? Irp

Evaluateexpression: -141181880 = f795bc48

这表示变量Irp 位于0XF795BC48。你可以通过使用dd (详见dd 命令)显示内存数据,确认该变量真的包含数据0xFF70FBC0。

kd> dd f795bc48 l1

f795bc48 ff70fbc0

以及内存指向这里:

kd> dd 0xff70fbc0

ff70fbc0 00940006 00000000 00000070 ff660c30

ff70fbd0 ff70fbd0 ff70fbd0 00000000 00000000

ff70fbe0 01010001 04000000 0006fdc0 00000000

ff70fbf0 00000000 00000000 00000000 04008f20

ff70fc00 00000000 00000000 00000000 00000000

ff70fc10 ff73f4d8 00000000 00000000 00000000

ff70fc20 ff70fc30 ffb05b90 00000000 00000000

ff70fc30 0005000e 00000064 0000003c 9c402408

查看象IRP这样的变量,正如dt显示(详见dt 命令),Type和Size成员有一个似是而非的数据:

kd> dt Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 6

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040Tail             : __unnamed

有时,你会希望使用C++ 赋值代替MASM表达式。“@@” 前缀会完成它。扩展命令总是使用象MASM表达式一样的参数,当你使用扩展命令!irp (详见 IRPs),

你能看到@@的效果。

kd> !irp @@(Irp)

Irp is active with 1 stacks 1 is current(= 0xff70fc30)

NoMdl System buffer = ff660c30 Thread ff73f4d8: Irp stack trace.

cmd  flg cl Device   File    Completion-Context

>[ e, 0]   5  0 82361348 ffb05b90 00000000-00000000  

       \Driver\SIoctl

Args:00000064 0000003c 9c402408 00000000

重复这个操作,不在上述的 Irp 变量中带@@ 前缀,!irp 将会使用变量的地址,而不是变量的值。为了使这更加具体,如果变量位于0xF795BC48,它包含的数据是0xFF70FBC0,使用!irp Irp 代替@@(Irp)将会请求WinDbg 格式化位于0xF795BC48的IRP stack。

你需要进一步了解的是:@@前缀相当通用,正如它的正式意思,使用不同于当前表达式中正在使用的赋值方法。如果大部分表达式是MASM,@@代表C++,

如果它是C++,@@代表MASM。

最后一点建议:如果表达式不如你期望那样工作,考虑你是否在请求调试器理解MASM或者C++语法。

显示和设置内存,变量,寄存器等等

有一些方法可以显示和改变它们。

  • 在当前例程中显示一个变量(当前的“scope”),使用dv (“Display     Variables”)。例如,如果停止在Sioctl!SioctlDeviceControl+0x103:

kd> dv

DeviceObject = 0x82361348

Irp = 0xff70fbc0

outBufLength = 0x64

buffer = 0x00000000 ""

irpSp = 0xff70fc30

data = 0xf886b0c0 "This String is from Device Driver !!!"

ntStatus = 0

mdl = 0x00000000

inBufLength = 0x3c

datalen = 0x26

outBuf = 0x00000030 ""

inBuf = 0xff660c30 "This String is from User Application; usingMETHOD_BUFFERED"

这是一个参数变量列表以及一些在断点位置已知的变量。“已知”是一个重要的限定词。例如如果一个变量优化成一个寄存器,它将不会被显示,尽管可以反汇编它(View=>Disassembly 打开反汇编窗口)并且检查寄存器。

如果只关心一个变量,你可以:

kd> dv outBufLength

outBufLength = 0x64

  • 另外一个有用的命令是dt (“Display     Type”)。例如,继续使用在Sioctl!SioctlDeviceControl+0x103的断点:

kd> dt Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 6

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             : __unnamed

上面的数据说明了变量Irp在0xF795BC48,它的值是0xFF70FBC0;因为 dt 知道IRP变量的指针(“Type _IRP*”),0xFF70FBC0区域被格式化为IRP。

展开一级结构:

kd> dt -r1 Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 6

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x000 MasterIrp        :0xff660c30

+0x000 IrpCount         :-10089424

+0x000 SystemBuffer     :0xff660c30

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x000 Flink            :0xff70fbd0  [ 0xff70fbd0 - 0xff70fbd0 ]

+0x004 Blink            :0xff70fbd0  [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x000 Status           : 0

+0x000 Pointer          : (null)

+0x004 Information      : 0

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x000 Status           : 67142040

+0x000 Pointer          :0x04008198

+0x004 Information      : 0x2a

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x000 AsynchronousParameters : __unnamed

+0x000 AllocationSize   :_LARGE_INTEGER 0x0

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             :__unnamed

+0x000 Overlay          :__unnamed

+0x000 Apc              : _KAPC

+0x000 CompletionKey    : (null)

你可以显示一些结构,甚至在它们不在范围之内的时候(被询问的内存不能以其他一些目的再生)

kd> dt nt!_IRP 0xff70fbc0

+0x000 Type             : 6

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             :__unnamed

上面的命令,按照你知道的来说,就是IRP在0xFF70FBC0,而事实上,这是在ntoskrnl映射出的IRP结构。

  • 如果你对众多成员的区域中的一块感兴趣呢?取得成员的大小,例如:

kd> dt nt!_IRP Size 0xff70fbc0

unsigned short 0x94

更直接的方法是使用?? (“EvaluateC++ Expression”) 命令:

kd> ?? Irp->Size

unsigned short 0x94

那是??,了解它的参数指向适当结构中的一个成员。

  • 显示内存,而不使用上述的格式,一些可用的命令,如dd, dwdb (“Display Memory”) :

kd> dd 0xff70fbc0 l0x10

ff70fbc0 00940006 00000000 00000070 ff660c30

ff70fbd0 ff70fbd0 ff70fbd0 00000000 00000000

ff70fbe0 01010001 04000000 0006fdc0 00000000

ff70fbf0 00000000 00000000 00000000 04008f20

 

kd> dw 0xff70fbc0 l0x20

ff70fbc0 0006 0094 0000 0000 0070 0000 0c30 ff66

ff70fbd0 fbd0 ff70 fbd0 ff70 0000 0000 0000 0000

ff70fbe0 0001 0101 0000 0400 fdc0 0006 0000 0000

ff70fbf0 0000 0000 0000 0000 0000 0000 8f20 0400

 

kd> db 0xff70fbc0 l0x40

ff70fbc0 06 00 94 00 00 00 00 00-70 00 00 00 30 0c 66 ff  ........p...0.f.

ff70fbd0 d0 fb 70 ff d0 fb 70 ff-00 00 00 00 00 00 00 00  ..p...p.........

ff70fbe0  01 00 01 01 00 00 00 04-c0 fd 06 00 00 00 0000  ................

ff70fbf0 00 00 00 00 00 00 00 00-00 00 00 00 20 8f 00 04  ............ ...

(注意: 3个命令各自的第二个参数是一个长度,由l (字母“l”)后面的数值给出,例如0x10。)

第一个显示16个双字(每个4字节,或者共64个字节)。第二个显示同样的字。第三个显示同样的字节。

  • 怎么改变变量?继续在Sioctl!SioctlDeviceControl+0x103,你会看到下面格式。

kd> outBufLength = 00

^ Syntax error in ‘outBufLength = 00‘

不工作?但是??完成了这个工作:

kd> ?? outBufLength = 0

unsigned long 0

现在回到IRP,你在上述使用的dt

kd> dt Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 6

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         : _IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             :__unnamed

改变第一个字(2个字节),通过ew (“Enter Values”):

kd> ew 0xff70fbc0 3

 

kd> dt Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 3

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             :__unnamed

当然,下面可能比ew更加自然

kd> ?? irp->type = 3

Type does not have given member error at‘type = 3‘

kd> ?? irp->Type = 3

short 3

 

kd> dt irp

ioctlapp!Irp

Local var @ 0xf795bc48 Type _IRP*

0xff70fbc0

+0x000 Type             : 3

+0x002 Size             : 0x94

+0x004 MdlAddress       : (null)

+0x008 Flags            : 0x70

+0x00c AssociatedIrp    :__unnamed

+0x010 ThreadListEntry  :_LIST_ENTRY [ 0xff70fbd0 - 0xff70fbd0 ]

+0x018 IoStatus         :_IO_STATUS_BLOCK

+0x020 RequestorMode    : 1 ‘‘

+0x021 PendingReturned  : 0 ‘‘

+0x022 StackCount       : 1 ‘‘

+0x023 CurrentLocation  : 1 ‘‘

+0x024 Cancel           : 0 ‘‘

+0x025 CancelIrql       : 0 ‘‘

+0x026 ApcEnvironment   : 0 ‘‘

+0x027 AllocationFlags  : 0x4 ‘‘

+0x028 UserIosb         :0x0006fdc0

+0x02c UserEvent        : (null)

+0x030 Overlay          :__unnamed

+0x038 CancelRoutine    : (null)

+0x03c UserBuffer       :0x04008f20

+0x040 Tail             :__unnamed

以上需要注意的两件事。首先,结构中成员的大小写是有意义的,正如WinDbg的提示那样,在Irp 中没有这样的成员。第二,dt irp 是二义的,

但是WinDbg显示了该实例,它的想法好象被修正了,其中一个在ioctlapp.exe而另外一个则在sioctl.sys。因为大小写是有意义的,你应该在任何时候都使用它。

关于ew的更多信息,有其他 “Enter Values”命令:eb 用于字节,ed 用于双字,eq 用于四倍字长(8字节)等等。参考WINDOWS调试工具帮助文件中的“Enter Values”。

本地窗口能更容易的显示内嵌到结构中的结构指针

gxlsystem.com,布布扣

你可以在本地窗口中改写它们的值。

  • 寄存器(也包括段寄存器和标记寄存器) 可以被显示和改变。例如:

kd> r

eax=81478f68ebx=00000000 ecx=814243a8 edx=0000003c esi=81778ea0 edi=81478f68

eip=f8803553esp=f7813bb4 ebp=f7813c3c iopl=0        nv up ei ng nz ac pe nc

cs=0008  ss=0010  ds=0023 es=0023  fs=0030  gs=0000             efl=00000292

或者只是:

kd> r eax

eax=81478f68

有时你会希望改变寄存器。例如,EAX经常被用于从例程退出时传递返回参数。因此,在例程退出之前:

r eax = 0xc0000001

现在显示状态数据为STATUS_UNSUCCESSFUL.

这里是其他的一些例子:

r eip = poi(@esp)

r esp = @esp + 0xc

他们分别表示,设置Eip (命令指针)为堆栈偏移为0x0指向的值,和Esp(堆栈指针)+0xC,有效的释放堆栈。WINDOWS调试工具帮助文件中的 “RegisterSyntax”,

解释了poi 命令和为什么寄存器一些地方需要加上“@”前缀。

你可能会问上述寄存器设置命令怎么用。考虑一下,当一个“坏”驱动的DriverEntry 将会引起故障检查(“蓝屏”)— 或许由于违规访问。

你可以通过在ntoskrn加载时设置一个延迟断点处理这些问题。下面命令必须在同一行中:

busioctl!DriverEntry "r eip = poi(@esp); r eax = 0xc0000001; r esp = @esp +0xc; .echo sioctl!DriverEntry entered; g"

它的意思是:在sioctl.sys的DriverEntry,1) 这样设置命令指针 (Eip) 2) 这样设置返回代码 (Eax) 3) 这样设置堆栈指针 (Esp) 4) 宣布已经进入DriverEntry 5) 继续运行。(当然,这技术仅仅移除DriverEntry 引起崩溃的可能性,例如违规访问。如果操作系统期待驱动程序供应函数,该函数将不可用,和可能是其他问题导致停机。)

在这里,你会想知道是否能用寄存器设置一个变量。例如,返回到IoCtl的dispatch routine:

kd> r

eax=00000000ebx=00000000 ecx=81a88f18 edx=81a88ef4 esi=ff9e18a8 edi=ff981e7e

eip=f87a40feesp=f88fac78 ebp=f88fac90 iopl=0        nv up ei pl zr na po nc

cs=0008  ss=0010 ds=0023  es=0023  fs=0030 gs=0000             efl=00000246

 

kd> ?? ntStatus = @ecx

long -2119659752

kd> dd &ntStatus l1

f88fac78 81a88f18

在这个情况中,应该使用@ecx格式,以保证WinDbg 知道你在引用一个寄存器。

寄存器的数量比默认显示的要多。要查看所有寄存器,使用rM命令(“M”必须是大写;实际上是r命令带M参数,这里在命令和参数之间不允许空格):

kd> rM 0xff

eax=00000001ebx=0050e2a3 ecx=80571780 edx=000003f8 esi=000000c0 edi=d87a75a8

eip=804df1c0esp=8056f564 ebp=8056f574 iopl=0        nv up ei pl nz na pe nc

cs=0008  ss=0010 ds=0023  es=0023  fs=0030 gs=0000             efl=00000202

fpcw=0000: rn 24------  fpsw=0000: top=0 cc=0000--------  fptw=0000

fopcode=6745  fpip=2301:a0020000  fpdp=dcfe:efcdab89

st0= 5.143591243081972142170e-4932  st1= 0.001025530551233493990e-4933

st2= 0.000000002357022271740e-4932  st3= 2.471625214254630491460e-4906

st4= 3.370207406893238285120e-4932  st5=-7.461339669368745455450e+4855

st6= 6.698191557136036873700e-4932  st7=-2.455410815115332972380e-4906

mm0=c3d2e1f010325476  mm1=0000ffdff1200000

mm2=000000018168d902  mm3=f33cffdff1200000

mm4=804efc868056f170  mm5=7430804efb880000

mm6=ff02740200000000  mm7=f1a48056f1020000

xmm0=09.11671e-041 3.10647e+035 -1.154e-034

xmm1=-7.98492e-039-2.83455e+038 -2.91106e+038 5.85182e-042

xmm2=1.77965e-043-1.17906e-010 -4.44585e-038 -7.98511e-039

xmm3=-7.98511e-0390 0 -7.98504e-039

xmm4=-7.98503e-0391.20545e-040 -1.47202e-037 -1.47202e-037

xmm5=-2.05476e+018-452.247 -1.42468e-037 -8.60834e+033

xmm6=2.8026e-044-1.47202e-037 -452.247 0

xmm7=8.40779e-045-7.98503e-039 0 -7.98511e-039

cr0=8001003b cr2=d93db000 cr3=00039000

dr0=00000000 dr1=00000000 dr2=00000000

dr3=00000000 dr6=ffff0ff0 dr7=00000400cr4=000006d9

  • 如果你不想使用命令作改变,你可以打开内存窗口(ViewàMemory),变量窗口 (ViewàLocals)或者寄存器窗口 (ViewàRegisters),并且改写你想要数值。
  • 例如,
  • gxlsystem.com,布布扣

如上图,你可以改写16进制的数值。

运行控制

在前面的部分(详见IoCreateDevice)你曾经想程序从一点运行到下一点,而不需要告诉它怎么做。这里有一些方法可以控制运行。下面所有的项目,除第一项,

都假设程序处于挂起状态。

  • 中断(CTRL-BREAK ) — 该快捷键总是中断系统,只要系统正在运行并与WinDbg 处于通信状态 (在KD 快捷键是CTRL-C)。
  • 步过(F10)     — 每按一次运行一条语句(如果C 或者C++     和     WinDbg处于“source     mode”, 可通过DebugàSource     Mode切换),或者一条指,
  • 并且规定如果遇到一个函数调用,将会运行过该函数,而不会进入它。
  • 步进(F11) — 就象步过那样,除了运行到一个函数调用时,会进入该调用例程。
  • 步出     (SHIFT-F11) — 这会使程序运行直到完成当前例程(在call stack中的当前地址)。如果你对该例程已经了解得足够多,这个快捷键很有用。
  • 运行到光标(F7 or CRTL-F10) — 当你想运行到该处中断,你可以将光标放到源代码窗口或者反汇编窗口中相应的位置,按下F7;程序将会运行到该位置。
  • 有一点要注意,然而:如果运行流程与该处不匹配(例如,一个IF语句不运行),WinDbg 将不会中断,因为并没有运行到指定地方。
  • 运行 (F5) — 运行直到遇到断点或者错误事件被检测到。你可以将“运行”想象为正常执行状态。
  • 将指令设置在当前行(CTRL-SHIFT-I) — 在源代码窗口,你可以把光标放在一行中,使用该快捷键,只要你允许(例如 F5或者F10),程序便从该处开始运行。
  • 在你想重复一些指令序列时,这很有用。但是要注意一些事情。例如,寄存器和变量的数据不会象你正常运行到该处时看到那样。
  • 直接设置Eip — 你可以为Eip寄存器设置一个数值,然后按下F5(或者F10或者其他的什么),运行开始于该地址。显然易见,该功能就象将指令设置在当前行
  • ,除非你指定了一个汇编指令的地址。
call stack

几乎运行到某一点,都会有一个区域作为堆栈使用;该堆栈用于存放本地状态,参数和返回地址。在内核空间中有一个内核栈,在用户空间中有一个用户栈。当中断发生时,可能有几个例程在当前的栈中。例如,如果由于sioctl.sys中PrintIrpInfo的断点引起指令停止执行,使用k (“Stack Backtrace”):

kd> k

ChildEBPRetAddr

f7428ba8f889b54a SIoctl!PrintIrpInfo+0x6[d:\winddk\3790.1824\src\general\ioctl\sys\sioctl.c @ 708]

f7428c3c 804e0e0dSIoctl!SioctlDeviceControl+0xfa[d:\winddk\3790.1824\src\general\ioctl\sys\sioctl.c @ 337]

WARNING: Stackunwind information not available. Following frames may be wrong.

f7428c6080580e2a nt!IofCallDriver+0x33

f7428d00805876c2 nt!CcFastCopyRead+0x3c3

f7428d34804e7a8c nt!NtDeviceIoControlFile+0x28

f7428d6400000000 nt!ZwYieldExecution+0xaa9

最高一行(最新的)栈帧就是停止的地方。你也可以看到此前的一些调用。但是如果你没有symbols,他们可能会显示得不正常。

在驱动中的当前文件和行号信息,都会在每个栈帧中呈现,你将会享受到在sioctl.sys中使用symbols的乐趣。

你可以为IoCtl的IRP处理程序打开源代码窗口。但是假如你对更早的例程不感兴趣?你打开调用窗口 (ViewàCall stack),所以:

gxlsystem.com,布布扣

你可以双击入口,然后便会被带到源代码文件中,如果该文件已被定位。

如果你只对在堆栈中属于例程的变量感兴趣,你可以双击该例程所在的项目,或者你可以用kn (与k同属) 然后.frame。例如,

取得关于调用了PrintIrpInfo的dispatch routine的信息:

kd> kn

# ChildEBP RetAddr

00 f7428ba8f889b54a SIoctl!PrintIrpInfo+0x6[d:\winddk\3790.1824\src\general\ioctl\sys\sioctl.c @ 708]

01 f7428c3c804e0e0d SIoctl!SioctlDeviceControl+0xfa [d:\winddk\3790.1824\src\general\ioctl\sys\sioctl.c@ 337]

WARNING: Stackunwind information not available. Following frames may be wrong.

02 f7428c6080580e2a nt!IofCallDriver+0x33

03 f7428d00805876c2 nt!CcFastCopyRead+0x3c3

04 f7428d34804e7a8c nt!NtDeviceIoControlFile+0x28

05 f7428d64 00000000nt!ZwYieldExecution+0xaa9

kd> .frame 1

01 f7428c3c804e0e0d SIoctl!SioctlDeviceControl+0xfa[d:\winddk\3790.1824\src\general\ioctl\sys\sioctl.c @ 337]

在设置桢号码之后,便能显示在桢中的已知变量和属于该桢的寄存器:

kd> dv

DeviceObject = 0x80f895e8

Irp = 0x820572a8

outBufLength = 0x64

buffer = 0x00000000 ""

irpSp = 0x82057318

data = 0xf889b0c0 "This Stringis from Device Driver !!!"

ntStatus = 0

mdl = 0x00000000

inBufLength = 0x3c

datalen = 0x26

outBuf = 0x82096b20 "This Stringis from User Application; using METHOD_BUFFERED"

inBuf = 0x82096b20 "This Stringis from User Application; using METHOD_BUFFERED"

kd> r

eax=00000000 ebx=00000000 ecx=80506be8 edx=820572a8 esi=81fabda0edi=820572a8

eip=f889bcf6 esp=f7428ba4 ebp=f7428ba8 iopl=0         nv up ei ng nz ac pe nc

cs=0008  ss=0010  ds=0023 es=0023  fs=0030  gs=0000             efl=00000292

SIoctl!PrintIrpInfo+0x6:

f889bcf6 8b4508           mov     eax,[ebp+0x8]     ss:0010:f7428bb0=820572a8

 

 

 

 

在模块中寻找名字
x (“Examine Symbols”)命令能定位模块中的symbols。例如,如果你想在Ioctl 例程中设置断点,以便处理DeviceIoControl IRPs 。
但是你不太记得该例程的名字了,你可以这么做:

kd> x sioctl!*ioctl*

f8883080 SIoctl!SioctlUnloadDriver(struct _DRIVER_OBJECT *)

f8883010 SIoctl!SioctlCreateClose (struct_DEVICE_OBJECT *, struct _IRP *)

f8883450 SIoctl!SioctlDeviceControl(struct _DEVICE_OBJECT *, struct _IRP *)

它的意思是,告诉我所有在sioctl模块中,包含“ioctl.”的symbols。

这看起来看琐细。然而,想想,在一个实际案例中,这个信息在调试器中出现:

热门排行

今日推荐

热门手游