WinDbg 调试实战入门 - 调试第三方程序(记事本)
调试信息的丰富度和易读性主要依赖于符号文件,调试器需要符号文件来获取有关代码模块的信息,例如函数名称和变量名称。如果有符号文件,则后续的工作难度会大大降低。本篇的调试流程简单,但覆盖了符号配置、断点设置、线程切换、查看堆栈这些基本的操作。Windbg 的命令十分强大,参数也很丰富,我们将在后续逐步学习掌握。
上一篇 ┇ 《WinDbg 源码级调试 C++ 程序》
在上一篇 《WinDbg 源码级调试 C++ 程序》文章中,我们讲到了 WinDbg 的安装和配置方法,并以一个自己写的程序为例进项源码级的调试,对 WinDbg 的功能和使用有了一个初步感性认识。这一篇中,我们将调试没有源码的第三方程序。这是大多数真实调试情境。
很多时候,我们需要去调试的程序并没有源码,或者源码不匹配。另外,例如 Windows 自身的程序或者第三方库、程序,我们是没有源码的。但需要了解它们的运行逻辑或查找卡死、崩溃原因,这个时候 WinDbg 就可以帮助我们。
下面我们以记事本为例,实战一次真实的调试流程。
🚩 调试目标
通过本次调试实战练习,我们掌握以下三点技能:
- 加载调试符号
- 设置断点
- 查看线程堆栈
流程很简单,用到的命令也不多。跟随本篇文章逐步操作,再自己独立完成几次,就掌握了最基本的调试方法。
▶ 加载调试程序符号
启动 Windbg.exe CTRL+E,或点击菜单 File/OpenExecutable 打开选择系统目录下的 notepad.exe
此时记事本进程已启动,但还看不到窗口,因为 Windbg 会暂停到程序入口处。
Windbg 进入等待命令状态。
首先,加载符号,分别输入如下两条命令:
.sympath srv*
.reload
通过 lm 命令查看下当前加载的模块和符号情况
如上图, notepad 后显示的是 deferred ,说明符号并没有加载上,我们需要输入以下两条命令强制符号重新加载:
!sym noisy
.reload /f
再次使用 lm 命令检查模块及符号信息
或者通过 !chksym 命令检查 notepad 模块的符号信息:
显示出了详细的符号信息,说明模块的符号都已经正确加载。
我们用 x 命令查看模块内部的符号信息。
x moduleName!symbolName
这将显示模块 moduleName 中名为 symbolName 的符号详细信息。如果 symbolName 为 *,则显示moduleName模块下的全部符号。
x notepad!*
符号信息很多,我们可以只查找关心的部分函数,比如我们想查看记事本的 main 函数,由于它是窗口程序,那么main函数的名称应该是 wWinMain,所以我们使用 x notepad!wWin* 以模糊匹配的方式搜索以 wWin 开头的函数列表:
其中 wWinMain 即为记事本程序的 main 函数。
▶ 设置断点
如果我们希望记事本进入 main 函数的时候可以被终端下来,则用 bu 命令下断点
bu notepad!wWinMain
检查断点是否成功
bl
确认成功后,按下 g 让程序跑起来,等待记事本运行到 WinMain
函数,它会被 windbg 终端暂停下来。
▶ 查看线程堆栈
此时,我们就可以用 k 命令查看堆栈信息:
或者 Alt + 6 调出堆栈窗口,以窗口形式显示出了同样的信息。
继续 g 让记事本运行,此时记事本窗口才正常显示出来。
刚才的操作验证了断点确实生效了,我们不妨再做个断点的试验,断下程序对 Windows API 的调用,例如写文件操作最终会调用到 ntdll中的ZwWriteFile函数,我们就在 ntdll 模块中下个断点,同样下断后用 bl 命令检查断点:
bu ntdll!ZwWriteFile
bl
我们看到 ntdll中的ZwWriteFile 断点生效了,同时之前的 wWinMain 断点也在断点列表中。用 g 命令 或 F5,让程序继续跑。
为了让程序触发文件写操作,在记事本窗口中,输入一些文本。 在“文件”菜单中,选择“保存”。 运行代码在 ZwCreateFile
处中断。
中断后,除了用前面提到的 k 命令可以查看堆栈,还可以使用 ~ 命令查看所有线程的列表。
或者通过 ALT+9 调出进程线程窗口,一样可以方便的查看线程列表,这里共显示了11个线程,线程的序号作为索引,从0开始。
每个线程有自己的堆栈,用 ~0s 命令切换线程, 如要查看线程 0 的堆栈,则输入
~0s
k
因为有了足够的符号,显示出了非常详细的堆栈信息 ,降低了调试难度。
💡
在以上命令输入后,有可能 windbg 会下载本地缺失的符号,处于忙的状态,等待下载完毕即可。
最后,输入 qd ,即可退出调试。
💬 总结
调试信息的丰富度和易读性主要依赖于符号文件,调试器需要符号文件来获取有关代码模块的信息,例如函数名称和变量名称。如果有符号文件,则后续的工作难度会大大降低。
本篇的调试流程简单,但覆盖了符号配置、断点设置、线程切换、查看堆栈这些基本的操作。Windbg 的命令十分强大,参数也很丰富,我们将在后续逐步学习掌握。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)