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
p
执行一句,遇到函数,跟进去,相当于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
参考文献: