NetRoc's Blog

N-Tech

 

WinDbg 文档翻译----19

cc682/NetRoc

http://netroc682.spaces.live.com/

使用断点

断点位于可执行代码中,它使得操作系统停止程序执行并中断到调试器。 然后,就可以分析目标和执行调试器命令。

可以通过指定虚拟地址、模块和函数偏移或源码文件和行号来设置断点(当在源码模式中时)。如果在某个例程上设置断点并且没有使用偏移,则当运行到这个例程上时就会触发断点。

还有下面一些类型的断点:

  • 和特定线程关联的断点。
  • 在被触发前可以被挑过指定次数的断点。
  • 触发时自动执行特定命令的断点。
  • 设置在非执行内存上的断点,并等待该内存被读写时中断。

如果在用户模式下调试多于一个进程,每个进程都有它自己的断点集合。要查看或修改某个进程的断点,必须将该进程设置为当前进程。关于当前进程的更多信息,查看控制进程和线程

控制断点的方法

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

  • 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设置的断点会立即绑定到某个地址。

bpbu断点有以下三个主要的不同点:

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

初始断点

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

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

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

如果想启动新调试目标并在实际的程序即将开始执行的时候中断下来,就不要使用-g选项。应该让初始断点被触发。当调试器激活之后,在mainwinmai函数上设置断点并使用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的地址上下断时,断点是在单个进程的指定的用户空间的地址设置。用户模式调试时,当前进程决定了虚拟地址的意义。更多信息,查看控制进程和线程

在内核模式,可以使用bpbuba 命令和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三个地址的字节)如果要在该指令上使用bpbuba设置断点,则必须将地址指定为0x770000F4 。

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

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

  1. 调试器保存0x770000F5 地址的内容,并用一条断点指令写入该地址。
  2. 如果用调试器显示这段内存的内容,并不会显示被写入的断点指令,而是显示那里原来"应该是"的内容。即调试器显示原来的内存,或者断点设置之后该内存被修改的内容。
  3. 如果使用BC命令删除断点,调试器将断点位置的内存恢复成原始值。

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

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

断点命令

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

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

例如下面的命令在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中非常多的使用重载。

条件断点

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

posted on 2008-05-05 14:28 NetRoc 阅读(1470) 评论(0)  编辑 收藏 引用

只有注册用户登录后才能发表评论。

导航

统计

常用链接

留言簿(6)

随笔档案(99)

文章分类(35)

文章档案(32)

Friends

Mirror

搜索

最新评论

阅读排行榜

评论排行榜