WinDBG
自己打算学习windbg,可是学习的过程中发现需要的资料都很凌乱,于是我就边学习边把自己收集的资料整理了一下,希望对初学者有用。
一 windbg介绍
商业软件的Debug和客户支持
内核驱动的调试,以及对驱动进行逆向工程时进行动态调试
研究Windows本身的内核或者软件
疑难BUG的调试,如死锁、COM调用、资源泄露、堆栈或者堆溢出
以性能优化为目的的调试
对调试目标基本不造成影响的非侵入式调试
调试前的必备工作
在开始调试前首先要做的工作是设置好符号(Symbols)路径。没有符号,你看到的调用堆栈基本上毫无意义。Microsoft的操作系统符号文件(PDB)是对外公开的。另外请注意在编译你自己的程序选择生成PDB文件的选项。如果设置好符号路径后,调用堆栈看起来还是不对。可以使用lm, !sym noisy, !reload 等命令来验证符号路径是否正确。 (具体请看第二节)
windbg也支持源码级的调试。在开始源码调试前,你需要用.srcpath设置源代码路径。如果你是在生成所执行代码的机器上进行调试,符号文件中的源码路径会指向正确的位置,所以不需要设置源代码路径。如果所执行代码是在另一台机器上生成的,你可以将所用的源码拷贝(保持原有的目录结构)的一个可以访问的文件夹(可以是网络路径)并将源代码路径设为该文件夹的路径。注意如果是远程调试,你需要使用.lsrcpath来设置源码路径。
TechTarget中国原创内容,原文链接:http://www.searchsecurity.com.cn/showcontent_18481.htm
二 配置符号和
安装与配置windbg的symbol(符号)
第一步 是从 http://www.microsoft.com/ddk/debugging 下载最新版本的 WinDBG,
因为符号服务器二进制文件是由 WinDBG 小组开发的。您将需要检查是否有 WinDBG
更新版本,因为该小组似乎具有相当紧凑的发布日程安排,并且每隔几个月就会发布
更新版本。
第二步 双击下载的文件安装windbg.安装时注意记住安装到那里了.
第三步 windbg访问符号需要两个文件(SYMSRV.DLL 和 SYMSTORE.EXE)所以添加主path
环境变量中它们的路径进去,即:你的windbg安装目录.
操作方法:在桌面我的电脑点右键--属性--高级--环境变量,在系统变量列表框中找到
path双击,在变量值最后面加一个分号再把你的安装目录写上.点确定. 这一步是告诉
windbg那两个文件放在什么地方.
第四步 新建一个环境变量_NT_SYMBOL_PATH 值为: SRV*c:\mysymbol*http://msdl.microsoft.com/download/symbols
操作方法:桌面我的电脑点右键--属性--高级--环境变量 ,点击新建,把上面的变量名
和变量值填上.这一步的意思是说告诉windbg,我的符号文件存放在c:mysymbol中(当然
其实里面什么也没有,甚至这个文件夹也不存在,不过没关系,系统找不到的话会给你创
建一个,并在上面的网址中去帮你下载符号文件放在里面)
第五步 运行windbg 打开一个exe文件或者附加到一个进程里去, 你会看到
Symbol search path is: SRV*c:\mysymbol* http://msdl.microsoft.com/download/symbols
打开c盘看到有一个新目录mysymbol,里面有windbg新下载的文件.
恭喜说明配置成功了.(我自己根据上面的教程,已经配置成功了)
第二节部分内容来自:http://hi.baidu.com/ziyan000/blog/item/5587be18333b010d35fa41da.html
当你安装成功,且配置完成后,就可以尝试运行调试代码了:
windbg是微软开发的免费源码级调试工具。windbg可以用于Kernel模式调试和用户模式调试,还可以调试Dump文件。
.hh keyword
会显示关于keyword的详细命令。
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的名字。
2. Dump文件调试:如果在你的客户的机器上出现问题,你可能不能使用远程调试来解决问题。你可以要求你的用户将windbg附加到出现问题的进程上,然后在命令窗口中输入:
.dump /ma File Name
创建一个Dump文件。在得到Dump文件后,使用如下的命令来打开它:
windbg –z DUMP_FILE_NAME
3. 本地进程调试:你可以在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 可以用来控制子进程的调试。如果你同时调试几个进程,可以使用 | 命令来显示并切换到不同的进程。
在同一个进程中可能有多个线程。~命令可以用来显示和切换线程。
三 常用命令介绍
WinDBG 提供了多种设断点的命令:bp, bu, bm, ba。
bp命令是在某个地址下断点,可以bp 0x7783FEB,也可以bp MyApp!SomeFunction。对于后者,WinDBG会自动找到MyApp!SomeFunction 对应的地址并设置断点。 但是使用bp的问题在于:1)当代码修改之后,函数地址改变,该断点仍然保持在相同位置,不一定继续有效; 2)WinDBG 不会把bp断点保存工作空间中。所以,我比较喜欢用bu命令。
bu命令是针对某个符号下断点。 比如bu MyApp!SomeFunction。在代码被修改之后,该断点可以随着函数地址改变而自动更新到最新位置。而且bu断点会保存在WinDbg工作空间中,下次启动Windbg的时候该断点会自动设置上去。
另外,在模块没有被加载的时候,bp断点会失败(因为函数地址不存在),而bu断点则可以成功。新版的WinDBG中bp失败后会自动被转成bu。
bm命令也是针对符号下断点。 但是它支持匹配表达式。很多时候你下好几个断点。比如,把MyClass所有的成员函数都下断点:bu MyApp!MyClass::* ,或者把所有以CreateWindow开头的函数都下断点:bu user32!CreateWindow* 。
以上三个命令是对代码下断点, 我们还可以对数据下断点。
ba命令就是针对数据下断点的命令,该断点在指定内存被访问时触发。命令格式为
ba Access Size [地址]
Access是访问的方式,比如e(执行),r(读/写),w(写)
Size是监控访问的位置的大小,以字节为单位。值为1、2或4,还可以是8(64位机)。
比如要对内存0x0483DFE进行写操作的时候下断点,可以用命令 ba w4 0x0483DFE
这里顺便提以下其他断点命令:
bl 列出所有断点
bc 清除断点
bd 禁用断点
be 启动被bd 命令经用的断点
==================
静态命令:
显示调用堆栈:在连接到一个调试窗口后,首先要知道的就是程序当前的执行情况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命令来修改内存。
显示当前线程,进程和模块信息:!teb显示当前线程的环境信息。最常见的用途是查看当前线程堆栈的起始地址,然后在堆栈中搜索值。!peb显示当前进程的环境信息,比如执行文件的路径等等。lm显示进程中加载的模块信息。
显示寄存器的值:r命令可以显示和修改寄存器的值。如果要在表达式中使用寄存器的值,在寄存器名前加@符号(比如@eax)。
显示最相近的符号:ln Address。如果你有一个C++对象的指针,可以用来ln来查看该对象类型。
查找符号:x命令可以用来查找全局变量的地址或过程的地址。x命令支持匹配符号。x kernel32!*显示Kernel32.dll中的所有可见变量,数据结构和过程。
查看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 `mysource.cpp: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 选项可以用来指定在事件发生时执行的调试命令。
四 附录
WinDbg调试手册 – 标准命令
WinDBG的大多数功能是以命令方式工作的, 本系列将介绍WinDBG的三类命令, 标准命令, 元命令和扩展命令.
标准命令
===============
标准命令用来提供适用于所有调试目标的基本调试功能.
所有基本命令都是实现在WinDBG内部的, 执行这些命令时不需要加载任何扩展模块. 大多数标准命令是一两个字符或者符号, 只有version等少数命令除外. 标准命令的第一个字符是不分大小写的, 第二个字符可能区分大小写. 迄今为止, WinDBG调试器共实现了130多条标准命令, 分为60多个系列. 为了便于记忆, 可以根据功能将标准命令归纳为如下18个子类.
控制调试目标执行
功能 | 命令 | 描述/助记 | 补充信息 |
恢复运行 | g | Go | ~123g, ~#g, ~*g |
跟踪执行 | t | Trace | |
单步执行 | p | Step | |
追踪监视 | wt | Trace and Watch Data |
寄存器相关
功能 | 命令 | 描述/助记 | 补充信息 |
观察和修改通用寄存器 | r | Registers | |
读写MSR寄存器 | rdmsr和wrmsr | Read MSR and Write MSR | |
设置寄存器显示掩码 | rm | Register Mask |
IO端口读写
功能 | 命令 | 描述/助记 |
读IO端口 | ib, iw, id | Input from port (byte, word, double word) |
写IO端口 | ob, ow, od | Output to port (byte, word, double word) |
内存控制
功能 | 命令 | 描述/助记 | 补充信息 |
观察内存 | d系列 | Display Memory | d, da, db, dc, dd, dD, df, dp, dq, du, dw, dW, dyb, dyd |
编辑内存 | e系列 | Enter Values | e, ea, eb, ed, eD, ef, ep, eq, eu, ew, eza, ezu |
搜索内存 | s | Search Memory | sb, sw, sd, sq, sa, su |
栈
功能 | 命令 | 描述/助记 | 补充信息 |
观察栈 | k系列 | Display Stack Backtrace | k, kb, kc, kd, kp, kP, kv |
设置维护断点
功能 | 命令 | 描述/助记 |
软件断点 | bp, bu, bm | Set Breakpoint, Set Unresolved Breakpoint, Set Symbol Breakpoint |
硬件断点 | ba | Break on Access |
管理断点 | bl | Breakpoint List |
清除,禁止,重新启用断点 | bc, bd, be | Breakpoint Clear, Breakpoint Disable, Breakpoint Enable |
线程
功能 | 命令 | 描述/助记 |
显示控制线程 | ~ | Thread Status |
进程
功能 | 命令 | 描述/助记 |
显示进程 | | | Process Status |
表达式
功能 | 命令 | 描述/助记 |
评估表达式 | ? | Evaluate Expression |
评估C++表达式 | ?? | Evaluate C++ Expression |
汇编, 反汇编
功能 | 命令 | 描述/助记 |
汇编 | a | Assemble |
反汇编 | u | Unassemble |
段
功能 | 命令 | 描述/助记 |
显示段的选择子 | dg | Display Selector: shows the segment descriptor for the specified selector |
执行命令文件
功能 | 命令 | 描述/助记 | 补充信息 |
运行命令脚本文件 | $ | Run Script File | $<, $><, $$<, $$><, $$>a< |
配置命令
功能 | 命令 | 描述/助记 | 补充信息 |
异常发生或者某事件发生时debuger的处理方式 | sx系列 | Set Exceptions | sx, sxd, sxe, sxi, sxn, sxr, sx- |
启用与禁止静默模式 | sq | Set Quiet Mode | sq sq{e|d} |
设置内核debugging选项 | so | Set Kernel Debugging Options | |
设置符号后缀 | ss | Set Symbol Suffix | ss [a|w|n] |
版本与系统信息
功能 | 命令 | 描述/助记 |
显示调试器和调试目标版本 | version | Show Debugger Version |
显示调试目标所在系统的信息 | vertarget | Show Target Computer Version |
检查符号
功能 | 命令 | 描述/助记 |
检查符号 | x | Examine Symbols |
源程序
功能 | 命令 | 描述/助记 | 补充信息 |
控制和显示源程序 | ls系列 | List Source Lines | ls, lsa, lsp, lsc, lsf |
调试符号
功能 | 命令 | 描述/助记 |
加载调试符号 | ld | Load Symbols |
搜索相邻符号 | ln | List Nearest Symbols |
显示模块列表 | lm | List Loaded Modules |
调试会话
功能 | 命令 | 描述/助记 |
结束调试会话 | q | Quit |
结束远程调试 | Quit | |
结束调试会话并分离调试目标 | qd | Quit and Detach |
在命令编辑框中输入一个问号(?), 可以显示出主要的标准命令和每个命令的简单介绍.
摘自:<软件调试> 张银奎
本节原文来自:http://xuser.org/archives/222
总结
感谢那些提供windbg资料的童鞋,希望越来越多的人,给我们提供准确,负责任的学习资料!!