• Net 高级调试之四:Windbg 动态调试


    一、简介
        
    今天是《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 }
    View Code
            1.2、Example_4_1_2
     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 }
    View Code
            1.3、Example_4_1_3
     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 }
    View Code
            1.4、Example_4_1_4
     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 }
    View Code

        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
    复制代码
                解释:,我们可以通过【~* k】命令,打印出所有线程栈,......表示省略,内容太多,没必要,显示重要的就可以了。
                  
    复制代码
     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.exentdll.dllMSCOREE.DLLKERNEL32.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  (Example_4_1_3+0x6308ea) [E:\...\Example_4_1_3\Program.cs @ 18] 
    4 01 012ff318 7077f036     Example_4_1_3!COM+_Entry_Point  (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  (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  (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  (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  (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  (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  (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  (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  (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  (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  (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  (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  (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 动态调试的命令,我们要想让调试任务更容易,掌握这些调试技巧还是很有必要的。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
  • 相关阅读:
    opencv-python图片转换、尺寸、传输
    Python语言程序设计 习题7
    城商行该如何实施信息基础设施管理?
    在 SQL 中计算分页元数据,无需额外的往返
    LeetCode50天刷题计划(Day 27— Pow(x, n)(18.30-19.20)
    1.15 自实现GetProcAddress
    【LeetCode算法系列题解】第71~75题
    [CF643F]Bears and Juice
    《深入理解Java虚拟机》读书笔记--第一部分走进java
    学习 Tensorflow 的困境与解药
  • 原文地址:https://www.cnblogs.com/PatrickLiu/p/17788840.html