Windbg使用教程

目录

1. Windbg简介

2. Windbg安装

2.1 版本说明

2.2 安装

2.2.1 windbg

2.2.2 Windbg Preview

3. Windbg分析崩溃的两种方式

4. 静态分析dump

4.1 示例

4.2 64位上下文转换为32位上下文

5. Windbg动态调试

5.1 什么情况下使用Windbg动态调试

5.2 附加到进程调试

5.3 启动进程调试

6. Windbg使用技巧

6.1 打开dmp时不加载pdb

7. Windb命令

7.1 Windbg帮助文档

7.2 常用功能命令

7.3 线程相关命令

7.3 模块相关命令--lm

7.4 pdb加载命令

7.5 dump导出命令

7.6 信息查看命令


1. Windbg简介

Windbg是微软提供的Windows平台下强大的用户态和内核态调试利器,给我们分析Windows上软件的异常提供了极大的便利与有力的支持,比原始的直接查看代码分析异常的效率要高的多。Windbg在某些方面甚至要比微软的Visual Studio还要强大,作为Windows平台的开发人员,必须要会使用该工具分析问题。

Windbg在排查内存越界、内存访问违例、stack overflow线程栈溢出、空指针与野指针、死循环、死锁、高CPU占用、内存泄露、GDI对象泄露、函数调用约定不一致导致的栈不平衡等方面,有着独到的优势。

其实,Windbg入门的门槛较低,只需掌握一些常用的Windbg命令即可。另外,有时需要深入研究问题时,可能还要使用到IDA反汇编工具,去查看二进制文件中的汇编代码的上下文,查看汇编代码才能最直观地看出软件崩溃的最直接的原因。所以,除了掌握Windbg之外,我们还需要学会使用IDA反汇编工具,二者结合使用。

2. Windbg安装

2.1 版本说明

Windbg有无需安装的6.0绿色版,也有需要安装的10.0的版本。

10.0版本相对6.0版本要智能很多,很多时候要查看相关内容时不用再输入各种复杂的命令,只要点击工作区显示的超链接即可查看。

在6.0版本中,很多命令都是需要手动输入的,很多命令也比较复杂。另外,6.0版本的Windbg无法识别VS2017编译出来的pdb,所以要处理VS2017的程序问题,需要使用10.0版本的Windbg的。

2.2 安装

2.2.1 windbg

新版本的Windbg已经内置到Windows SDK的安装包中,这个安装包可以到微软的官方网站上下载

① 下载winsdksetup.exe

下载地址:Windows 调试工具 - Windows drivers | Microsoft Learn

下载安装程序winsdksetup.exe

② 下载windbg安装包

双击winsdksetup.exe

两种安装方式:

  • 在线安装表示直接下载安装
  • 下载到本地表示将windbg的安装包下载到本地再安装

只勾选Debugging Tools for Windows选项即可

下载完成以后如下所示:

③ 双击安装windbg

安装完成以后如下所示:

2.2.2 Windbg Preview

本文以下说明以Windbg Preview为例进行说明

3. Windbg分析崩溃的两种方式

Windbg支持两种使用方式:

  • 静态分析dump文件--常用
  • 动态调试目标进程
    • 直接启动进程进行调试
    • 附加到正在运行的目标进程进行调试

4. 静态分析dump

4.1 示例

第一步:使用Windbg打开dmp

此时会自动分析和下载需要的Windows系统的PDB文件。

这时候,我们可以根据Windbg的基本分析得到大概的崩溃原因。如上图所示,崩溃原因是:Access violation,code是c0000005,所以崩溃原因是:内存访问违例。

Windows下溃崩的原因也就那几种,注意遇到积累即可。例如:

  • Access violation:内存访问违例
  • Stack overflow:线程栈溢出
  • Integer divided by zero:除0异常

第二步:设置pdb路径,然后输入.reload命令。

输入命令:

如果pdb加载失败,可以使用强制加载:.reload /f XXX.dll。

即使是强制加载,二进制文件和pdb也需要是对应的,即:同时编译生成,否则强制加载也会失败。

第三步:输入.ecxr命令,切换到发生异常的线程上下文。

此时是可以看到崩溃时的各种寄存器的值,例如:rcx、rdi、eax、ebx等,也可以看到发生异常时的汇编指令是什么。

此时我们可以根据汇编指令和对应寄存器的值来找到初步的线索。例如:

  • 指令中是否访问了小于64KB地址的内存,从而引发内存访问违例--可能是空指针或野指针导致
  • 指令中是否访问了内存态的内存地址,从而引发内存访问违例--可能是内存越界导致的

第四步:输入kn或kv或kp命令,查看异常发生时的函数调用堆栈。

其中kv能同时查看到调用时给函数传递的参数。

崩溃堆栈解析结果以!为分界:

  • !前面为模块名,不显示dll和exe
  • !后面为函数名、偏移值、文件名、代码行号
    • 偏移值:每个模块被加载到内存中时都有一个首地址,偏移值表示调用堆栈相对于当前模块的偏移量。

如果此时没有pdb被加载进来,则只能看到模块名称和偏移值,无法看到具体函数名称和代码行号。我们可以使用lm vm命令查看对应模块的详细信息,帮助我们找到对应的pdb文件。

第五步:设置源码路径

然后就可以看到详细的崩溃函数,并且点击调用堆栈中的函数时,Windbg的源代码视图就会自动跳到对应的代码行上

此时就可以结合函数调用堆栈、变量信息、源码进行分析为何崩溃了。

4.2 64位上下文转换为32位上下文

有时dump文件可能是从64位系统的资源管理器中导出的,Windbg打开后看到的函数调用堆栈很奇怪,完全和业务代码对不上,此时可能是因为Windbg使用的是64位的异常上下文。

而我们的程序是32位的,所以需要使用.effmach X86命令转换成32位的上下文。

补充说明:

在64位Windows系统上抓32位进程的dmup文件时,如果用的是64位任务管理器,那么在用Windbg加载后,要用!wow64exts.sw切换到X86模式下。如果不想做这步切换,就要用32位的任务管理器来生成dmp文件。32位任务管理器在:C:\Windows\SysWOW64\Taskmgr.exe

5. Windbg动态调试

5.1 什么情况下使用Windbg动态调试

情景1:

当我们的软件在用户那里发生崩溃,但是我们自己本机又无法复现时,我们不可能在用户那里装一个VS2015去调试。但是我们可以把pdb、源码文件拷贝到用户机器,使用Windbg在用户机器进行调试。

如果短期内无法查出原因,崩溃以后我们可以使用.dump命令保存一份full dump文件,之后再详细分析。

情景2:

通过Windbg动态调试进程,如果程序在运行过程中发生异常,Windbg调试器会立即感知并中断,然后就可以查看发生异常的汇编指令和函数调用堆栈。

情景3:

5.2 附加到进程调试

第一步:附加进程

第二步:设置pdb文件路径

第三步:打开源码(.cpp文件即可)

第四步:设置断点

第五步:开始调试

其调试快捷键和VS相同

5.3 启动进程调试

第一步:设置启动进程和参数

第二步:设置pdb路径

第三步:打开源码文件

第四步:启动调试

第五步:设置断点,再次启动调试

6. Windbg使用技巧

6.1 打开dmp时不加载pdb

Windbg在打开时,默认会去微软符号服务器下载对应的pdb,这是因为我们设置的pdb路径中包含:srv*。

但是某些时候我们希望快速打开dmp,而不加载pdb。我们可以修改符号路径,将其指向一个不存在pdb的路径,这样在打开dmp时,去指定的路径下加载pdb,由于找不到pdb直接就可以直接打开dmp。

7. Windb命令

7.1 Windbg帮助文档

安装版本的windbg在安装目录下有帮助文档:debugger.chm

7.2 常用功能命令

.cls

清屏

.ecxr

切换到发生异常的线程 上下文

kn或kv或kp

查看异常发生时的函数调用堆栈。

其中kv能同时查看到调用时给函数传递的参数

!analyze -v

自动分析异常,并给出详细的分析结果

示例如下:

!analyze -v

这个命令可以详细分析异常,并给出详细的分析结果

7.3 线程相关命令

~*kb

查看所有线程堆栈,*通配符表示所有线程

~0kb

查看主线程堆栈

● ~波浪线就像一个线头thread,所以线程相关命令都由~开头

● 0是主线程的序号  

● kb表示输出调用堆栈中函数的前3个参数;

~#

查看崩溃/异常的线程号

~.

查看当前线程号

~1s

切换到1号线程

.thread

显示当前线程信息

示例如下:

~*kb

查看所有线程堆栈,*通配符表示所有线程

~0kb

查看主线程堆栈

  • ~波浪线就像一个线头thread,所以线程相关命令都由~开头
  • 0是主线程的序号  
  • kb表示输出调用堆栈中函数的前3个参数;

7.3 模块相关命令--lm

lm:List Loaded Modules,用于显示指定的已加载模块

lm

查看模块信息

主要显示模块名称,模块开始地址,模块结束地址,模块路径等信息。

lm vm XXXX

查看指定模块的pdb是否加载,如果已经加载,则会显示出对应pdb的路径

lm vm命令能看到pdb文件有没有加载成功,能看到目标模块代码段的起始地址和结束地址,能看到目标模块文件生成时的时间戳。

查看时间戳有什么作用呢?通过查看时间戳,我们可以确定文件的编译时间,进而根据时间去找对应的pdb文件。

示例如下:

如下所示使用lm vm命令查看,可以看到没有显示出pdb的路径,则表示对应模块的pdb没有被加载

7.4 pdb加载命令

.reload

重新加载pdb

.reload /f xxxx.dll

强制加载xxxx.dll的pdb。

即使是强制加载,二进制文件和pdb也需要是对应的,即:同时编译生成,否则强制加载也会失败。

7.5 dump导出命令

.dump /ma D:\test.dmp

将当前进程的上下文导出到dump文件中

7.6 信息查看命令

.process

显示当前进程信息

!dlls

查看模块信息

!cpuinfo

显示cpu信息

!address

显示内存信息

!vm

显示虚拟内存信息

!memusage

显示物理内存信息

.srcpath

查看当前源代码搜索路径等信息

.sympath

查看当前符号搜索路径等信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值