2024年1月13日发(作者:)

WinDbg配置和使用基础

WinDbg是微软发布的一款相当优秀的源码级(source-level)调试工具,可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。

1. WinDbg介绍:

Debugging Tools and Symbols: Getting Started

A word for WinDbg

2. WinDbg下载:

Install Debugging Tools for Windows 32-bit Version

Install Debugging Tools for Windows 64-bit Versions

3. 配置WinDbg:

运行WinDbg->菜单->File->Symbol File Path->按照下面的方法设置_NT_SYMBOL_PATH变量:

在弹出的框中输入“C:MyCodesSymbols;

SRV*C:MyLocalSymbols*/download/symbols”(按照这样设置,WinDbg将先从本地文件夹C:MyCodesSymbols中查找Symbol,如果找不到,则自动从MS的Symbol Server上下载Symbols)。另一种做法是从这个Symbol下载地址中,下载相应操作系统所需要的完整的Symbol安装包,并进行安装,例如我将其安装在D:WINDOWSSymbols,在该框中输入“D:WINDOWSSymbols”。(这里要注意下载的Symbols的版本一定要正确,在我的Win2003+Sp1上,我曾经以为安装Win2003+Sp2的Symbols可能会牛×点,但结果证

明我错了,用WinDbg打开可执行文件时,提示“PDB symbol for not

loaded;Defaulted to export symbols for ”的错误,我有重新装上Win2003+Sp1的Symbols, 现在一切运行正常^_^)

4. 使用WinDbg:

WinDbg提供了图形界面和命令行两种运行方式。这里介绍使用图形界面的WinDbg来调试应用程序:

File->OpenExecutable->可以选择一个可执行文件进行调试;

File->Attache to a Process->可以选择一个运行中的进程,并对其进行调试;

至此,我们就可以在上图中用红色方框标记的文本框中输入各个功能指令了(有关指令的帮助文档,可以参考:Help->Contents->Debugging Tools for Windows->Debuggers->Debugger

Reference,该目录下列集了所有指令机器功能说明!)。

Hi happyhippy,

我是这样调试的,但是始终没成功的。

1.生成符号信息

更改Relase版的设置(2处):

一、Project Setting ->C/C++

Debug info:

选择Program Database

二、Project Setting ->Link

选中Generate debug info

重新编译

2.安装Windbg

下载安装最新的Windbg

3.设定环境变量

对于等系统文件,Windbg可以自动从微软网站下载所需的符号信息.我们只需设定_NT_SYMBOL_PATH

这个环境变量告诉Winddbg.方法如下:

我的电脑>右键菜单>属性>高级选项卡>环境变量>系统变量>新建

变量名: _NT_SYMBOL_PATH

变量值:SRV*C:websymbols*

解释一下: C:websymbols用来存储系统符号.你可以指定其他的路径.Windbg会自动创建该文件夹

4.设定符号路径

新建一个文件夹,譬如C:MyAppSymbols

将第一步中生成的Release目录下.pdb文件拷贝到该文件夹,该pdb文件包含了调试所需的符号信息.

打开Windbg,选择File>Symbol file path>将C:MyAppSymbols路径添加进去

5.调试程序

选择 File > Open Executable 然后选择你需要调试的Exe文件,就显示

WARNING: Whitespace at end of path element

CommandLine: C:

Symbol search path is: SRV*C:websymbols*

Executable search path is:

ModLoad: 00400000 00405000

ModLoad: 77f80000 77ffd000

ModLoad: 6bc40000 6bd3b000 C:

ModLoad: 78000000 78045000 C:

ModLoad: 77e60000 77f33000 C:

ModLoad: 77f40000 77f7c000 C:

ModLoad: 77df0000 77e4f000 C:

(318.430): Break instruction exception - code 80000003 (first chance)

eax=00000000 ebx=00131f04 ecx=00000009 edx=00000000 esi=7ffdf000 edi=00131f70

eip=77f813b1 esp=0012f984 ebp=0012fc98 iopl=0 nv up ei pl nz na po nc

cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202

*** ERROR: Symbol file could not be found. Defaulted to export symbols for -

ntdll!DbgBreakPoint:

77f813b1 cc int 3

然后我就用BP来设置断点用F5来执行,显示

ModLoad: 75e00000 75e1a000 C:

ModLoad: 796d0000 79732000 C:

ModLoad: 786f0000 7875f000 C:

ModLoad: 6bc20000 6bc2d000 C:

ModLoad: 71710000 71794000 C:

ModLoad: 6dd30000 6dd36000 C:

ModLoad: 10000000 10055000 C:Program

ModLoad: 76af0000 76b2e000 C:

ModLoad: 772a0000 77306000 C:

ModLoad: 78f90000 791d6000 C:

ModLoad: 777c0000 777de000 C:

ModLoad: 79b20000 79b30000 C:

ModLoad: 7cf00000 7cfef000 C:

ModLoad: 68e90000 68eb9000 C:

ModLoad: 77990000 77a2b000 C:

Breakpoint 0 hit

eax=00000001 ebx=00000001 ecx=0012fac0 edx=00130608 esi=00402300 edi=0012fe94

eip=004014ee esp=0012f99c ebp=0012f9a8 iopl=0 nv up ei ng nz ac pe nc

cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000296

test!CTestDlg::OnButton1+0xe:

004014ee c3 ret

不知道这样操作有没有什么错误的,

如果是对的,那怎么看它的调试信息?

如果不对,还请各位赐教。

*** ERROR: Symbol file could not be found. Defaulted to export symbols for -

这上面的这个提示对调试有没有影响?

谢谢!

@Jim

"C:websymbols用来存储系统符号.你可以指定其他的路径.Windbg会自动创建该文件夹",你可以先查看下,windbg有没有从ms的网站上把symbols下载回来?

刚开始我也尝试过让windbg从ms下载symblos,试了几次没有成功,最后我自己的做法是:从,下载相应操作系统所对应的完整的Symbol安装包,并进行安装。

Hi happyhippy ,

1.生成符号信息

更改Relase版的设置(2处):

一、Project Setting ->C/C++

Debug info:

选择Program Database

二、Project Setting ->Link

选中Generate debug info

我这样操作后就生成了个.pdb符号文件的。我将这个文件COPY到C:websymbols中的。

应该不会提示说找不到符号文件的啊,还请赐教。

谢谢。

Jim

WinDbg 设置断点

在windbg中,断点设置的地址形式有好多种,可以是以下几种:

1.虚拟地址:即给出直接地址,如 12345678

2.函数偏移量:如DriverEntry+5c.

3.源代码+行数 :`[[Module!]Filename][:LineNumber]`

4.对C++可以对模块中的某个类的方法设置断点:

设置断点语法:

1:无条件设置断点: bp Address

例如:

kd> bp 0040108c

kd> bp main+5c

kd> bp `source.c:31`

kd> bp MyClass::MyMethod

kd> bp MyClass__MyMethod

kd> bp @@( MyClass::MyMethod )

2:设置有条件断点

kd> bp Address "j (Condition) 'OptionalCommands'; 'gc' "kd> bp Address ".if (Condition) {OptionalCommands} .else {gc}"

这两种设断点语法是等价的。

bp,bu,bm 的区别:

bu:Set Unresolved Breakpoint

bp 和bu的区别:

1:bp是立即生效,且马上被转化为内存中的某个地址,如果调式模块被改变,bp指向的地址不会变,而bu只是和symbol文件相关联,模块改变的时候,指向的symbol的offset或者plus是不变的

2: bp指定的断点在模块unload之后从bl列表中删除,而bu的断点是永远存在的。

3:在windbg的可视源码或者可视的反汇编代码中所设的断点都是bu模块的断点

bm:Set Symbol Breakpoint

使用bm设置断点支持正则表达式的模式匹配,所以可以使用他来设置多个断点

如果正则表达式被匹配的话,他的效果将和bu设置的是一样的

例如:

0:000> bm dbgtest!*main*

1: 00413530 @!"dbgtest!wmain"

2: 00411810 @!"dbgtest!__tmainCRTStartup"

3: 004117f0 @!"dbgtest!wmainCRTStartup"

使用bp和bm /a的风险:

当wndbg在设置软件断点在代码段的时候,windbg将程序指令替换为断点指令,但是当断点设置在数据段的时候,将会将程序数据替换为断点指令,从而导致数据被修改,因此在设置断点在数据段的时候,推荐使用 ba(ba (Break on Access).)指令

控制断点的方法

使用下面一些方法来控制或显示断点:

bl (Breakpoint List)命令列出当前存在的断点和他们的状态。

bp (Set Breakpoint) 命令设置新断点。

bu (Set Unresolved Breakpoint) 命令设置新断点。使用bu设置的断点和bp设置的断点特点不同,详细信息查看后面的内容。

bm (Set Symbol Breakpoint) 在匹配指定格式的符号上设置断点。

ba (Break on Access)命令设置数据断点。这种断点在指定内存被访问时触发。(可以在写入、读取、执行或发生内核I/O时触发,但不是所有处理器都支持所有的内存访问断点。更多信息,查看ba (Break on Access)。)

bc (Breakpoint Clear) 命令移除一个或多个断点。

bd (Breakpoint Disable) 命令暂时禁用一个或多个断点。

be (Breakpoint Enable) 命令重新启用一个或多个断点。

br (Breakpoint Renumber)命令修改一个已存在的断点的ID。

(仅WinDbg) 反汇编窗口(Disassembly window) 和 源码窗口(Source

windows) 会将设置了断点的行高亮。已启用的断点为红色,禁用的断点为黄色,如果当前程序计数器(EIP)位置是断点位置则显示为紫色。

(仅WinDbg) Edit | Breakpoints 命令或ALT+F9快捷键打开Breakpoints对话框。该对话框会列出所有断点,所以可以用它来禁用、启用、删除已存在的断点或设置新断点。

(仅WinDbg) 如果光标在反汇编窗口或源码窗口中,可以按下F9或点击工具栏上的Insert or remove 按钮 () 来在光标所在行上设置断点。如果在当前窗口不是反汇编窗口或源码窗口时按下快捷键或点击上述按钮,则和使用Edit | Breakpoints具有相同效果。

每个断点都有一个关联的10进制数字称为断点ID 。该数字在各种命令中用于指定断点。

未定断点:BU vs. BP

如果一个断点是设置在某个还未加载的函数名上,则称为延迟、虚拟或未定断点。

(这些术语可交替使用。) 未定断点没有被关联到任何具体被加载的模块上。每当一个新的模块被加载时,会检查该函数名。如果这个函数出现,调试器计算虚拟断点的实际位置并启用它。

使用bu设置的断点自动被认为是未定断点。如果断点在一个已加载模块中,则会启用并正常生效。但是,如果模块之后被卸载并重新加载,这个断点不会消失。而使用bp设置的断点会立即绑定到某个地址。

bp和bu断点有以下三个主要的不同点:

bp 断点的位置总是被转换成地址。如果某个模块改变了,并且bp设置的地址位置改变,断点还是在原来的位置。而bu断点仍然和使用的符号值关联(一般是符号加上偏移),它会一直跟踪符号的地址,即使这个地址已经改变。

如果bp的断点地址在某个已加载模块中找到,并且该模块之后被卸载,则该断点会从断点列表中移除。而bu断点经过反复的卸载和加载仍然存在。

用bp设置的断点不会保存到WinDbg 工作空间(workspaces)中,而使用bu设置的断点会保存。

当在WinDbg 反汇编窗口或源码窗口中使用鼠标设置断点时,调试器创建的是bu断点。

初始断点

当调试器启动一个新的目标程序时,初始断点在主映像和所有静态加载的DLL被加载、DLL初始化例程被调用之前自动触发。

调试器附加到一个已存在的用户模式程序时,初始断点立即触发。

-g 命令行选项使得WinDbg或CDB跳过初始断点。在这时可以自动执行命令。更多信息,查看控制异常和事件。

如果想启动新调试目标并在实际的程序即将开始执行的时候中断下来,就不要使用-g选项。应该让初始断点被触发。当调试器激活之后,在main或winmai函数上设置断点并使用g (Go) 命令。之后所有初始化过程都会运行并且程序在main函数即将执行时停止。

关于内核模式的自动断点的更多信息,查看崩溃和重起目标机。

断点中的地址

断点支持几种地址语法,包括虚拟地址、函数偏移和源码行号。例如,可以使用下面的方法之一来设置断点:

0:000> bp 0040108c

0:000> bp main+5c

0:000> bp `source.c:31`

关于这些语法的更多信息,查看数值表达式语法, 源码行语法,以及各个命令的主题。

断点的数量

在内核模式下,最多可以使用32个断点。在用户模式下,可以使用任意数量的断点。

数据断点的数量由目标处理器架构决定。

方法的断点

如果要在MyClass类的MyMethod方法上设置断点,可以使用两种不同语法:

用MASM表达式语法,可以用双冒号或者双下划线来指定一个方法。

0:000> bp MyClass::MyMethod

0:000> bp MyClass__MyMethod

用C++表达式语法,必须用双冒号指定方法。

0:000> bp @@( MyClass::MyMethod )

如果要使用更复杂一些的断点命令,应该使用MASM表达式语法。表达式语法的更多信息,查看表达式求值。

用户空间和系统空间

每个用户模式应用程序在虚拟内存0x00000000 到0x7FFFFFFF 的地址称为用户

空间。

当WinDbg或CDB在小于0x80000000的地址上下断时,断点是在单个进程的指定的用户空间的地址设置。用户模式调试时,当前进程决定了虚拟地址的意义。更多信息,查看控制进程和线程。

在内核模式,可以使用bp、 bu、 ba 命令和Breakpoints 对话框在用户空间设置断点。必须首先使用.process /i (或在一些内核空间的函数上的指定进程的断点)来将目标切换成当前进程上下文,并使用该进程上下文来指定拥有目标地址空间的用户模式进程。

用户模式的断点总是和设置该断点时进程上下文为激活状态的进程关联起来。如果有用户模式调试器在调试该进程,而还有一个内核模式调试器在调试进程运行的机器,即使断点由内核调试器设置,它中断时也是进入用户模式调试器。这时可以从内核模式调试器中断系统,或使用.breakin (Break to the Kernel

Debugger) 命令来将控制权交给内核调试器。

注意 如果目标机运行在Microsoft Windows NT 4.0上,则不能使用内核调试器在用户空间中设置断点。

断点伪寄存器(Pseudo-Registers)

如果在某个表达式中想引用某个断点的地址,可以使用一个$bpNumber 语法的伪寄存器,Number是断点ID。关于该语法的更多信息,查看伪寄存器语法。

设置断点时的风险

当使用内存地址或符号加偏移的方式设置断点时,一定不能将断点设置到一条指令的中间。

例如,有下面一段汇编代码。

770000f1 5e pop esi

770000f2 5b pop ebx

770000f3 c9 leave

770000f4 c21000 ret 0x10

770000f7 837ddc00 cmp dword ptr [ebp-0x24],0x0

前三条指令只有1个字节长。但是第四条指令有3字节长。(包含在0x770000F4,0x770000F5, 和0x770000F6三个地址的字节)如果要在该指令上使用bp、bu或 ba设置断点,则必须将地址指定为0x770000F4 。

如果使用ba命令在0x770000F5 地址设置了断点,处理器将在该位置设置断点。但是 该断点永远不会被触发,因为处理器认为0x770000F4 才是这条指令的实际地址。

如果使用bp 或bu命令在 0x770000F5 设置断点,调试器在这个位置会写入断点。但是由于调试器使用如下方法设置断点,它可能造成目标运行错误:

1. 调试器保存0x770000F5 地址的内容,并用一条断点指令写入该地址。

2. 如果用调试器显示这段内存的内容,并不会显示被写入的断点指令,而是显示那里原来"应该是"的内容。即调试器显示原来的内存,或者断点设置之后该内存被修改的内容。

3. 如果使用BC命令删除断点,调试器将断点位置的内存恢复成原始值。

当在0x770000F5设置断点时,调试器保存它的值并写入断点指令。但是当程序运行时到达0x770000F4 时,会将它视为一条多字节指令的第一个字节。处理器将0x770000F4、0x770000F5可能还有后面的一些字节当作一条指令。这会产生各种非正常的行为。

因此,当使用bp、bu或ba 命令设置断点时,要确定断点在合适的地址上。如果使用WinDbg图形界面来添加断点就不用在意这样的情况,因为它会自动选择正确的地址。

断点命令

可以在断点中包含一条命令用于在断点触发时自动执行。

也可以包含一条用于执行的命令字符串。但是,其中任何恢复程序执行的命令(例如g和t)都会终止命令列表的执行。

例如下面的命令在MyFunction+0x47中断,写入一个dump文件并恢复执行。

0:000> bu MyFunction+0x47 ".dump c:; g"

注意 如果正在从内核调试器控制用户模式调试器,不要在命令字符串中使用g (Go) 。串口的速度可能跟不上该命令,并且不能再中断到CDB中。关于这种情况的更多信息,查看从内核调试器控制用户模式调试器。

@!""

@!"" 语法用于在MASM求值器中进行转义,使得符号解析支持任意文本。必须以@!"开始并以引号(")结束。如果不使用该语法,则在MASM表达式的符号名中不能使用空格、大于小于号(<, >)和其他特殊字符。模板和重载是符号中需要这种引号的主要原因。也可以使用如下的@!"" 语法来设置bu 命令。

0:000> bu @!"ExecutableName!std::pair,std::allocator > >::operator="

这个例子中,ExecutableName 是一个可执行文件的名字。

这种转义语法在C++中比C中更加有用(例如重载的操作符),因为C函数名中不会存在空格(或特殊字符)。但是,该语法在托管代码中也同样重要,因为.NET

Framwork中非常多的使用重载。

条件断点

可以设置仅在特定条件下被触发的断点。关于这类断点的更多信息,查看设置条件断点。

VMware 加 Windbg 进行驱动源码级调试

用VMware 和 WinDbg 进行驱动源码调试

一、环境的搭建

1) 设置VMware虚拟机的Serial Port。

打开VM--》settings--》HardWare -->

然后按next, 选择Output to named pipe.

按next,

分别填入:.pipecom_1

This end is the server.

The other end is a virtual machine.

选中yield CPU on poll

2)启动虚拟机,进入系统,修改启动项。(此例为windows xp) 编辑c:这个文件如下:

[boot loader]

timeout=30

default=multi(0)disk(0)rdisk(0)partition(1)WINDOWS

[operating systems]

multi(0)disk(0)rdisk(0)partition(1)WINDOWS="Microsoft Windows XP Professional"

/noexecute=optin /fastdetect

multi(0)disk(0)rdisk(0)partition(1)WINDOWS="Microsoft Windows XP Professional -Debug"

/fastdetect /debug /debugport =com1

/baudrate=115200

3)修改com1属性

在设备管理器中找到com并设置com1。

安装后在桌面建立一个快捷方式。然后右键点属性,点快捷方式。在目标后追加:

-b

s=0

-k

com:pipe,port=.pipecom_1,baud=115200,reset

其中各个参数的意义如下:

-b :目标机器连接上后立即中断

-k : 连接类型等等。

4、设定windbg的symbol path.

其中d:symbolslocal为符号表在本机的存放目录。

后边的为符号表的网络地址。当需要解析时,windbg会自动到网上下载符号表。

二、开始调试

1.启动虚拟机,在系统选项中选择第二个,不回车。

2.点击桌面的windbg快捷方式。

3.虚拟机开始运行,windbg如下:

此后就可以进行调试了。

三、驱动源码调试

1.以check模式编译驱动程序。

然受在符号表目录中设置驱动编译产生的*.pdb文件的目录。

在源码文件目录中设置驱动源码所在的目录。

在可执行文件目录中设置编译好的驱动文件所在的目录。

2.

windbg调试实例(1)

1、崩溃发生过程

程序执行过程中,崩溃,弹出mssagebox,提示R6034错误。查看r6034错误:表示运行库的manifest设置不正确,

2、提取dump过程

1)查看任务管理器,崩溃的进程还在。判定可以用Windbg截获dump

2)打开windbg,file--attach to a process,选择崩溃进程如;3)使用命令 .dump /mf

4)确认提取成功:查看在d盘根目录下是否有文件。

3、分析崩溃原因

1) 打开windbg,file--open crash dump ,打开dump文件

2) 查看崩溃文件版本:使用lm v m test* 获得崩溃进程文件文件信息

01 0:008> lm v m test*

02 start end module name

03 00400000 00526000 test (deferred)

04 Image path: C:"Program Files (x86)"test"

05 Image name:

06 Timestamp: Wed Dec 10 17:29:43 2008 (493F8C07)

07 CheckSum: 0012ACC3

08 ImageSize: 00126000

09 File version:

10 Product version:

11 File flags: 0 (Mask 3F)

12 File OS: 40004 NT Win32

13 File type: Dll

14 File date:

15 Translations: b0

16 InternalName: test

17 ProductVersion: 1,0,1126,34

18 FileVersion: 2008,12,10,34

3)加载对应符号pdb,根据Timestamp: Wed Dec 10 17:29:43 2008 (493F8C07),找到对应的pdb文件,选择file--symble file path 将pdb文件路径加入。

4)分析

01 0:008> ~*k 看所有线程的堆栈

02 0 Id: 990.c38 Suspend: 1 Teb: 7efdd000 Unfrozen

03 ChildEBP RetAddr

04 WARNING: Stack unwind information not available. Following frames may be wrong.

05 0017d878 75ae81c8 user32!WaitMessage+0x15

06 0017d8ac 75af478a user32!GetCursorFrameInfo+0x7c

07 0017d958 75af46f5 user32!SetMessageQueue+0x4e8

08 0017dab0 75b2d2c3 user32!SetMessageQueue+0x453

09 0017db0c 75b2d342 user32!MessageBoxTimeoutW+0x52

10 0017db40 75b2d390 user32!MessageBoxTimeoutA+0x76

11 0017db60 75b2d3d5 user32!MessageBoxExA+0x1b

12 0017db7c 718f986e user32!MessageBoxA+0x18

13 0017dbc0 718f1c2c msvcr80!__crtMessageBoxA+0x1b4

14 0017dbe4 718f2217 msvcr80!_NMSG_WRITE+0x162

15 0017dc20 718f2348 msvcr80!__p__winver+0x13c

16 0017dc30 776dfcc0 msvcr80!_CRTDLL_INIT+0x1d

17 0017dc50 776e9b28 ntdll!RtlReleasePebLock+0x28

18 0017dd48 776e95ae ntdll!LdrFindResourceDirectory_U+0x9bf

19 0017dfcc 777029db ntdll!LdrFindResourceDirectory_U+0x445

20 0017e250 765e4d50 ntdll!RtlDestroyQueryDebugBuffer+0x48d5

21 0017e2b4 765e4dca kernel32!LoadLibraryExW+0x112

22 0017e2c8 004265e9 kernel32!LoadLibraryW+0x11 此处为程序到系统程序的一个临界点。分析首先从这里分析起。

23 0017e524 00426e43 test!KLoadDllUtility::AutoSearchLoadLibrary+0x5f [e:"kloaddllutility.h @ 32]

24 0017e554 00426877 test!KPopSupport::InitializeKPopSystem+0x29 [e:" @ 1037]

25 ……………………

分析线程堆栈,一般情况下,出现messagebox都是不正常的。由于此次崩溃系统有弹出messagebox,而刚好第一个线程堆栈有messagebox相关字眼,所以怀疑是第一个线程堆栈出现问题。

C++语言:

01 0:008> dd 0017e2c8 dd (dd表示以四个字节的方式查看ebp信息。)

02 0017e2c8 0017e524 004265e9 0017e314 004d3578 (0017e524表示上一个函数的ebp; 004265e9 表示函数返回地址 0017e314 表示函数的参数 查看msdn,loadlibary只有一个参数,并且参数是一个字符串,所以只有0017e314有效,并且表示一个地址。)

03 0017e2d8 02724fd8 00000000 00000000 00000000

04 0017e2e8 00000000 026fc1a8 00000000 00000000

05 0017e2f8 00000000 00000008 0000000f 00000000

06 0017e308 00000000 00000000 00000000 003a0063

07 0017e318 0050005c 006f0072 00720067 006d0061

08 0017e328 00460020 006c0069 00730065 00280020

09 0017e338 00380078 00290036 004b005c 006e0069

10 0:008> db 0017e314 (db以字节的方式查看参数内容)

11 0017e314 63 00 3a 00 5c 00 50 00-72 00 6f 00 67 00 72 00 c.:.".

12 0017e324 61 00 6d 00 20 00 46 00-69 00 6c 00 65 00 73 00 a.m. .F.s.

13 0017e334 20 00 28 00 78 00 38 00-36 00 29 00 5c 00 4b 00 .(.x.).".e.

14 0017e344 69 00 6e 00 67 00 73 00-6f 00 66 00 74 00 5c 00 s.".

15 0017e354 4b 00 69 00 6e 00 67 00-73 00 6f 00 66 00 74 00 s.

16 0017e364 20 00 49 00 6e 00 74 00-65 00 72 00 6e 00 65 00 .n.e.

17 0017e374 74 00 20 00 53 00 65 00-63 00 75 00 72 00 69 00 t. .u.r.i.

18 0017e384 74 00 79 00 20 00 32 00-30 00 30 00 38 00 5c 00 t.y. .".

19 0:008> db (db继续显示,使内容显示完全)

20 0017e394 53 00 43 00 4f 00 4d 00-2e 00 64 00 6c 00 6c 00 S.C.d.l.l.

21 0017e3a4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

22 0017e3b4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

23 0017e3c4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

24 0017e3d4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

25 0017e3e4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

26 0017e3f4 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................

27 0017e404 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ...............

4、分析结果总结

分析发现 :调用了.根据对产品功能的了解,test不会调用该dll,说明调用出错,调错了文件,接下来需要做的是理清test调用流程,修改bug啦。至于为啥错误id为r6034,是因为需要运行库,而test进程内部未设定。

1、程序崩溃发生过程

这是一个对文件进行处理的模块,而处理模块在处理之前,需查询被处理的文件是否值得处理。这个任务执行过程中发生了崩溃,问题就发生在查询模块。

2、提取dump文件

3、分析dump:

1)启动windbg,file--open crash dump 配置符号库,reload完成 。

2)使用命令 :.ecxr获得进程崩溃时寄存器的内容

0:021> .ecxr (意指恢复崩溃时所有寄存器的内容,包括堆栈等)

eax=0532d414 ebx=00000fec ecx=000003fb edx=00000000 esi=0532c428 edi=00000000

eip=750f53ea esp=0490f22c ebp=0490f234 iopl=0 nv up ei pl nz na pe nc

cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206

msvcr80!memmove+0x5a:

750f53ea f3a5 rep movs dword ptr es:[edi],dword ptr [esi]

es:002b:00000000=???????? ds:002b:0532c428=00000000

3)使用命令:k 显示堆栈

0:021> k (.ecxr不能直接显示堆栈,需使用k显示堆栈)

*** Stack trace for last set context - .thread/.cxr resets it

ChildEBP RetAddr

WARNING: Stack unwind information not available. Following frames may be wrong.

0490f234 03ba4806 msvcr80!memmove+0x5a(kxewfsys调用memmove,memmove是个字符串操作函数,能把字符串的一部分复制到另一部分,这里出问题,可能是复制时传递的指针有问题,或者字符串的大小有问题)

0490f25c 03ba2547 kxewfsys!__ovfl_get+0xa6 (调用memmove之前内部的一些处理)

0490f27c 03ba4a74 kxewfsys!__bt_cmp+0x77

0490f2a8 03ba2679 kxewfsys!__bt_search+0x74

0490f2c4 03ba1409 kxewfsys!__bt_get+0x49

0490f2d8 03ba7448 kxewfsys!IKBDBImpl::Get+0x19

0490f2f8 03ba750d kxewfsys!CFdbFileInfo::Search+0x28

[e:eingsoft_eubabuildbuild_srckicekice_ @ 483]

0490f338 03ba9c63 kxewfsys!CFdbFileInfo::QueryFileInfo+0x4d

[e:eingsoft_eubabuildbuild_srckicekice_ @ 546]

0490f380 028210d1 kxewfsys!CFdbManager::QueryFileInfo+0xa3

[e:eingsoft_eubabuildbuild_srckicekice_ @ 370]

(kxewfsys 是处理查询模块)

*** ERROR: Symbol file could not be found. Defaulted to export symbols for -

0490f390 03b1323c kxewhite!kxe_white_query_file_info+0x21

[e:eingsoft_eubabuildbuild_srckicekice_ @ 103] (将文件提交进行查询.)

0490f45c 03b13442 kspfeng!KSEGetAddonEntries+0x21fbc (是文件处理模块用到的公共功能的封装文件)

*** ERROR: Symbol file could not be found. Defaulted to export symbols for -

0490f498 03abcaa4 kspfeng!KSEGetAddonEntries+0x221c2

0490f4c8 03b07fd5 ksecore+0x1caa4

0490f52c 03b1c52b kspfeng!KSEGetAddonEntries+0x16d55

0490f860 5019dd7c kspfeng!KSEGetAddonEntries+0x2b2ab

0490f864 01c95695 0x5019dd7c

0490f868 1a77217c 0x1c95695

0490f86c 01c95693 0x1a77217c

0490f870 1a7982dc 0x1c95693

0490f874 01c95693 0x1a7982dc

4、总结崩溃原因

查询模块,对文件路径的处理存在bug。

本文来自CSDN博客,转载请标明出处:

Windbg的学习记录(一)

Windbg是Microsoft退出的一款调试工具,它不像Visual Studio是针对特殊用例的调试器,它的调试手段覆盖了整个操作系统。有些时候程序的运行崩溃令人困惑找出原因也相当费时费力(可能也和方法的不正确有关)。Windbg可以帮助我们比Visual Studio更细致的进行调试,包括操作系统的信息、进程运行的状态、时间和环境变量、汇编指令、call stack等等,很多情况下可以查出许多隐性的错误。所以对于Windbg的学习和使用是对于开发人员更综合的要求,需要掌握和具备一些更基础的知识。

现在以调试一个简单的程序为例,看一下Windbg可以提供给我们的有用信息。运行Windbg选择File -> Open Executable打开可执行文件,然后设置Symbol file路径(如果想要更多关于操作系统信息的Symbol file需要到Microsoft网站下载)。在命令行窗口输入x加上可执行文件名加上!再加上代码中的函数名可以获得函数的入口地址,这样就可以方便的设置调试断点。如图:输入x cpp2!getConstBuffer获得getConstBuffer的地址

输入bp(break point)加上函数入口地址就可以设置函数端点。比如bp 004113c0,然后运行程序输入g(即go)可以看到源代码的函数被增加了相应的断点。如图:你会看到再次运行时程序会在相应的断点前停下。现在停止在地址为004113c0的getConstBuffer函数下。bp还可以输入如bp cpp2!getConstBuffer的方式定义函数断点。

使用k命令可以查看当前的callstack。继续输入g让程序运行会看到最后程序停留在出现错误的位置,Exception是Access Violation。看到一条mov指令在写入eax指向的内存,这就是出现错误的指令。使用dc可以查看eax上的内存数据。即输入dc eax便可以快速查看,发现就是在修改字符+的时候出现了错误。如图:

如果有操作系统的Symbol file那么可以继续进行调试,即查看eax的相应内存页的属性。输入!address eax便可以查看了,发现这块区域的内存是直读的即是常量指针。所以使用mov命令会导致程序出错。

输入u加上地址可以查看反汇编指令,使用!address加上某个函数的地址也可以查看内存信息。比如使用x cpp2!getConstBuffer获得地址,然后使用u 004113c0和!address 004113c0查看更详细的信息。

Windbg可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。由于大部分程序员不需要做Kernel模式调试, 我在这篇文章中不会介绍Kernel模式调试。Kernel模式调试对学习Windows核心极有帮助。如果你对此感兴趣,可以阅读Inside Windows 2000和Windbg所带的帮助文件。

这篇文章得主要目的是介绍WINDBG的主要功能以及相关的命令。关于这些命令的详细语法,请参阅帮助文件。对文章中提到的许多命令,WINDBG有相应的菜单选项。

如何得到帮助

在命令(Command)窗口中输入.hh 命会调出帮助文件令。

.hh keyword

会显示关于keyword的详细命令。

启动DEBUGGER

Windbg可以用于如下三种调试:

1.

远程调试:你可以从机器A上调试在机器B上执行的程序。具体步骤如下:

在机器B上启动一个调试窗口(Debug Session)。你可以直接在Windbg下运行一个程序或者将Windbg附加(Attach)到一个进程。

在机器B的Windbg命令窗口上启动一个远程调试接口(remote):

.server npipe:pipe=PIPE_NAME

PIPE_NAME是该接口的名字。

在机器A上运行:

windbg –remote npipe:server=SERVER_NAME,pipe=PIPE_NAME

SERVER_NAME是机器B的名字。

1.

Dump文件调试:如果在你的客户的机器上出现问题,你可能不能使用远程调试来解决问题。你可以要求你的用户将Windbg附加到出现问题的进程上,然后在命令窗口中输入:

.dump /ma File Name

创建一个Dump文件。在得到Dump文件后,使用如下的命令来打开它:

windbg –z DUMP_FILE_NAME

1.

本地进程调试:你可以在Windbg下直接运行一个程序:

Windbg “path to executable” arguments

也可以将Windbg附加到一个正在运行的程序:

Windbg –p “process id”

Windbg –pn “process name”

注意有一种非侵入(Noninvasive)模式可以用来检查一个进程的状态并不进程的执行。当然在这种模式下无法控制被调试程序的执行。这种模式也可以用于查看一个已经在Debugger控制下运行的进程。具体命令如下:

Windbg –pv –p “process id”

Windbg –pv –pn “process name”

调试多个进程和线程

如果你想控制一个进程以及它的子进程的执行,在Windbg的命令行上加上-o选项。Windbg中还有一个新的命令.childdbg 可以用来控制子进程的调试。如果你同时调试几个进程,可以使用 | 命令来显示并切换到不同的进程。

在同一个进程中可能有多个线程。~命令可以用来显示和切换线程。

调试前的必备工作

在开始调试前首先要做的工作是设置好符号(Symbols)路径。没有符号,你看到的调用堆栈基本上毫无意义。Microsoft的操作系统符号文件(PDB)是对外公开的。另外请注意在编译你自己的程序选择生成PDB文件的选项。如果设置好符号路径后,调用堆栈看起来还是不对。可以使用lm, !sym noisy, !reload 等命令来验证符号路径是否正确。

Windbg也支持源码级的调试。在开始源码调试前,你需要用.srcpath设置源代码路径。如果你是在生成所执行代码的机器上进行调试,符号文件中的源码路径会指向正确的位置,所以不需要设置源代码路径。如果所执行代码是在另一台机器上生成的,你可以将所用的源码拷贝(保持原有的目录结构)的一个可以访问的文件夹(可以是网络路径)并将源代码路径设为该文件夹的路径。注意如果是远程调试,你需要使用.lsrcpath来设置源码路径。

静态命令:

显示调用堆栈:在连接到一个调试窗口后,首先要知道的就是程序当前的执行情况k* 命令显示当前线程的堆栈。~*kb会显示所有线程的调用堆栈。如果堆栈太长,Windbg只会显示堆栈的一部分。.kframes可以用来设置缺省显示框架数。

显示局部变量:接下来要做通常是用dv显示局部变量的信息。CTRL+ALT+V可以切换到更详细的显示模式。关于dv要注意的是在优化过的代码中dv的输出极有可能是不准确的。这时后你能做的就是阅读汇编代码来发现你感兴趣的值是否存储在寄存器中或堆栈上。有时后当前的框架(Frame)上可能找不到你想知道的数据。如果该数据是作为参数传到当前的方法中的,可以读一读上一个或几个框架的汇编代码,有可能该数据还在堆栈的某个地址上。静态变量是储存在固定地址中的,所以找出静态变量的值较为容易。.Frame(或者在调用堆栈窗口中双击)可以用来切换当前的框架。注意dv命令显示的是当前框架的内容。你也可在watch窗口中观察局部变量的值。

显示类和链表: dt可以显示数据结构。比如dt PEB 会显示操作系统进程结构。在后面跟上一个进程结构的地址会显示该结构的详细信息:dt PEB 7ffdf000。

Dl命令可以显示一些特定的链表结构。

显示当前线程的错误值:!gle会显示当前线程的上一个错误值和状态值。!error命令可以解码HRESULT。

搜索或修改内存:使用s 命令来搜索字节,字或双字,QWORD或字符串。使用e命令来修改内存。

计算表达式:?命令可以用来进行计算。关于表达式的格式请参照帮助文档。使用n命令来切换输入数字的进制。

显示当前线程,进程和模块信息:!teb显示当前线程的环境信息。最常见的用途是查看当前线程堆栈的起始地址,然后在堆栈中搜索值。!peb显示当前进程的环境信息,比如执行文件的路径等等。lm显示进程中加载的模块信息。

显示寄存器的值:r命令可以显示和修改寄存器的值。如果要在表达式中使用寄存器的值,在寄存器名前加@符号(比如@eax)。

显示最相近的符号:ln Address。如果你有一个C++对象的指针,可以用来ln来查看该对象类型。

查找符号:x命令可以用来查找全局变量的地址或过程的地址。x命令支持匹配符号。x kernel32!*显示中的所有可见变量,数据结构和过程。

查看lock:!locks显示各线程的锁资源使用情况。对调试死锁很有用。

查看handle:!handle显示句柄信息。如果一段代码导致句柄泄漏,你只需要在代码执行前后使用!handle命令并比较两次输出的区别。有一个命令!htrace对调试与句柄有关的Bug非常有用。在开始调试前输入:

!htrace –enable

然后在调试过程中使用!htrace handle_value 来显示所有与该句柄有关的调用堆栈。

显示汇编代码:u。

程序执行控制命令:

设置代码断点:bp/bu/bm 可以用来设置代码断点。你可以指定断点被跳过的次数。假设一段代码KERNEL32!SetLastError在运行很多次后会出错,你可以设置如下断点:

bp KERNEL32!SetLastError 0x100.

在出错后使用bl 来显示断点信息(注意粗体显示的值):

0 e 77e7a3b0 004f (0100) 0:*** KERNEL32!SetLastError

重新启动调试(.restart命令)并设置如下的断点:

bp Kernel32!SetLastError 0x100-0x4f

Debugger会停在出错前最后一次调用该过程的地方。

你可以指定断点被激活时Debugger应当执行的命令串。在该命令串中使用J命令可以用来设置条件断点:

bp `:143` "j (poi(MyVar)”0n20) ''; 'g' "

上面的断点只在MyVar的值大于32时被激活(g命令

条件断点的用途极为广泛。你可以指定一个断点只在特殊的情况下被激活,比如传入的参数满足一定的条件,调用者是某个特殊的过程,某个全局变量被设为特殊的值等等。

设置内存断点:ba可以用来设置内存断点。调试过程中一个常见的问题是跟踪某些数据的变化。如下的断点:

ba w4 0x40000000 "kb; g"

可以打印出所有修改0x40000000的调用堆栈。

控制程序执行:p, pa,t, ta等命令可以用来控制程序的执行。

控制异常和事件处理:Debugger的缺省设置是跳过首次异常(first chance

expcetion),在二次异常(second chance exception)时中断程序的执行。sx命令显示Debugger的设置。sxe和sxd可以改变Debugger的设置。

sxe clr

可以控制Debugger在托管异常发生时中断程序的执行。常用的Debugger事件有:

av 访问异常

eh C++异常

clr 托管异常

ld 模块加载

-c 选项可以用来指定在事件发生时执行的调试命令。