现在的位置: 首页 > 综合 > 正文

Windows程序调试器 – WinDbg

2012年11月25日 ⁄ 综合 ⁄ 共 9612字 ⁄ 字号 评论关闭

windbg是一个强大win平台上的调试工具,现在介绍一下它的基本功能。

加入要调试程序代码debugee.cpp如下:

// debugee.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

void loop(void)
{
	for (int i = 0; i < 1000; i ++)
	{
		printf("loop [%d]\n", i);
	}
}

int main(int argc, char* argv[])
{
	printf("Hello World!\n");

	loop();
	return 0;
}

===============================================================

断点

===============================================================

设置符号断点,如果正确加载了符号表文件(-y 指定, 或者ctrl+s)。

bm debugee!main

在main函数入口的位置设置断点,用用tab键看看,还有提示哦。

在源文件的行号上设置断点,如果正确指定了源文件路径(-srcpath指定,或者ctrl+p)

bp `debugee.cpp:10` 

bp `:10`

条件断点指令, 当i=500的时候停止,否则从断点开始继续执行gc:

bp `debugee.cpp:10` "j (poi(i)=500) ' ' ;  'gc' "

执行到800以上的时候停止

bp `debugee.cpp:10` "j (poi(i)>0n800) ' ' ;  'gc' "


硬件数据断点,很酷的功能哦(真正的硬件断点有个数限制,cpu的dr寄存器个数)

在变量i的内存位置开始写入4个字节大小的时候,断点

ba w 4 i

在变量i的内存位置开始读取4个字节大小的时候,断点

ba r 4 i

指令断点

先u一段loop函数的反汇编看看

0:000> u debugee!loop
debugee!loop [H:\temp\debugee\debugee.cpp @ 7]:
0040d6f0 55              push    ebp
0040d6f1 8bec            mov     ebp,esp
0040d6f3 83ec44          sub     esp,44h
0040d6f6 53              push    ebx
0040d6f7 56              push    esi
0040d6f8 57              push    edi
0040d6f9 8d7dbc          lea     edi,[ebp-44h]
0040d6fc b911000000      mov     ecx,11h
0:000> u
debugee!loop+0x11 [H:\temp\debugee\debugee.cpp @ 7]:
0040d701 b8cccccccc      mov     eax,0CCCCCCCCh
0040d706 f3ab            rep stos dword ptr es:[edi]
0040d708 c745fc00000000  mov     dword ptr [ebp-4],0
0040d70f eb09            jmp     debugee!loop+0x2a (0040d71a)
0040d711 8b45fc          mov     eax,dword ptr [ebp-4]
0040d714 83c001          add     eax,1
0040d717 8945fc          mov     dword ptr [ebp-4],eax
0040d71a 817dfce8030000  cmp     dword ptr [ebp-4],3E8h
0:000> u
debugee!loop+0x31 [H:\temp\debugee\debugee.cpp @ 8]:
0040d721 7d13            jge     debugee!loop+0x46 (0040d736)
0040d723 8b4dfc          mov     ecx,dword ptr [ebp-4]
0040d726 51              push    ecx
0040d727 681c204200      push    offset debugee!`string' (0042201c)
0040d72c e82f39ffff      call    debugee!printf (00401060)
0040d731 83c408          add     esp,8
0040d734 ebdb            jmp     debugee!loop+0x21 (0040d711)
0040d736 5f              pop     edi
0:000> u
debugee!loop+0x47 [H:\temp\debugee\debugee.cpp @ 12]:
0040d737 5e              pop     esi
0040d738 5b              pop     ebx
0040d739 83c444          add     esp,44h
0040d73c 3bec            cmp     ebp,esp
0040d73e e89d39ffff      call    debugee!_chkesp (004010e0)
0040d743 8be5            mov     esp,ebp
0040d745 5d              pop     ebp
0040d746 c3              ret

好的,接下来在,调用printf的时候给它断住。

bc * 

ba e 1 0x0040d72c 
注意:ba e指令断点的时候,字节限制必须是1,

===============================================================

语句控制

===============================================================

执行一句,遇到函数,飞过去,相当于gdb的next,vc的f10

执行一句,遇到函数,跟进去,相当于gdb的step,vc的f11

t

执行到一个ret语句停止,一般用来完成一个子函数, 执行到子函数即将返回的位置

pt 或者tt 区别是如果当前是ret指令怎么办

执行到下一个call指令

pc

0:000> pc
eax=cccccccc ebx=7ffdd000 ecx=00000000 edx=003710d8 esi=00000000 edi=0012ff70
eip=004010d6 esp=0012ff20 ebp=0012ff80 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
>   19: 	printf("Hello World!\n");
debugee!main+0x36:
004010d6 e875000000      call    debugee!printf (00401150)
0:000> u
debugee!main+0x36 [H:\temp\debugee\debugee.cpp @ 19]:
004010d6 e875000000      call    debugee!printf (00401150)
004010db 83c404          add     esp,4
004010de c745fc00000000  mov     dword ptr [ebp-4],0
004010e5 e820ffffff      call    debugee!ILT+5(?loopYAXXZ) (0040100a)
004010ea eb13            jmp     debugee!main+0x5f (004010ff)
004010ec 682c504200      push    offset debugee!`string' (0042502c)
004010f1 e85a000000      call    debugee!printf (00401150)
004010f6 83c404          add     esp,4

从子函数中执行到调用子函数语句的下一句位置 go up

gu

在子函数的开始位置执行wt, 显示子函数的所有执行路径,最后给出统计信息

wt

   34     0 [  4]         debugee!write_char
   70    68 [  3]       debugee!write_string
   34     0 [  4]         debugee!write_char
   84   102 [  3]       debugee!write_string
  742   520 [  2]     debugee!_output
   34     0 [  3]       debugee!write_char
  789   554 [  2]     debugee!_output
   34     0 [  3]       debugee!write_char
  808   588 [  2]     debugee!_output
   29  1469 [  1]   debugee!printf
    7     0 [  2]     debugee!_ftbuf

641971 instructions were executed in 641970 events (0 from other threads)

Function Name                               Invocations MinInst MaxInst AvgInst
debugee!_aulldiv                                    790      17      17      17
debugee!_aullrem                                    790      15      15      15
debugee!_flush                                      299      51      51      51
debugee!_ftbuf                                      300       7      44      43
debugee!_isatty                                     300      14      14      14
debugee!_output                                     300     726     808     792
debugee!_stbuf                                      300      59      59      59
debugee!_write                                      299     330     380     370
debugee!get_int_arg                                 300      12      12      12
debugee!loop                                          1    3323    3323    3323
debugee!printf                                      300      29      37      36
debugee!write_char                                 3190      34      34      34
debugee!write_multi_char                            300      10      10      10
debugee!write_string                                600      12      84      43
kernel32!WriteConsoleA                              299      11      11      11
kernel32!WriteConsoleInternal                       299      60      61      60
kernel32!WriteFile                                  299      30      30      30
kernel32!_SEH_epilog                                598       9       9       9
kernel32!_SEH_prolog                                598      19      19      19
kernel32!__security_check_cookie                    299       5       5       5
ntdll!CsrClientCallServer                           299      39      39      39
ntdll!KiFastSystemCall                              299       2       2       2
ntdll!NtRequestWaitReplyPort                        598       1       2       1
ntdll!ZwRequestWaitReplyPort                        299       1       1       1

299 system calls were executed

Calls  System Call
  299  ntdll!KiFastSystemCall

===============================================================

数据显示

===============================================================

0:000> dt i

Local var @ 0x12ff28 Type int
803
0:000> ?? i
int 803
0:000> ? poi(i)
Evaluate expression: 803 = 00000323

===============================================================

内存

===============================================================

显示内存

0:000> db i L20
0012ff18  16 00 00 00 80 ff 12 00-ea 10 40 00 00 00 00 00  ..........@.....
0012ff28  00 00 00 00                                      ....
0:000> ds i L20
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0012ff80  "..."
0:000> dd i L20
0012ff18  00000016 0012ff80 004010ea 00000000
0012ff28  00000000 7ffd4000 cccccccc cccccccc
0012ff38  cccccccc cccccccc cccccccc cccccccc
0012ff48  cccccccc cccccccc cccccccc cccccccc
0012ff58  cccccccc cccccccc cccccccc cccccccc

内存搜索

0:000> n16 ; s i L20 cc
base is 16
0012ff30  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff31  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff32  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff33  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff34  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff35  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff36  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................
0012ff37  cc cc cc cc cc cc cc cc-cc cc cc cc cc cc cc cc  ................

内存编辑

设置变量i的值为20

n 20; ed i 20 设置10进制,在i的内存位置(变量名之时内存地址,poi(i)表示i的值)编辑双字, 设置成20


设置源文件选项

l+ls 设置在命令窗口提示源文件代码


===============================================================

寄存器

===============================================================

查看所有寄存器的信息

r

修改寄存器的值

r @eax=0x00000323 或者r eax=0x00000323 或者 n 16 ; r eax=323

n命令的意思是切换数的进制,n 16表示使用16进制,323的字面就是16进制的0x323

显示和设置浮点数

0:000> rF
fpcw=027F: rn 53 puozdi  fpsw=0000: top=0 cc=0000 --------  fptw=FFFF
fopcode=0000  fpip=0000:00000000  fpdp=0000:00000000
st0= 0.000000000000000000000e+0000  st1= 0.000000000000000000000e+0000
st2= 0.000000000000000000000e+0000  st3= 0.000000000000000000000e+0000
st4= 0.000000000000000000000e+0000  st5= 0.000000000000000000000e+0000
st6= 0.000000000000000000000e+0000  st7= 0.000000000000000000000e+0000

rF st0=123.5e10


显示和设置sse寄存器

0:000> rX
xmm0=0 0 0 0
xmm1=0 0 0 0
xmm2=0 0 0 0
xmm3=0 0 0 0
xmm4=0 0 0 0
xmm5=0 0 0 0
xmm6=0 0 0 0
xmm7=0 0 0 0

rX xmm0=1 2 3 4

===============================================================

线程和栈

===============================================================

线程和栈操作指令:

切换到3号线程(顺序是按照线程创建的顺序编码的)

~3s

挂起当前线程

~n 

挂起所有线程

~*n

挂起10号线程

~10n

唤醒当前线程

~m

唤醒所有线程

~*m

唤醒10号线程

~10m

显示栈, 类似于gdb的bt命令

k kd kn ~kd ~*kn


移动栈帧到第10层,0层是当前执行位置

0:000> ~kn
 # ChildEBP RetAddr  
00 0012ff80 00401209 debugee!main+0x2a [H:\temp\debugee\debugee.cpp @ 19]
01 0012ffc0 77e6f23b debugee!mainCRTStartup+0xe9 [crt0.c @ 206]
02 0012fff0 00000000 kernel32!BaseProcessStart+0x23
0:000> .frame 2
02 0012fff0 00000000 kernel32!BaseProcessStart+0x23

===============================================================

windbg的快捷键

===============================================================

ctrl+s 设置符号表

ctrl+p 设置源文件路径

ctrl+e 创建子进程开始调试

f6 附加进程进行调试

f5 继续执行go

shift+f5 停止

ctrl+shift+f5 重启

f10 单步

F11, F8 步入

shift+f11 步出函数

alt+1 命令窗口

alt+2 局部变量

alt+3 观察watch

alt+4 寄存器

alt+5 内存

alt+6 调用栈

alt+7 反汇编

alt+8 记事本

alt+9 进程和线程 

===============================================================

异常处理

===============================================================

// debugee.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

void loop(void)
{
	for (int i = 0; i < 1000; i ++)
	{
		printf("loop [%d]\n", i);
	}

	*(int *)0 = 0;
}


int main(int argc, char* argv[])
{
	printf("Hello World!\n");

	try
	{
		loop();
	}
	catch (...)
	{
		printf("exception caught.\n");
	}
	return 0;
}

如果在调试状态下,调试器首先接管异常处理,这个时候,

1, 如果执行gN,调试器会放弃处理,让栈回溯代码继续寻找异常处理handler,这个时候如果你在执行gN之前,在catch里面的printf上打一个断点,可以拦截到异常处理。

2, 如果执行gh, 那么调试器告诉操作系统说,异常已经处理好了,于是操作系统傻傻的来重启异常语句,于是又异常。。。转悠吧。。。但是,如果你在这个时候,手动处理一下异常,比如说,把eip设置到一个安全的位置,例如,下一个语句。

0040105c e8ef000000      call    debugee!printf (00401150)
00401061 83c408          add     esp,8
00401064 ebdb            jmp     debugee!loop+0x21 (00401041)
>>>异常在这一句 00401066 c7050000000000000000 mov dword ptr ds:[0],0  ds:0023:00000000=????????
00401070 5f              pop     edi
00401071 5e              pop     esi
00401072 5b              pop     ebx
00401073 83c444          add     esp,44h
00401076 3bec            cmp     ebp,esp
00401078 e853010000      call    debugee!_chkesp (004011d0)
0040107d 8be5            mov     esp,ebp
0040107f 5d              pop     ebp
00401080 c3              ret

修改 r eip=0x00401070 设置到下一句,恢复执行的时候,就ok了(当然也可以真正的把异常修复了,例如把写入地址的指针赋给正确的值)

===============================================================

dump文件

===============================================================

生成小dump

.dump /m f:\debugee_mini.dmp

生成大dump

.dump /ma f:\debugee_large.dmp 


===============================================================

源文件和符号表

===============================================================

0:000> lm
start    end        module name
00400000 0042f000   debugee  C (private pdb symbols)  H:\temp\debugee\Debug\debugee.pdb
77e40000 77f42000   kernel32   (pdb symbols)          f:\symbols\kernel32.pdb\DAE455BF1E4B4E249CA44790CD7673182\kernel32.pdb
7c800000 7c8c0000   ntdll      (pdb symbols)          f:\symbols\ntdll.pdb\93E72E109DC84F16AA54797E4DA8C1682\ntdll.pdb
0:000> ls
     1: // debugee.cpp : Defines the entry point for the console application.
     2: //
     3: 
     4: #include "stdafx.h"
     5: 
     6: void loop(void)
     7: {
     8: 	for (int i = 0; i < 1000; i ++)
     9: 	{
    10: 		printf("loop [%d]\n", i);
0:000> lsc
Current: H:\temp\debugee\debugee.cpp(11)
0:000> lsf debugee.cpp

===============================================================

符号服务器

===============================================================

在windbg或者vs启动时候设置环境变量,可以调试windows系统的dll

Set _NT_SYMBOL_PATH = symsrv*symsrv.dll*f:\localsymbols*http://msdl.microsoft.com/download/symbols

可以先把符号表下载下来

symchk.exe /r c:\windows\system32 /s SRV*c:\symbolcache\*http://msdl.microsoft.com/download/symbols



参考文献:

http://www.windbg.info/doc/1-common-cmds.html

抱歉!评论已关闭.