Windbug调试崩溃处理方式

调试工具:

可以自己保存DUMP,自己分析DUMP给出具体的错误逻辑线程

DebugDiagx64.msi

DebugDiagx86.msi

此和操作系统关联,64位使用X64,32位系统使用X86


WINDBUG 64位调试64位程序,32位调试32位程序

X86 Debuggers And Tools-x86_en-us.msi

X64 Debuggers And Tools-x64_en-us.msi


设置启动路径:


set PATH=%PATH%;C:\Program Files\Microsoft Silverlight\4.0.50917.0

set PATH=%PATH%;c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
set _NT_SYMBOL_PATH=srv*F:\symbols*https://msdl.microsoft.com/download/symbols

windbg

F:\symbols 为本地符号文件



打开 DebugDiag  Collection


指定进程



选择需要抓取DUMP的进程



下一步 选择breakpoints  ,添加断点 add breakpoints ,选择退出函数NTDLLL,Action Type选择FULL Userdump,抓补数据数分为几段,选择Action Limit为5  然后点击OK。设置好过滤条件。


最后保存下,


然后下一步 设置DUMP备份的目录和名称



下一步 选择 Active


完成后开始捕获崩溃的异常DUMP


捕获完成后使用自带的DUMP分析工具进行初步的笼统的定位分析,DebugDiag  Analysis加载DUMP文件进行分析



选择加载的DUMP


直到返回对应的网站分析界面。根据分析界面定位问题


还可以使用WINDUG来分析DUMP日志

打开WINDBG ,选择FILE----Open Crash Dump 加载DUMP文件




KB来查看错误日志的记录:


根据编号可以查看对应的线程问题号出现的异常和BUG

.sympath 查看加载的PDB文件的具体路径和从网上下载的路径。




如果提示符号文件没有加载使用 .reload 重新加载符号文件。

.reload /f EnvCheckDLL.dll

.reload /f SetupBeforeCpy.dll

这个时候会提示具体的此文件的加载的路径,然后把对应的符号文件复制到对应的需要加载的路径下。


返回对应的SetupBeforeCpy.dll没有符号文件。

使用 !sym noisy 弹出对应的符号文件加载路径

弹出需要加载的此文件的路径



然后再重新的加载.reload /f SetupBeforeCpy.dll

然后把对应的符号文件复制到显示的路径下



把对应的符号文件放到对应的提示路径下后,如果提示

Signature does not match 说明当时调用的DLL和此时生成的PDB不是同一次生成的,必须调用的DLL和pdb是同一次生成的。



如果输入一样的,就会提示加载成功 返回 OK

然后点击错误,就可以直接的定位错误的代码位置行。

如果一次不行就关闭WINDBG重新的打开就会再次加载添加到对应路径下的pdb文件。




set PATH=%PATH%;C:\Program Files\Microsoft Silverlight\4.0.50917.0
set PATH=%PATH%;c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727
set _NT_SYMBOL_PATH=srv*F:\symbols*https://msdl.microsoft.com/download/symbols
windbg





windbg常用命令
 
1 !address eax查看对应内存页的属性 2 vertarget 显示当前进程的大致信息 3 !peb 显示process Environment Block 4. lmvm 可以查看任意一个dll的详细信息 例如:0:026 lmvm msvcrt (deferred)表示察看msvcrt.dll的信息,但是没有加载 symbol可以通过.reload
1 !address eax查看对应内存页的属性
2 vertarget 显示当前进程的大致信息
3 !peb 显示process Environment Block
4. lmvm 可以查看任意一个dll的详细信息
 例如:0:026 lmvm msvcrt (deferred)表示察看msvcrt.dll的信息,但是没有加载
symbol可以通过.reload命令来加载
5.reload /!sym 加载符号文件
6 lmf 列出当前进程中加载的所有dll文件和对应的路径

0:018> lmf


 7 r 命令显示和修改寄存器上的值
r命令显示和修改寄存器上的值
0:018> r 显示寄存器的值

0:018> r eax=0 修改了寄存器,把eax的值修改为0x0


 8 d命令显示esp寄存器指向的内存
 如下
0:018>d esp
用dd命令直接指定054efc14地址
0:018>dd 054efc14 
注意:第二个d表示DWORD格式,此外还有db(byte),du(Unicode),dc(char)等等。
 数据查看指令 d{a|b|c|d|D|f|p|q|u|w|W}
 d{b|c|d|D|f|p|q}分别是显示:
byte&ASCII, double-word&ASCII,double-word,double-precision,float,pointer-sized,quad-word数据;
DA用于显示ASCII,DU用于显示UNICODE;
BYB,BYD,显示binary和Byte及binary和DWORD

补充一个DV,用于查看本地变量用的


9 e命令可以用来修改内存地址
 跟d命令一样,e命令后面也可以跟类型后缀,比如ed命
 令表示用DWORD的方式修改。下面的命令把054efc14地址上的值修改为11112222。
0:018>ed 054efc14 11112222
修改后可以用dd命令来查看内存。
0:018>dd 0543fc14 L4 L4参数指定内存区间的长度为4个DWORD,这样输出只有1行,
 而不是8行了。


10s 命令用来搜索内存具体见help文档


11!runaway 可以显示每一个线程的cpu消耗
0:018> !runaway 结果如下:
0:83c 0 days 0:00:00.406
 13:bd4 0 days 0:00:00.046
 10:ac8 0 days 0:00:00.046
 24:4f4 0 days 0:00:00.031
上面输出的第一列是线程的编号和线程ID,后一列对应的是该线程在用户态模式中的
 总的繁忙时间。
 在该命令加上f参数,还可以看到内核态的繁忙时间,当进程内存占用率比较高的时候
 ,通过该命令可以方便的找到对应的繁忙线程。


12 ~ 命令是用来切换目标线程
0:018> ~ 可以显示线程的信息

0:018> ~0s把当前的线程切换到0号线程,也就是主线程,切换后提示符会变为0:000.


 13 ~* 命令列出当前进程中的所有线程的详细信息
14~*kb命令列出所有线程的堆栈
15 k 命令用来显示当前线程的堆栈,如下
0:018> k
跟d命令一样,k后面也可以跟很多后缀,比如kb kp,kn,kv,kl等,这些后缀控制了
 显示的格式和信息。
 栈指令k[b|p|P|v]
这四条指令显示的内容类似,但是每个指令都有特色,KB显示三个参数,Kp显示所有的参数,但需要Full Symbols或Private PDBSymbols支持。KP与Kp相似,只是KP将参数换行显示了。Kv用于显示FPO和调用约定,KD,用于显示Stack的Dump,在跟踪栈时比较有用。
 这些指令区分大小。

16 u命令把指定地址上的代码翻译成汇编输出
0:018> u 7739d023 
 USER32!NtUserWaitMessage:
7739d023 b84a120000 mov eax,0x124a
 7739d028 ba0003fe7f mov edx,0x7ffe0300
 7739d02d ff12 call dword ptr [edx]
 7739d02f c3 ret
如果符号文件加载正确,可以用uf命令直接反汇编整个函数,比如uf USER32! NtUserWaitMessage


 17 x 查找符号的二进制地址如下
0:018> x msvcr!printf
 77bd27c2 msvcrt!printf = 
上面的命令找到了printf函数的入口地址在77bd27c2


 0:001> x ntdll!GlobalCounter
 7c99f72c ntdll!GlobalCounter = 
上面的命令表示ntdll!GlobalCounter这个变量保存的地址是7c99f72c。
 注意:符号对应的是变量和变量所在的地址,不是变量的值,上面只是找到GlobalCounter这个变量的值是7c99f72,要找到变量的值,需要用d命令读取内存地址来获取。


X命令还支持通配符,比如x ntdll !*命令列出ntdll模块中的所有的符号,以及对应的二进制地址。


18 dds 打印内存地址上的二进制值
 同时自动搜索二进制值对应的符号。
 比如要看看当前**中保存了那些函数地址,就可以检查ebp指向的内存
0:018>dds ebp
 0013ed98 0013ee24
 0013ed9c 75ecb30f BROWSEUI!BrowserProtectedThreadProc+0x44
 0013eda0 00163820
 0013eda4 0013ee50
 0013eda8 00163820
 0013edac 00000000
 0013edb0 0013ee10
 0013edb4 75ece83a BROWSEUI!__delayLoadHelper2+0x23a
 0013edb8 00000005
 0013edbc 0013edcc
 0013edc0 0013ee50
 0013edc4 00163820
 0013edc8 00000000
 0013edcc 00000024
 0013edd0 75f36d2c BROWSEUI!_DELAY_IMPORT_DESCRIPTOR_SHELL32
 0013edd4 75f3a184 BROWSEUI!_imp__SHGetInstanceExplorer
 0013edd8 75f36e80 BROWSEUI!_sz_SHELL32
 0013eddc 00000001
 0013ede0 75f3726a BROWSEUI! urlmon_NULL_THUNK_DATA_DLN+0x116
 0013ede4 7c8d0000 SHELL32!_imp__RegCloseKey (SHELL32+0x0)
 0013ede8 7c925b34 SHELL32!SHGetInstanceExplorer


这里dds命令从ebp指向的内存地址0013ed98开始打印,第一列是内存地址的值,第二列是地址上对应的二进制数据,第三列是二进制对应的符号。上面的命令自动找到了75ecb390f对应的符号是BROWSEUI!BrowserProtectedThreadProc +0x44.


 Com interface 和c++ vtable里面的成员函数都是顺序排列的。所以,dds命令可以方便的找到虚函数表中的具体的函数地址,比如用下面的命令可以找到OpaqueDatinfo类型中虚函数的实际函数地址。
 首先通过x命令找到OpaqueDataInfo虚函数地址
0:000> x ole32!OpaqueDataInfo::vftable’
7768265c ole32!OpaqueDataInfo::`vftable'' = 
 77682680 ole32!OpaqueDataInfo::`vftable'' = 
接下来dds命令可以打印出虚函数表中的函数名字
0:000> dds 7768265c 
 19 .frame 命令在栈中切换以便检查局部变量
 要查看局部变量的需要如下:
1查看线程的callstack
 0:018>knl
 00 0012f7a0 7c821c94 ntdll!KiFastSystemCallRet
 01 0012f7a4 7c836066 ntdll!NtRequestWaitReplyPort+0xc
 02 0012f7c4 77eaaba3 ntdll!CsrClientCallServer+0x8c
 03 0012f8bc 77eaacb8 kernel32!ReadConsoleInternal+0x1b8
 04 0012f944 77e41990 kernel32!ReadConsoleA+0x3b


第一列的号称为Frame num,通过.frame命令就可以切换到对应的函数中检查局部变量,比如我们检查kernel32!ReadConsoleA,这个函数的frame num是4,于是,我们如下
2 iframe切换到指定行号的函数中
0:018> .frame 4
 3然后调用x显示当前frame的局部变量,比如这个函数中有两个局部变量pcls和rawptr
 0:018> x
 0012fced pcls = 0x0039ba80
 0012fcd8 rawptr = 0x0039ba80


 20 dt 格式化显示资料
Dt命令格式化显示变量的资料和结构
0:000> dt pcls
 Local var @ 0x12fce4 Type MyCls*
 0x0039ba80 
 +0x000 str : 0x00416648 'abcd'
 +0x004 inobj : inner
上面的命令打印出pcls的类型是MyCls指针,指向的地址是0x0039ba80,其中的两个class成员的偏移分别在+0和+4,对应的值在第2列显示。加上-b -r参数可以显示inner class和数组的信息:
0:000> dt pcls -b -r
 Local var @ 0x12fce4 Type MyCls*
 0x0039ba80 
 +0x000 str : 0x00416648 'abcd'
 +0x004 inobj : innner
 +0x000 arr : 'abcd'
 [00] 97 ''a''
 [01] 98 ''b''
 [02] 99 ''c''
 [03] 100 ''d''
 [04] 0 ''''
 [05] 0 ''''
 [06] 0 ''''
 [07] 0 ''''
 [08] 0 ''''
 [09] 0 ''''
对于任意的地址,也可以手动指定符号类型来格式化显示。比如把0x0039ba80地址上的数据用MyCls类型来显示:
0:000> dt 0x0039ba80 MyCls
 +0x000 str : 0x00416648 'abcd'
 +0x004 inobj : innner
 21bp设定调试断点
 比如可以这样写:0:018>bp notepad!WinMain 在notepade的winmain函数处下断点
 断点的位置可以用符号来表示,如上,也可以直接用地址以及windbg的Pseudo_Register(虚拟寄存器)。比如,我们用$exentry表示进程的入口,那么可以用bp @$exentry在进程的入口设置断点,如果notepade的winmain的入口地址为01006420,那么断点也可以这么写
Bp 01006420
 bp mysource.cpp:143` 'j (poi(MyVar)”0n20) ''''; ''g'' '
意思就是:当myvar的值等于0x20时,g命令继续执行
 下面一个设置条件断点
0:001> bp exceptioninject!foo3 “k; .echo ‘breaks’ ; g”
在exceptioninject!foo3上设置断点后,每次断下来后,先用k显示callstack,然后用.echo命令输出简单的字符串‘breaks’,最后g命令继续执行。
 下面看一个更复杂的设置条件断点的例子:
ba w4 execptioninject!i ”j(poi(exceptioninject!i)<0n40) ‘.printf//”exceptioninject!i value is :%d//”,poi(exceptioninject!i); g’ ; ‘.echo stop!’ ”
首先ba w4 exceptioninject!i 表示在修改exceptioninject!i这个全局变量的时候,停下来,
j(judge)命令的作用就是对后面的表达式作条件判断如果为true,执行第一个单引号里面的命令,否则执行第2个单引号里面的命令。
 条件表达式是(poi(exceptioninject!i)<0n40),在windbg中excepioninject!i符号表示符号所在的内存地址,而不是符号的数值,相当于c语言的&操作符的作用,poi命令就是取这个地址上的值,相当于c语言的*操作符。所以这个条件判断的意思就是判断exceptioninject!i的值,是否小于十进制的40。如果为真,就执行第一个单引号,‘.printf//”exceptioninject!i value is :%d//”,poi(exceptioninject!i); g’,如果为假,就执行第二个单引号‘.echo stop!’
第一个单引号里有三个命令,.printf .echo 和g。这里的printf和c语言的printf函数语法一样,不过由于这个printf命令本身是在ba命令的双引号里面,所以需要用//来转义print中的引号。第一个引号的作用是:打印出当前exceptioninject!i的值,.echo命令换行 g命令继续执行
 第二个引号的作用就是显示stop,由于后面没有g命令,所以windbg会停下。
22 bm 使用模式匹配设置断点
 这个功能需要符号表的支持,bm可以通过模式一次设置多个断点,比如
bm mydriver!FastIO* 可以将所有与FastIO*模式匹配的函数下设置断点,比如FastIoRead ,FastIoWriter等函数都会被设置上断点。需要注意的是,bm命令需要full or export symbols支持。
23 ba 对内存访问设置断点 break on access
就是对于内存访问设置断点,对于在多核处理或者多核处理器调试的时候很有用,对于调试多线程也很有用,比如说,我们可以对一个全局变量设置断点,
ba mydriver!gMonitoreedDevices , 如果你认为这个变量的值被莫名的修改了,相信通过ba设置的断点,你可以很快找到是谁修改的。


 也可以这样
ba w4 0x4000000 'kb;g' 当0x4000000地址有写操作时,进入断点 。w表示类型为写 4表示长度为4个字节
24 bl 列出所有的断点 break list
 25 bc 清除断点 break clear
 26 be 开启断点 break enable
 27 bd禁用断点 break disable
以上提到的断点指令通过和j指令很容易形成条件断点,比如
bp USER32!GetMessageW 'r $t1=poi(esp+4);r $t2=poi(@$t1+4); j(@$t2 = 0x102 ) ''du @$t1+8 L2;gc'';''gc'''
这个条件断点,截取WM_CHAR消息,并将字符(包括中文)显示出来。
 条件断点的最简形式:bp Address 'j (Condition) ''OptionalCommands''; ''gc'' '
 Address是指令的地址,Condition是一个条件表达式,如果@eax=1,''OptionalCommands''是在断点被击中并且表达式成立时要执行的指令;gc指定是从一个条件断点返回,是不可少的一部分。
28跟踪指令T,TA,TB,TC,WT,P,PA,PC
 T指令单步执行,在源码调试状态下,可指源码的一行,根据不同的选项也可以为一行ASM指令;
TA单步跟踪到指定地址,如果没有参数将运行到断点处。
TB执行到分支指令,分支指令包括calls, returns, jumps, counted loops, and while loops
 TC执行到Call指令
WT Trace and Watch Data,一条强大指令,对执行流程做Profile,执行一下看看结果吧
P,PA,PC相信不用多做解释,大家也都明白了
29源代码操作指令.,lsf,lsc,ls,l,lsp
 .指令打一个源文件,可以打开一个全路径的文件,也可以通过函数地址来打开并定位到源文件中函数的位置,如. –a myapp!main,. j://mydriver//mydriver.c
 lsf指定一个源文件为当前源文件,使用lsc可显示当前指定的源文件ls可显示源文件的代码。Lsf可以使用全路径,如果源路径已经设置,也可以直接指定源文件名称。如lsf mydriver.c,lsf j://mydriver//mydriver.c
 lsc显示当前源文件
ls显示当前源文件的代码,如ls 200显示第200行
l 用于设置源文件选项
lsp 设置源文件行在调试时显示范围比如,
 显示当前行的前50,后50,lsp 100
但通常使用Windbg时,可以直接用Ctrl+O来打开并查看源文件
30 查询符号
kd> x nt!KeServiceDescriptorTable* 
 8046e100 nt!KeServiceDescriptorTableShadow = 
 8046e0c0 nt!KeServiceDescriptorTable = 
 kd> ln 8046e100 
 (8046e100) nt!KeServiceDescriptorTableShadow | (8046e140) nt!MmSectionExtendResource 
 Exact matches: 
 nt!KeServiceDescriptorTableShadow = 
 31!gle 查看LastError值
32指定进制的形式0x/0n/0t/y 分别表示 16/10/8/2进制
? 0x12345678+0n10 
 Evaluate expression: 305419906 = 12345682 
 33!sym noice/quiet symbol prompts开关
34.srcpath 设置源代码的路径
35dv查看本地变量
36!teb 显示当前线程的执行块(execution block)
37!peb 显示当前进程的执行块(execution block)
38ln[Address] 显示当前地址上的对象类型
39!locks 显示死锁
40!handle可以获取整个进程或者某一个handle的详细信息
 首先运行以下!handle,可以看到当前进程的每个一个handle的类型,以及统计信息
0:002>!handle
 Handle 4
 Type key
 Handle c
 Type keyEvent
…….
然后找到一个key,查看详细信息
0:001>!handle 4 f
就会列出这个handle的详细信息。
41!htrace命令检查操作句柄的历史记录
!htrace命令可以打印出指定的handle的最近几次调用堆栈
0:001>!htrace 384
 42!cs列出CriticalSection的详细信息
43!threadpool能看到完成端口,线城池工作线程和timer回调占线程池的情况
44.time 可以看到进程跑了多长时间
45 !dso 查看当前线程中有哪些对象,分析泄露时用到
46.dump保存进程的dump文件
Dump文件是进程的内存镜像,


 可当在调试器中打开dump文件时,使用上面的命令检查,看到的结果跟用调试检查进程看到的一样
.dump /ma c://testdump.dmp
这个命令把当前进程的镜像保存为c://testdump.dmp,其中/ms参数表示dump的文件应该包含进程的完整信息。
 在windbg中,通过file—open---open Crash dump菜单打开dump文件进行分析。打开文件后,运行调试命令看到的信息和状态就是dump文件保存时进程的状态。通过dump文件能够方便的保存发生问题时进程的状态,方便事后分析。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值