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 )
这两种设断点语法是等价的。
bp,bu,bm 的区别:
bp 和bu的区别:
1:bp是立即生效,且马上被转化为内存中的某个地址,如果调式模块被改变,bp指向的地址不会变,而bu只是和symbol文件相关联,模块改变的时候,指向的symbol的offset或者plus是不变的
2: bp指定的断点在模块unload之后从bl列表中删除,而bu的断点是永远存在的。
3:在windbg的可视源码或者可视的反汇编代码中所设的断点都是bu模块的断点
使用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 设置的断点会立即绑定到某个地址。
当在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)
如果在某个表达式中想引用某个断点的地址,可以使用一个$bp Number 语法的伪寄存器 ,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 设置断点,调试器在这个位置会写入断点。但是由于调试器使用如下方法设置断点,它可能造成目标运行错误:
-
- 调试器保存0x770000F5 地址的内容,并用一条断点指令写入该地址。
- 如果用调试器显示这段内存的内容,并不会显示被写入的断点指令,而是显示那里原来"应该是"的内容。即调试器显示原来的内存,或者断点设置之后该内存被修改的内容。
- 如果使用BC 命令删除断点,调试器将断点位置的内存恢复成原始值。
当在0x770000F5设置断点时,调试器保存它的值并写入断点指令。但是当程序运行时到达0x770000F4 时,会将它视为一条多字节指令的第一个字节。处理器将0x770000F4、0x770000F5可能还有后面的一些字节当作一条指令。这会产生各种非正 常的行为。
因此,当使用bp 、bu 或ba 命令设置断点时,要确定断点在合适的地址上。如果使用WinDbg图形界面来添加断点就不用在意这样的情况,因为它会自动选择正确的地址。
断点命令
可以在断点中包含一条命令用于在断点触发时自动执行。
也可以包含一条用于执行的命令字符串。但是,其中任何恢复程序执行的命令(例如g 和t )都会终止命令列表的执行。
例如下面的命令在MyFunction+0x47 中断,写入一个dump文件并恢复执行。
0:000> bu MyFunction+0x47 ".dump c:/mydump.dmp; g"
注意 如果正在从内核调试器控制用户模式调试器,不要在命令字符串中使用g (Go) 。串口的速度可能跟不上该命令,并且不能再中断到CDB中。关于这种情况的更多信息,查看从内核调试器控制用户模式调试器 。
@!"<chars>"
@!" <chars> " 语法用于在MASM求值器中进行转义,使得符号解析支持任意文本。必须以@!" 开始并以引号(" )结束。如果不使用该语法,则在MASM表达式的符号名中不能使用空格、大于小于号(<, >)和其他特殊字符。模板和重载是符号中需要这种引号的主要原因。也可以使用如下的@!"<chars>" 语法来设置bu 命令。
0:000> bu @!" ExecutableName !std::pair<unsigned int,std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> > >::operator="
这个例子中,ExecutableName 是一个可执行文件的名字。
这种转义语法在C++中比C中更加有用(例如重载的操作符),因为C函数名中不会存在空格(或特殊字符)。但是,该语法在托管代码中也同样重要,因为.NET Framwork中非常多的使用重载。
条件断点
可以设置仅在特定条件下被触发的断点。关于这类断点的更多信息,查看设置条件断点 。