Windbg使用指南(1)---常用命令

debug常用命令解析:
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 = <no type information>
上面的命令找到了printf函数的入口地址在77bd27c2
 
0:001> x ntdll!GlobalCounter
7c99f72c ntdll!GlobalCounter = <no type information>
上面的命令表示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 <PERF> (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' = <no type information>
77682680 ole32!OpaqueDataInfo::`vftable' = <no type information>
接下来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源代码操作指令.lsflsclsllsp
. 指令打一个源文件,可以打开一个全路径的文件,也可以通过函数地址来打开并定位到源文件中函数的位置,如 . –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 = <no type information>
8046e0c0 nt!KeServiceDescriptorTable = <no type information>
kd> ln 8046e100
(8046e100)     nt!KeServiceDescriptorTableShadow     | (8046e140)     nt!MmSectionExtendResource
Exact matches:
nt!KeServiceDescriptorTableShadow = <no type information>
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文件能够方便的保存发生问题时进程的状态,方便事后分析。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值