今天是《net 高级调试》的第四篇文章。到今天为止,也有三篇文章了,对 windbg 也有初步的认识了,当然,一个工具流畅、熟练的使用,对于我们调试 net 程序是至关重要的。在前几篇文章的基础上,我们这篇文章主要介绍一些和使用 windbg 有关的命令和操作。就我个人而言,第一次接触这个东西,还是挺难的,以前从来没有用过 windbg,用的最多的就是 visual studio 的调试功能。不怕大家笑话,如何通过 windbg 加载一个 exe,我都不知道,更不要谈加载 dump 文件。我看第一遍视频的时候,也不知道说了个啥,命令的执行,调试的开始,都感觉是一头雾水,似懂非懂,自己一实操,总是得不到别人调试那样的结果,很是郁闷。怎么办呢?没办法,要想学会,除了努力那就是坚持。针对视频,放慢速度,一帧一帧的按着视频的操作,自己来一遍,速度虽然慢,但是有些操作开始有了感觉了,当整个视频系列看了一遍,所有操作都操作一遍,终于有些头绪了。还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现,我这是第三遍。
如果在没有说明的情况下,所有代码的测试环境都是 net framewok 4.8,但是,有时候为了查看源码,可能需要使用 net core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
操作系统:windows professional 10
调试工具:windbg preview(可以去microsoft store 去下载)
开发工具:visual studio 2022
net 版本:net framework 4.8
coreclr源码:源码下载
二、相关知识
1、windbg 动态调试
1.1、调试概况
在任何一种调试中都有两个组件:调试器本身,调试目标。
当调试器【windbg】附加进程【attach to process】时,调试器会给 目标程序 注入一个远程线程并用 int 3 中断程序,后续和 net 程序中的 debuggerrcthread 线程交互执行命令。int 3 指令之所以可以让进程中断,主要是来自于 cpu 硬件中断,当调试器发出了一个 int 3 的中断请求,cpu会到内核态执行3号例程,也就是执行【中断向量表】,内核执行中断的操作。
actor------------》调试器(windbg)《------------》调试目标(net 程序,c++程序...)
以上图例就是一个调试器的作用和所处的地位。
2、程序的中断和恢复执行
2.1、中断执行(让程序中断有4中方式)
a、使用windbg 启动程序,在进程初始化函数中,如果发现有调试器附加在上面,就会执行 break 中断。
说明一下:windbg 调试器刚开始的中断就是 int 3 中断,但是这个中断的时机很早,我们可以做一些初始化的工作,比如:加载sos.dll 等类似的工作。
b、附加进程,调试器会注入远程线程执行 int 3 中断程序。
这个挺简单的,我们双击程序,直接运行。然后通过 windbg 的【attach to process】附加进程,就可以进入调试器界面,这个时候,其实什么也不用做,调试器已经暂停了,这个暂停就是 int 3 中断。我们通过 windbg 的【break】命令也是 int 3 中断。当然,我们通过 c# debugger.break() 代码执行的也是 int 3 的中断。
c、使用 bp 命令给程序下断点。
我们可以通过【u】命令查看方法的汇编代码,找到想要设断点的代码的地址,直接通过这个地址来下断点,当程序再次运行的时候,就会在这个断点处暂停。
d、异常中断。
异常中断的流程是:当你的程序发生异常,会从用户态转到内核态,内核态检测到你的程序附加了调试器,内核态就会把这个请求转交给调试器,调试器也就能中断了,可以调试了。
2.2、恢复执行
可以使用 g 命令回复程序的执行。
3、单步调试代码。
当我们调试程序的时候,最多的时候是使用 visual studio 的 f10、f11、f5这样的命令,在 windbg 中也有类似的命令可以使用。
3.1、p 命令
p(step):命令其实就是vs 中的 f10 快捷键,单步执行,遇到函数也是当成一条指令执行,不会进入函数体。
3.2、t 命令
t(trace):命令其实就是 vs 的 f11 快捷键,它是一种进入函数的单步执行调试。
3.3、pc 命令
pc(step to next call) 就是一直运行直到遇到 call 为止,不会进入函数体,call 是一个函数调用,汇编指令。
3.4、tc 命令
tc(trace to next call) 和 pc 不同的是,tc 会进入方法体,直到遇到 call 为止。
3.5、pt 命令
pt(step to next return) 遇到下一个 ret 为止。
3.6、tt 命令
tt(trace to next return) 会进入函数体直到遇到 ret 为止。递归的意思。
4、退出测试会话
结束调试会话,有两个目的,看是否保留程序的执行。
4.1、q(quit):结束调试会话+调试程序退出
调试会话结束,应用程序也会退出。
4.2、qd(quit and detach):结束调试会话+调试程序继续运行
调试会话结束,应用程序保持运行态,不会退出。
三、调试过程
废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
1、测试源码
1.1、example_4_1_1

1 namespace example_4_1_1 2 { 3 internal class program 4 { 5 static void main(string[] args) 6 { 7 for (int i = 0; i < int.maxvalue; i++) 8 { 9 console.writeline($"i={i}"); 10 thread.sleep(1000); 11 } 12 console.readline(); 13 } 14 } 15 }

1 namespace example_4_1_2 2 { 3 internal class program 4 { 5 static void main(string[] args) 6 { 7 debugger.break(); 8 9 int a = 10; 10 int b = 12; 11 12 var sum = a + b; 13 14 console.writeline($"sum={sum}"); 15 16 console.readline(); 17 } 18 } 19 }

1 namespace example_4_1_3 2 { 3 internal class program 4 { 5 static void main(string[] args) 6 { 7 run(); 8 9 console.readline(); 10 } 11 12 static void run() 13 { 14 console.writeline("请输入一个除数:"); 15 var num = console.readline(); 16 var result = 10 / convert.toint32(num); 17 } 18 } 19 }

1 namespace example_4_1_4 2 { 3 internal class program 4 { 5 static void main(string[] args) 6 { 7 sum1(10); 8 debugger.break(); 9 10 int i = 10; 11 int j = 20; 12 13 var sum = sum1(i); 14 console.writeline($"sum={sum}"); 15 16 console.readline(); 17 } 18 19 private static int sum1(int a) 20 { 21 var i = a; 22 var j = 11; 23 int sum = sum2(i, j); 24 25 return sum; 26 } 27 28 private static int sum2(int a, int b) 29 { 30 var i = a; 31 var j = b; 32 var k = 13; 33 34 var sum = sum3(i, j, k); 35 return sum; 36 } 37 38 private static int sum3(int i, int j, int k) 39 { 40 return i + j + k; 41 } 42 } 43 }
2、眼见为实
2.1、windbg【attach to process】附加进程,通过 int 3 命令中断程序。
测试代码:example_4_1_1
程序很简单,直接运行 exe 程序,打开 windbg,点击菜单【attach to process】进入调试器界面。其实什么操作都不用做,我们就可以看到调试器的输出结果。特别强调,红色标注的就是 int 3中断。
1 modload: 6fa30000 6faba000 c:\windows\microsoft.net\framework\v4.0.30319\clrjit.dll 2 modload: 75f50000 75feb000 c:\windows\system32\oleaut32.dll 3 (5384.3250): break instruction exception - code 80000003 (first chance) 4 eax=00270000 ebx=00000000 ecx=7790cee0 edx=7790cee0 esi=7790cee0 edi=7790cee0 5 eip=778d3410 esp=04c2f92c ebp=04c2f958 iopl=0 nv up ei pl zr na pe nc 6 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 7 ntdll!dbgbreakpoint: 8 778d3410 cc int 3
当然,我们也可以通过【g】命令,继续运行程序,然后点击工具栏的【break】按钮,程序就进入中断,这里的结果和上面是一样的。
1 0:006> g 2 (5384.528c): break instruction exception - code 80000003 (first chance) 3 eax=00279000 ebx=00000000 ecx=7790cee0 edx=7790cee0 esi=7790cee0 edi=7790cee0 4 eip=778d3410 esp=04eaf960 ebp=04eaf98c iopl=0 nv up ei pl zr na pe nc 5 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 6 ntdll!dbgbreakpoint: 7 778d3410 cc int 3
当然,也可以通过【u】命令,查看 ntdll!dbgbreakpoint的汇编代码。
1 0:008> u ntdll!dbgbreakpoint 2 ntdll!dbgbreakpoint: 3 778d3410 cc int 3 4 778d3411 c3 ret(方法返回)
我们也可以查看【disassembly】视图,截图如下:
解释内核态:涉及到内核态的执行,我们也可以通过windbg 查看,重新在打开一个 windbg,点击【文件】----》【attach kernel】,选择【local】项,点击【ok】按钮,进入调试器界面。然后,我们可以输入【!idt】命令来查看。
1 lkd> !idt 2 3 dumping idt: fffff80069a91000 4 5 00: fffff80064b93100 nt!kidivideerrorfaultshadow 6 01: fffff80064b93180 nt!kidebugtraporfaultshadow stack = 0xfffff80069a959d0 7 02: fffff80064b93240 nt!kinmiinterruptshadow stack = 0xfffff80069a957d0 8 03: fffff80064b932c0 nt!kibreakpointtrapshadow 9 04: fffff80064b93340 nt!kioverflowtrapshadow 10 ..................
红色标注的就是在内核态的中断函数,我们可以使用【u】命令,查看他的汇编代码。
1 lkd> u nt!kibreakpointtrapshadow 2 nt!kibreakpointtrapshadow: 3 fffff800`64b932c0 f644240801 test byte ptr [rsp+8],1 4 fffff800`64b932c5 7467 je nt!kibreakpointtrapshadow+0x6e (fffff800`64b9332e) 5 fffff800`64b932c7 0f01f8 swapgs 6 fffff800`64b932ca 0faee8 lfence 7 fffff800`64b932cd 650fba24251890000001 bt dword ptr gs:[9018h],1 8 fffff800`64b932d7 720c jb nt!kibreakpointtrapshadow+0x25 (fffff800`64b932e5) 9 fffff800`64b932d9 65488b242500900000 mov rsp,qword ptr gs:[9000h] 10 fffff800`64b932e2 0f22dc mov cr3,rsp
1 0:008> ~*k 2 3 0 id: 5384.4128 suspend: 1 teb: 0025e000 unfrozen 4 # childebp retaddr 5 00 0057ee00 75942d3b ntdll!ntdelayexecution+0xc 6 ...... 7 8 1 id: 5384.22a4 suspend: 1 teb: 00261000 unfrozen 9 # childebp retaddr 10 00 0088fab8 778b0f30 ntdll!ntwaitforworkviaworkerfactory+0xc 11 ...... 12 13 2 id: 5384.3034 suspend: 1 teb: 00264000 unfrozen 14 # childebp retaddr 15 00 00affb9c 778b0f30 ntdll!ntwaitforworkviaworkerfactory+0xc 16 ....... 17 18 3 id: 5384.35fc suspend: 1 teb: 00267000 unfrozen 19 # childebp retaddr 20 00 00bff9f0 778b0f30 ntdll!ntwaitforworkviaworkerfactory+0xc 21 ...... 22 23 4 id: 5384.3c50 suspend: 1 teb: 0026a000 unfrozen 24 # childebp retaddr 25 00 0258f804 75939623 ntdll!ntwaitformultipleobjects+0xc 26 01 0258f804 711567d7 kernelbase!waitformultipleobjectsex+0x103 27 02 0258f86c 711566ff clr!debuggerrcthread::mainloop+0x99 28 03 0258f898 71156620 clr!debuggerrcthread::threadproc+0xd0 29 04 0258f8c4 7711f989 clr!debuggerrcthread::threadprocstatic+0xa3 30 05 0258f8d4 778c7084 kernel32!basethreadinitthunk+0x19 31 06 0258f930 778c7054 ntdll!__rtluserthreadstart+0x2f 32 07 0258f940 00000000 ntdll!_rtluserthreadstart+0x1b 33 34 5 id: 5384.2050 suspend: 1 teb: 0026d000 unfrozen 35 # childebp retaddr 36 00 0475fabc 75939623 ntdll!ntwaitformultipleobjects+0xc 37 ...... 38 39 6 id: 5384.2ce0 suspend: 1 teb: 00276000 unfrozen 40 # childebp retaddr 41 00 04c2f84c 778b0f30 ntdll!ntwaitforworkviaworkerfactory+0xc 42 ...... 43 44 7 id: 5384.489c suspend: 1 teb: 00273000 unfrozen 45 # childebp retaddr 46 ...... 47 04 04d6ffb0 00000000 ntdll!_rtluserthreadstart+0x1b 48 49 # 8 id: 5384.528c suspend: 1 teb: 00279000 unfrozen 50 # childebp retaddr 51 00 04eaf98c 7790cf19 ntdll!dbgbreakpoint(int 3 中断) 52 01 04eaf98c 7711f989 ntdll!dbguiremotebreakin+0x39 53 02 04eaf99c 778c7084 kernel32!basethreadinitthunk+0x19 54 03 04eaf9f8 778c7054 ntdll!__rtluserthreadstart+0x2f 55 04 04eafa08 00000000 ntdll!_rtluserthreadstart+0x1b
2.2、使用windbg 启动程序,在进程初始化函数中断进程。
测试代码:example_4_1_2
我们编译项目,打开 windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们可以看到,如下输出:
1 executable search path is: 2 modload: 00be0000 00be8000 example_4_1_2.exe 3 modload: 77860000 77a02000 ntdll.dll 4 modload: 717e0000 71832000 c:\windows\syswow64\mscoree.dll 5 modload: 77100000 771f0000 c:\windows\syswow64\kernel32.dll 6 modload: 75820000 75a33000 c:\windows\syswow64\kernelbase.dll 7 modload: 5efe0000 5f07f000 c:\windows\syswow64\apphelp.dll 8 (300c.20b8): break instruction exception - code 80000003 (first chance) 9 eax=00000000 ebx=00000000 ecx=534d0000 edx=00000000 esi=77871f64 edi=7787252c 10 eip=77910de2 esp=00f7f824 ebp=00f7f850 iopl=0 nv up ei pl zr na pe nc 11 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 12 ntdll!ldrpdodebuggerbreak+0x2b: 13 77910de2 cc int 3
红色部分需要注意,然后我们使用【u】命令查看它的汇编代码。
1 0:000> u ntdll!ldrpdodebuggerbreak+0x2b 2 ntdll!ldrpdodebuggerbreak+0x2b: 3 77910de2 cc int 3 4 77910de3 eb07 jmp ntdll!ldrpdodebuggerbreak+0x35 (77910dec) 5 77910de5 33c0 xor eax,eax 6 77910de7 40 inc eax 7 77910de8 c3 ret 8 77910de9 8b65e8 mov esp,dword ptr [ebp-18h] 9 77910dec c745fcfeffffff mov dword ptr [ebp-4],0fffffffeh 10 77910df3 8b4df0 mov ecx,dword ptr [ebp-10h]
我们可以使用【k】命令,继续查看。
1 0:000> k 2 # childebp retaddr 3 00 00f7f850 7790b2f8 ntdll!ldrpdodebuggerbreak+0x2b(int 3 中断) 4 01 00f7fab0 778ba3d1 ntdll!ldrpinitializeprocess+0x1c98(进程初始化的时候执行的 break中断,) 5 02 00f7fb08 778ba2c1 ntdll!_ldrpinitialize+0xba 6 03 00f7fb14 00000000 ntdll!ldrinitializethunk+0x11
ntdll是一个网关函数dll,如果想使用内核的功能几必须通过 ntdll 里面的函数。ntdll!ldrpdodebuggerbreak 这个中断是在进程初始化之前进行的,是很早的一个时机,加载的东西也不多,只有【example_4_1_2.exe,ntdll.dll,mscoree.dll,kernel32.dll...】,之所以这样,可以让我们设置一些或者说配置一些初始化的东西,比如:加载 sos等。
2.3、使用 bp 命令给程序下断点,可以让程序中断。
测试代码:example_4_1_2
比如,我们在【int a = 10;】这样代码下断点,行数:12.

我们编译项目,打开 windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。我们使用【g】命令,继续运行,然后我们使用【!clrstack】命令,查看线程栈。
1 0:000> !clrstack 2 os thread id: 0x3c54 (0) 3 child sp ip call site 4 00d5ed38 7597f262 [helpermethodframe: 00d5ed38] system.diagnostics.debugger.breakinternal() 5 00d5edb4 7064f195 system.diagnostics.debugger.break() [f:\dd\ndp\clr\src\bcl\system\diagnostics\debugger.cs @ 91] 6 00d5eddc 02ba0886 example_4_1_2.program.main(system.string[]) [e:\visual studio 2022\...\example_4_1_2\program.cs @ 10] 7 00d5ef78 70faf036 [gcframe: 00d5ef78]
红色标注的是 main 方法的地址,然后执行【!u 02ba0886】命令,查看他的汇编代码。
1 0:000> !u 02ba0886 2 normal jit generated code 3 example_4_1_2.program.main(system.string[]) 4 begin 02ba0848, size a3 5 6 ...... 7 8 e:\visual studio 2022\...\example_4_1_2\program.cs @ 12:(这个行号就是c# 代码的行号) 9 02ba0887 c745f00a000000 mov dword ptr [ebp-10h],0ah
我们找到了代码的位置,就可以下断点了,使用【bp】命令。
0:000> bp 02ba0887
【g】继续运行,就会到断点出暂停。

效果如图。
2.4、触发异常,也可以让程序中断。
测试代码:example_4_1_3
代码很简单,我简单说一些流程,我们首先将要测试的项目编译好,然后打开 windbg,通过【launch executable】附加应用程序,调试器会响应一个 int 3中断,我们通过【g】命令,继续运行程序。程序提示输入一个数字,我输入0,肯定就会异常了。
效果如图:

异常中断的代码是:014b08ea f77df4 idiv eax, dword ptr [ebp-0ch],效果如图:
我们使用【dp】命令,查看【ebp-0ch】代码的值。
1 0:000> dp ebp-0ch l1 2 012ff2fc 00000000
我们可以使用【dp】命令,也可以使用【?】命令,查看一下eax 是什么,其实 eax就是十进制的10。
1 0:000> ? eax 2 evaluate expression: 10 = 0000000a
代码【idiv】就是表示除法触发的异常。
我们也可以使用【k】命令,查看调用栈,也能看出在哪里中断。
1 0:000> k 2 # childebp retaddr 3 00 012ff308 014b086b example_4_1_3!com+_entry_point <perf> (example_4_1_3+0x6308ea) [e:\...\example_4_1_3\program.cs @ 18] 4 01 012ff318 7077f036 example_4_1_3!com+_entry_point <perf> (example_4_1_3+0x63086b) [e:\...\example_4_1_3\program.cs @ 9] 5 ...... 6 0f 012ffde4 77057054 ntdll!__rtluserthreadstart+0x2f 7 10 012ffdf4 00000000 ntdll!_rtluserthreadstart+0x1b
红色部分就是中断的 c# 代码的行号。
2.5、单步调试命令测试。
测试代码:example_4_1_4
说明一下,这些测试命令,不需要每个命令建立一个独立的测试项目,所以我这里就使用了一个项目做测试。为了更明确,每个命令单独测试。但是编译项目,通过windbg 加载项目就不详述了。都是通过 windbg的【launch executable】附加应用程序的,进入到的调试器后,通过【g】命令继续运行,会在 c# 【debugger.break()】代码出中断,我们通过【!clrstack】线程栈,找到 program 的 main 方法的地址,然后在这个地址上通过【bp】命令下断点,剩下就可以自由调试了。
1 0:000> !clrstack 2 os thread id: 0x35ec (0) 3 child sp ip call site 4 00aff270 76e8f262 [helpermethodframe: 00aff270] system.diagnostics.debugger.breakinternal() 5 00aff2ec 6fe1f195 system.diagnostics.debugger.break() [f:\dd\ndp\clr\src\bcl\system\diagnostics\debugger.cs @ 91] 6 00aff314 00cc0895 example_4_1_4.program.main(system.string[]) [e:\visual studio\...\example_4_1_4\program.cs @ 11] 7 00aff4b4 7077f036 [gcframe: 00aff4b4]
在红色标注的地址上设置断点。
0:000> bp 00cc0895
【g】继续运行。

开始调试了。
p命令:1 0:000> p 2 eax=00000022 ebx=00aff3e4 ecx=0000000a edx=00000000 esi=02ab24bc edi=00aff330 3 eip=00cc08b3 esp=00aff314 ebp=00aff348 iopl=0 nv up ei pl nz ac pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x6408b3): 6 00cc08b3 8945e8 mov dword ptr [ebp-18h],eax ss:002b:00aff330=00000000 7 0:000> p 8 eax=00000022 ebx=00aff3e4 ecx=0000000a edx=00000000 esi=02ab24bc edi=00aff330 9 eip=00cc08b6 esp=00aff314 ebp=00aff348 iopl=0 nv up ei pl nz ac pe nc 10 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 11 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x6408b6): 12 00cc08b6 b9a8422a6f mov ecx,offset mscorlib_ni!getobjectdata+0x102 (6f2a42a8) 13 0:000> p 14 eax=00000022 ebx=00aff3e4 ecx=6f2a42a8 edx=00000000 esi=02ab24bc edi=00aff330 15 eip=00cc08bb esp=00aff314 ebp=00aff348 iopl=0 nv up ei pl nz ac pe nc 16 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 17 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x6408bb): 18 00cc08bb e83428faff call 00c630f4
t命令:
1 0:000> t 2 eax=00000000 ebx=0053ee2c ecx=0000000a edx=00779910 esi=025224bc edi=0053ed70 3 eip=023808a7 esp=0053ed54 ebp=0053ed88 iopl=0 nv up ei pl zr na pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x21e08a7): 6 023808a7 ff156c4d3302 call dword ptr ds:[2334d6ch] ds:002b:02334d6c=02380918 7 0:000> t 8 eax=00000000 ebx=0053ee2c ecx=0000000a edx=00779910 esi=025224bc edi=0053ed70 9 eip=02380918 esp=0053ed50 ebp=0053ed88 iopl=0 nv up ei pl zr na pe nc 10 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 11 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x21e0918): 12 02380918 55 push ebp
效果如图:
执行【t】命令后,效果如图:
pc命令:
1 0:000> pc 2 eax=00000000 ebx=00bfee64 ecx=0000000a edx=00f69910 esi=02f124bc edi=00bfedb0 3 eip=014108a7 esp=00bfed94 ebp=00bfedc8 iopl=0 nv up ei pl zr na pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x9b08a7): 6 014108a7 ff156c4d1301 call dword ptr ds:[1134d6ch] ds:002b:01134d6c=01410918
效果如图:
运行后的效果,如图:
tc命令:
1 0:000> tc 2 eax=00000000 ebx=010fee04 ecx=0000000a edx=013998f0 esi=031424bc edi=010fed50 3 eip=02f708a7 esp=010fed34 ebp=010fed68 iopl=0 nv up ei pl zr na pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x22608a7): 6 02f708a7 ff156c4d5e01 call dword ptr ds:[15e4d6ch] ds:002b:015e4d6c=02f70918
第一次执行【tc】命令,效果如图:
1 0:000> tc 2 eax=0000000a ebx=010fee04 ecx=0000000a edx=0000000b esi=031424bc edi=010fed50 3 eip=02f7095c esp=010fed14 ebp=010fed2c iopl=0 nv up ei pl zr na pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x226095c): 6 02f7095c ff15784d5e01 call dword ptr ds:[15e4d78h] ds:002b:015e4d78=02f70990
第二次执行【tc】命令,效果如图:
1 0:000> tc 2 eax=0000000b ebx=010fee04 ecx=0000000a edx=0000000b esi=031424bc edi=010fed50 3 eip=02f709e5 esp=010fece8 ebp=010fed0c iopl=0 nv up ei pl zr na pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x22609e5): 6 02f709e5 ff15844d5e01 call dword ptr ds:[15e4d84h] ds:002b:015e4d84=02f70a18
第三次执行【tc】命令,效果如图:
pt命令:
初始状态,如图:

执行了【pt】命令,遇到【ret】就暂停,也就是回到29行。
1 0:000> pt 2 eax=00000022 ebx=010ff29c ecx=0000000a edx=00000000 esi=030624bc edi=010ff1e0 3 eip=015f097a esp=010ff1c0 ebp=010ff1f8 iopl=0 nv up ei pl nz ac pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x97097a): 6 015f097a c3 ret
效果如图:
tt命令:
如果我们运行【tt】命令,第一暂停会到【sum3】方法的44行。
1 0:000> tt 2 eax=00000022 ebx=00aff158 ecx=0000000a edx=00000000 esi=02c924bc edi=00aff0a0 3 eip=00e00a4d esp=00aff034 ebp=00aff05c iopl=0 nv up ei pl nz ac pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x730a4d): 6 00e00a4d c20400 ret 4
效果如图:
1 0:000> tt 2 eax=00000022 ebx=00aff158 ecx=0000000a edx=00000000 esi=02c924bc edi=00aff0a0 3 eip=00e00a03 esp=00aff060 ebp=00aff07c iopl=0 nv up ei pl nz ac pe nc 4 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216 5 example_4_1_4!com+_entry_point <perf> (example_4_1_4+0x730a03): 6 00e00a03 c3 ret
第二次执行【tt】命令,会在sum2方法的39行暂停,以此类推。
2.6、退出调试会话
这里不需要更多的代码,也不用专门写一个测试程序了,所以就直接用 example_4_1_4 项目,很简单,就不作图例了。
测试代码:example_4_1_4
a、结束会话,并退出程序
使用【q】命令。
b、结束会话,程序继续运行。
使用【qd】命令。
四、总结
终于写完了,为什么说是终于,因为写这一篇文章,不是一天完成的,还要写文章,记录操作过程,作图例,所以时间就长了。今天介绍的是 windbg 动态调试的命令,我们要想让调试任务更容易,掌握这些调试技巧还是很有必要的。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
发表评论