|
一、简介
今天是《Net 高级调试》的第十篇文章。说起来,高级调试,调试的内容还是挺多的,技巧也不少,但是,要想做一个合格的高级调试人员,还需要掌握如何调试动态生成的IL代码。今天要探讨的高级调试的技巧是如何调试通过 Emit 动态生成 IL 代码。可能有人会问,我们不是编写 C# 代码,或者是 VB.Net 代码吗?怎么还要动态生成 IL 代码,这些工作不是编译器做的吗?当然,一般情况是这样的,但是,当我们编写一些高性能的框架的时候,使用 IL 代码编写也是常事。既然也可以直接使用 IL 编写代码,那对它的调试也是少不了的,调试机会虽然很少,具有这个本领,等遇到这样的问题,就不至于慌乱了,俗话说的好:艺多不压身。当然了,第一次看视频或者看书,是很迷糊的,不知道如何操作,还是那句老话,一遍不行,那就再来一遍,还不行,那就再来一遍,俗话说的好,书读千遍,其意自现。
如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
操作系统:Windows Professional 10
调试工具:Windbg Preview(可以去Microsoft Store 去下载)
开发工具:Visual Studio 2022
Net 版本:Net Framework 4.8
CoreCLR源码:源码下载
二、基础知识
1、动态代码调试
动态代码调试的机会虽然不多,但是掌握动态代码调试的技巧还是很有必要的,俗话说的好,艺多不压身。当我们遇到有这样写代码,由此引发的问题的时候,我们也可以做到遇事不惊。
今天我们就讨论三种调试的技巧。
2、三种调试策略
2.1、捕获 JIT 的 CompileMethod 方法。
C# 代码的编译过程分为两个阶段,第一个阶段就是编译器的阶段,这个阶段编译器将我们编写的C#源代码编译成 IL 代码,第二阶段,就是在运行的时候,JIT 将 IL 代码编译为机器代码,我们的程序就运行了。现在是动态生成的 IL 代码,没有第一个阶段,直接就是第二个 JIT 编译的阶段,我们可以尝试在 JIT 的某个方法中设置断点进行拦截,拿到方法的描述符,也就是 MD,有了 MD,我们就可以使用 bp md 命令,为这个方法下断点,这样就可以对动态生成的代码进行调试了。
2.2、从代码中获取委托的函数指针(我的测试没有实现)。
这种方法比较简单,但需要在方法中加一行代码,具有一定的破坏性。代码:Marshal.GetFunctionPointerForDelegate(委托实例).ToInt64()。
2.3、在动态的代码中注入 Debugger.Break()。
我们在很多的调试中都会用到 Debugger.Break() 函数,让程序中断到调试器,如果这个动态代码,我们具有完整的控制权,我们就可以通过注入这个方法:Debugger.Break(),实现 int 3 中断,进而获取想要的数据。代码如下:IL.Emit(OpCodes.Call,typeof(Debugger).GetMethod("Break"))。
3、程序集
3.1、程序集崩溃
程序集泄露是指什么呢?就是程序的内存暴涨,产生大量的程序集。
三、调试过程
废话不多说,这一节是具体的调试操作的过程,又可以说是眼见为实的过程,在开始之前,我还是要啰嗦两句,这一节分为两个部分,第一部分是测试的源码部分,没有代码,当然就谈不上测试了,调试必须有载体。第二部分就是根据具体的代码来证实我们学到的知识,是具体的眼见为实。
1、测试源码
以下项目代码就是在眼见为实用到的测试用例。
1.1、Example_10_1_1
- 1 namespace Example_10_1_1
- 2 {
- 3 internal class Program
- 4 {
- 5 private delegate int AddDelegate(int a, int b);
- 6 static void Main(string[] args)
- 7 {
- 8 var dynamicAdd = new DynamicMethod("Add", typeof(int), new Type[] { typeof(int), typeof(int) });
- 9 var il = dynamicAdd.GetILGenerator();
- 10 il.Emit(OpCodes.Ldarg_0);
- 11 il.Emit(OpCodes.Ldarg_1);
- 12 il.Emit(OpCodes.Add);
- 13 il.Emit(OpCodes.Ret);
- 14
- 15 var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));
- 16 Console.WriteLine(addDelegate(10, 20));
- 17 }
- 18 }
- 19 }
复制代码 View Code
1.2、Example_10_1_2
- 1 namespace Example_10_1_2
- 2 {
- 3 internal class Program
- 4 {
- 5 private delegate int AddDelegate(int a, int b);
- 6 static void Main(string[] args)
- 7 {
- 8 var dynamicAdd = new DynamicMethod("Add", typeof(int), new Type[] { typeof(int), typeof(int) });
- 9 var il = dynamicAdd.GetILGenerator();
- 10 il.Emit(OpCodes.Ldarg_0);
- 11 il.Emit(OpCodes.Ldarg_1);
- 12 il.Emit(OpCodes.Add);
- 13 il.Emit(OpCodes.Ret);
- 14
- 15 var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));
- 16
- 17 Console.WriteLine("Function Pointer:0x{0:x16}", Marshal.GetFunctionPointerForDelegate(addDelegate).ToInt64());
- 18
- 19 Debugger.Break();
- 20
- 21 Console.WriteLine(addDelegate(10, 20));
- 22 }
- 23 }
- 24 }
复制代码 View Code
1.3、Example_10_1_3
- 1 namespace Example_10_1_3
- 2 {
- 3 internal class Program
- 4 {
- 5 private delegate int AddDelegate(int a, int b);
- 6 static void Main(string[] args)
- 7 {
- 8 var dynamicAdd = new DynamicMethod("Add", typeof(int), new Type[] { typeof(int), typeof(int) });
- 9 var il = dynamicAdd.GetILGenerator();
- 10
- 11 il.Emit(OpCodes.Call,typeof(Debugger).GetMethod("Break"));
- 12 il.Emit(OpCodes.Ldarg_0);
- 13 il.Emit(OpCodes.Ldarg_1);
- 14 il.Emit(OpCodes.Add);
- 15 il.Emit(OpCodes.Ret);
- 16
- 17 var addDelegate = (AddDelegate)dynamicAdd.CreateDelegate(typeof(AddDelegate));
- 18
- 19 Console.WriteLine(addDelegate(10, 20));
- 20 }
- 21 }
- 22 }
复制代码 View Code
1.4、Example_10_1_4
- 1 namespace Example_10_1_4
- 2 {
- 3 internal class Program
- 4 {
- 5 //XmlSerializer序列化程序集泄露解决办法。
- 6 //static readonly XmlSerializer xmlSerializer = new XmlSerializer(typeof(Customer), new XmlRootAttribute("FabrikamCustomer"));
- 7 static void Main(string[] args)
- 8 {
- 9 var xml = @"<FabrikamCustomer><Id>001</Id><FirstName>John</FirstName><LastName>Dow</LastName></FabrikamCustomer>";
- 10
- 11 Enumerable.Range(0, 30000)
- 12 .Select(i => GetCustomer(i, "FabrikamCustomer", xml))
- 13 .ToList();
- 14
- 15 Console.WriteLine("处理完成!");
- 16 Console.ReadLine();
- 17 }
- 18
- 19 public static Customer GetCustomer(int i, string rootElementName, string xml)
- 20 {
- 21 var xmlSerializer = new XmlSerializer(typeof(Customer), new XmlRootAttribute("FabrikamCustomer"));
- 22 using (var textReader = new StringReader(xml))
- 23 {
- 24 using (var xmlReader = XmlReader.Create(textReader))
- 25 {
- 26 Console.WriteLine(i);
- 27
- 28 return (Customer)xmlSerializer.Deserialize(xmlReader);
- 29 }
- 30 }
- 31 }
- 32 }
- 33
- 34 public class Customer
- 35 {
- 36 public string Id { get; set; }
- 37 public string FirstName { get; set; }
- 38 public string LastName { get; set; }
- 39 }
- 40 }
复制代码 View Code
2、眼见为实
项目的所有操作都是一样的,所以就在这里说明一下,但是每个测试例子,都需要重新启动,并加载相应的应用程序,加载方法都是一样的。流程如下:我们编译项目,打开 Windbg,点击【文件】----》【launch executable】附加程序,打开调试器的界面,程序已经处于中断状态。
2.1、捕获 JIT 的 CompileMethod 方法,为 Add 方法设置断点,进行调试。
测试源码:Example_10_1_1
上图是我们的代码,我们使用【!mbp】命令在18行下断点,【mbp】命令是 SOSEX的扩展命令,执行前必须加载 SOSEX.dll,我们需要使用【g】命令,继续运行程序,然后到达指定断点处停止后。- 1 0:000> !mbp Program.cs 18
- 2 The CLR has not yet been initialized in the process.
- 3 Breakpoint resolution will be attempted when the CLR is initialized.
复制代码
我们已经在我们设置的断点处暂停。我们开始对 CompileMethod 这个方法下断点。首先我们通过【lm(load module)】命令找到clrjit.dll 这个dll。- 1 0:000> lm
- 2 start end module name
- 3 00f80000 00f88000 Example_10_1_1 C (service symbols: CLR Symbols with PDB: C:\ProgramData\...\Example_10_1_1.pdb)
- 4 5bd10000 5bdaf000 apphelp (deferred)
- 5 6dbc0000 6efce000 mscorlib_ni (service symbols: CLR Symbols with PDB: C:\ProgramData\...88BB3460CAC52\mscorlib.pdb)
复制代码- 6 <strong>6efd0000 6f780000 clr (pdb symbols)</strong> C:\ProgramData\Dbg\sym\clr.pdb\5138062248484B79BCF6F6B3F3B59A3D2\clr.pdb
- 7 721b0000 7223d000 mscoreei (pdb symbols) C:\ProgramData\Dbg\sym\mscoreei.pdb\3353E43482934F9AB820643DF51EC4692\mscoreei.pdb
- 8 72240000 72292000 MSCOREE (pdb symbols) C:\ProgramData\Dbg\sym\mscoree.pdb\3A1E9FD59D013FF42905FC8655F33DCB1\mscoree.pdb
- 9 <strong>722d0000 7235a000 clrjit (deferred)</strong>
- 10 72380000 7242b000 ucrtbase_clr0400 (deferred)
- 11 ......
- 12 77810000 77828000 win32u (deferred)
- 13 77830000 77ab0000 combase (deferred)
- 14 77ac0000 77c62000 ntdll (pdb symbols) C:\ProgramData\Dbg\sym\wntdll.pdb\DBC8C8F74C0E3696E951B77F0BB8569F1\wntdll.pdb
- 15
- 16 Unloaded modules:
- 17 77830000 77ab0000 combase.dll
复制代码 红色标注的,说明CLR 和 JIT 都已经加载了。然后,我们通过【x】命令查找 CompileMethod方法。- 1 0:000> x clrjit!*CompileMethod*
- 2 722d3700 clrjit!CILJit::compileMethod (class ICorJitInfo *, struct CORINFO_METHOD_INFO *, unsigned int, unsigned char **, unsigned long *)
复制代码 我们找到了 CompileMethod 方法,给这个方法下一个断点。- 1 0:000> bp clrjit!CILJit::compileMethod
复制代码 我们【g】了两次,已经在 CILJit::compileMethod 方法处断住了。- 1 0:000> g
- 2 Breakpoint 1 hit
- 3 eax=00000002 ebx=0133ef84 ecx=03383840 edx=00000000 esi=033824c8 edi=0133eedc
- 4 eip=016c0a3d esp=0133ee8c ebp=0133eee8 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 Example_10_1_1!COM+_Entry_Point <PERF> (Example_10_1_1+0x740a3d):
- 7 016c0a3d b9144e6601 mov ecx,1664E14h
- 8 0:000> g
- 9 Breakpoint 0 hit
- 10 eax=72354698 ebx=80000004 ecx=722d3700 edx=00005c10 esi=7234b3fc edi=0133e970
- 11 eip=722d3700 esp=0133e7ac ebp=0133e804 iopl=0 nv up ei ng nz na po nc
- 12 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000282
- 13 <strong>clrjit!</strong><strong>CILJit::compileMethod</strong>:
- 14 722d3700 55 push ebp
复制代码 我们在 CILJit::compileMethod 这个方法已经断住了,然后我们使用【!kb】命令,查看一下它的参数。- 1 0:000> kb 1
- 2 # ChildEBP RetAddr Args to Child
- 3 00 0133e804 6f09ccc3 72354698 0133e970 0133e8e8 clrjit!CILJit::compileMethod [f:\dd\ndp\clr\src\jit32\ee_il_dll.cpp @ 151]
复制代码 72354698 0133e970 0133e8e8 这个三个参数的第三参数其实就是包含方法描述符的地址。- 1 0:000> dp 0133e8e8 l1
- 2 0133e8e8 01665384
复制代码 我们可以使用【!dumpmd】命令查看这个方法的信息。- 1 0:000> !dumpmd 01665384
- 2 Method Name: <strong>DynamicClass.Add(Int32, Int32)(我们要查找的方法)
- </strong>3 Class: 016652f0
- 4 MethodTable: 01665344
- 5 mdToken: 06000000
- 6 Module: 01664eac
- 7 IsJitted: no
- 8 CodeAddr: <strong>ffffffff(说明方法还没有编译)
- </strong>9 Transparency: Transparent
复制代码 我们使用【k】命令,看看我的调用栈。- 1 0:000> k
- 2 # ChildEBP RetAddr
- 3 00 0133e804 6f09ccc3 <strong>clrjit!CILJit::compileMethod</strong> [f:\dd\ndp\clr\src\jit32\ee_il_dll.cpp @ 151]
- 4 01 0133e804 6f09cd9b <strong>clr!invokeCompileMethodHelper</strong>+0x10b
- 5 02 0133e84c 6f09cdf8 <strong>clr!invokeCompileMethod</strong>+0x3d
- 6 03 0133e8b8 6f09d0a7 <strong>clr!CallCompileMethodWithSEHWrapper</strong>+0x39
- 7 04 0133ec78 6f09caac <strong>clr!UnsafeJitFunction</strong>+0x431
- 8 05 0133ed6c 6f09e703 <strong>clr!MethodDesc::MakeJitWorker</strong>+0x40b
- 9 06 0133ede4 <strong>6f08f78f</strong> <strong>clr!MethodDesc::DoPrestub</strong>+0x5f3
- 10 07 0133ee60 6efdf4bb clr!PreStubWorker+0xe0
- 11 08 0133ee84 016c0aab clr!ThePreStub+0x11
- 12 09 0133eee8 6efdf036 Example_10_1_1!COM+_Entry_Point <PERF> (Example_10_1_1+0x740aab) [E:\Visual Studio 2022\Program.cs @ 19]
- 13 0a 0133eef4 6efe22da clr!CallDescrWorkerInternal+0x34
- 14 0b 0133ef48 6efe859b clr!CallDescrWorkerWithHandler+0x6b
- 15 0c 0133efb0 6f18b11b clr!MethodDescCallSite::CallTargetWorker+0x16a
- 16 0d 0133f0d4 6f18b7fa clr!RunMain+0x1b3
- 17 0e 0133f340 6f18b727 clr!Assembly::ExecuteMainMethod+0xf7
- 18 0f 0133f824 6f18b8a8 clr!SystemDomain::ExecuteMainMethod+0x5ef
- 19 10 0133f87c 6f18b9ce clr!ExecuteEXE+0x4c
- 20 11 0133f8bc 6f187305 clr!_CorExeMainInternal+0xdc
- 21 12 0133f8f8 721bfa84 clr!_CorExeMain+0x4d
- 22 13 0133f930 7224e81e mscoreei!_CorExeMain+0xd6
- 23 14 0133f940 72254338 MSCOREE!ShellShim__CorExeMain+0x9e
- 24 15 0133f958 765ff989 MSCOREE!_CorExeMain_Exported+0x8
- 25 16 0133f958 77b27084 KERNEL32!BaseThreadInitThunk+0x19
- 26 17 0133f9b4 77b27054 ntdll!__RtlUserThreadStart+0x2f
- 27 18 0133f9c4 00000000 ntdll!_RtlUserThreadStart+0x1b
复制代码 我们生成的 Add 方法,会首先进入 clr!MethodDesc::DoPrestub 桩函数,开始引导JIT编译,我们就在这个方法设断点。 我们【g】继续运行,就会在 DoPrestub 方法断住。我们在通过【!dumpmd】命令查看方法的描述符。- 1 0:000> g
- 2 Breakpoint 0 hit
- 3 eax=016c0470 ebx=00000000 ecx=6f08f6aa edx=01665384 esi=01703978 edi=01665384
- 4 eip=6f08f78f esp=0133edf0 ebp=0133ee60 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 <strong>clr!PreStubWorker+0xe0:(断点处)
- </strong> 7 6f08f78f 8bf8 mov edi,eax
- 8 0:000> !dumpmd 01665384
- 9 Method Name: DynamicClass.Add(Int32, Int32)
- 10 Class: 016652f0
- 11 MethodTable: 01665344
- 12 mdToken: 06000000
- 13 Module: 01664eac
- 14 IsJitted: yes
- 15 CodeAddr: 05940050(方法已经编译)
- 16 Transparency: Transparent
复制代码 我们有了CodeAddr: 05940050 方法的地址,我们就可以在这个地址上下断点。- 1 0:000> bp 05940050
- 2 0:000> g
- 3 Breakpoint 1 hit
- 4 eax=016c0470 ebx=0133ef84 ecx=0000000a edx=00000014 esi=033824c8 edi=0133eedc
- 5 eip=05940050 esp=0133ee88 ebp=0133eee8 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 05940050 8bc1 mov eax,ecx
复制代码 我们下了断点,【g】继续运行。执行进入 Add 方法第一行就被断住了,我们可以使用【!clrstack】命令,来验证。- 1 0:000> !clrstack
- 2 OS Thread Id: 0x4168 (0)
- 3 Child SP IP Call Site
- 4 0133ee88 05940050<strong> DynamicClass.Add(Int32, Int32)(这就是我们动态生成的方法)
- </strong>5 0133ee8c 016c0aab Example_10_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\Example_10_1_1\Program.cs @ 19]
- 6 0133f054 6efdf036 [GCFrame: 0133f054]
复制代码 我们已经成功命中了 Add 方法,还是挺不容易的。- 1 0:000> !U /d 05940050
- 2 Normal JIT generated code
- 3 DynamicClass.Add(Int32, Int32)
- 4 Begin 05940050, size 5
- 5 >>> 05940050 8bc1 mov eax,ecx
- 6 05940052 03c2 add eax,edx
- 7 05940054 c3 ret
复制代码 ecx 是方法的第一个参数,edx:是方法的第二个参数
mov eax, ecx 将 ecx 赋值给 eax,
add eax, edx 将 edx 和 eax 相加,eax 作为返回值返回
ret
我们对方法代码也做了一些解释。
2.2、通过委托的函数指针找方法的描述符。
测试源码:Example_10_1_2
我们通过 Windbg 直接加载 Example_10_1_2 项目,【g】命令直接运行,会到【Debugger.Break()】这行代码处暂停,也就是一个 int 3 中断。我们的程序输出:Function Pointer:0x0000000004cd062e。然后我们使用【u 0x0000000004cd062e】命令,查看汇编代码。- 1 0:000> u 0x0000000004cd062e
- 2 04cd062e b81806cd04 mov eax,<strong>4CD0618h
- </strong>3 04cd0633 e9e4c9dbfb jmp 00a8d01c
- 4 04cd0638 ab stos dword ptr es:[edi]
- 5 04cd0639 ab stos dword ptr es:[edi]
- 6 04cd063a ab stos dword ptr es:[edi]
- 7 04cd063b ab stos dword ptr es:[edi]
- 8 04cd063c ab stos dword ptr es:[edi]
- 9 04cd063d ab stos dword ptr es:[edi]
复制代码 4CD0618h 红色标注的就是包含方法描述符的地址。我们使用【dp 4CD0618h l1】命令,查看详情。- 1 0:000> dp 4CD0618h l1
- 2 04cd0618 <strong>00b20480</strong>
复制代码 00b20480 这个地址就是方法描述符,我们可以使用【!dumpmd 00b20480】命令。- 0:000> dp 4CD0618h L1
- 04cd0618 00b20480
- 0:000> !U 00b20480
- Unmanaged code
- 00b20480 e88bec4b6e call clr!PrecodeFixupThunk (6efdf110)
- 00b20485 5e pop esi
- 00b20486 001b add byte ptr [ebx],bl
- 00b20488 e883ec4b6e call clr!PrecodeFixupThunk (6efdf110)
- 00b2048d 5e pop esi
- 00b2048e 091a or dword ptr [edx],ebx
- 00b20490 e87bec4b6e call clr!PrecodeFixupThunk (6efdf110)
- 00b20495 5e pop esi
- 00b20496 1219 adc bl,byte ptr [ecx]
- 00b20498 e873ec4b6e call clr!PrecodeFixupThunk (6efdf110)
复制代码 我这里没有得到 Add 的方法描述符。没有实现。
2.3、在动态代码中注入 Debugger.Break()实现代码的调试。
测试源码:Example_10_1_3
我们通过 windbg 正常加载项目,直接【g】运行,会在16行处暂停,如图:
执行效果如下:- 1 0:000> !clrstack
- 2 OS Thread Id: 0x482c (0)
- 3 Child SP IP Call Site
- 4 00b5f3a8 760df262 [HelperMethodFrame: 00b5f3a8] System.Diagnostics.Debugger.BreakInternal()
- 5 00b5f424 6e74f195 System.Diagnostics.Debugger.Break() [f:\dd\ndp\clr\src\BCL\system\diagnostics\debugger.cs @ 91]
- 6 00b5f44c <strong>00f10053</strong> DynamicClass.Add(Int32, Int32)
- 7 00b5f458 00ee0b56 Example_10_1_3.Program.Main(System.String[]) [E:\Visual Studio 2022\...\Example_10_1_3\Program.cs @ 24]
- 8 00b5f654 6efdf036 [GCFrame: 00b5f654]
复制代码 00f10053 红色标注的就是我们要找的Add 方法。我们可以使用【!u】命令查看它的汇编代码。- 1 0:000> !U /d 00f10053
- 2 Normal JIT generated code
- 3 DynamicClass.Add(Int32, Int32)
- 4 Begin 00f10048, size 12
- 5 00f10048 57 push edi
- 6 00f10049 56 push esi
- 7 00f1004a 8bf9 mov edi,ecx
- 8 00f1004c 8bf2 mov esi,edx
- 9 00f1004e e8e5f0836d call mscorlib_ni!System.Diagnostics.Debugger.Break (6e74f138)
- 10 >>> 00f10053 03fe add edi,esi
- 11 00f10055 8bc7 mov eax,edi
- 12 00f10057 5e pop esi
- 13 00f10058 5f pop edi
- 14 00f10059 c3 ret
复制代码 2.4、程序集泄露
测试源码:Example_10_1_4
我们加载完 Example_10_1_4 项目,【g】继续运行,运行一段时间,我们点击【break】按钮暂停,当然,在运行的时候,我们可以通过任务管理器,查看这个应用程序的内存,发现内存一直在增长。
我们使用【!dumpdomain】命令,查看一下应用程序域,不看不知道,一看吓一跳。- 1 0:006> !dumpdomain
- 2 --------------------------------------
- 3 System Domain: 6f71caf8
- 4 LowFrequencyHeap: 6f71ce1c
- 5 HighFrequencyHeap: 6f71ce68
- 6 StubHeap: 6f71ceb4
- 7 Stage: OPEN
- 8 Name: None
- 9 --------------------------------------
- 10 Shared Domain: 6f71c7a8
- 11 LowFrequencyHeap: 6f71ce1c
- 12 HighFrequencyHeap: 6f71ce68
- 13 StubHeap: 6f71ceb4
- 14 Stage: OPEN
- 15 Name: None
- 16 Assembly: 00c9d6f8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
- 17 ClassLoader: 00c9d7c0
- 18 Module Name
- 19 6dbc1000 C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
- 20
- 21 --------------------------------------
- 22 Domain 1: 00c4d7c8
- 23 LowFrequencyHeap: 00c4dc34
- 24 HighFrequencyHeap: 00c4dc80
- 25 StubHeap: 00c4dccc
- 26 Stage: OPEN
- 27 SecurityDescriptor: 00c4ece0
- 28 Name: Example_10_1_4.exe
- 29 Assembly: 00c9d6f8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
- 30 ClassLoader: 00c9d7c0
- 31 SecurityDescriptor: 00c9b0d0
- 32 Module Name
- 33 6dbc1000 C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
- 34
- 35 Assembly: 00caa098 [E:\Visual Studio 2022\Source\Projects\Example_10_1_4.exe]
- 36 ClassLoader: 00ca7268
- 37 SecurityDescriptor: 00ca7160
- 38 Module Name
- 39 00c04044 E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_10_1_4\bin\Debug\Example_10_1_4.exe
- 40
- 41 Assembly: 00cab1c0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll]
- 42 ClassLoader: 00cab288
- 43 SecurityDescriptor: 00ca88e0
- 44 Module Name
- 45 6c591000 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Core\v4.0_4.0.0.0__b77a5c561934e089\System.Core.dll
- 46
- 47 Assembly: 00caafc0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll]
- 48 ClassLoader: 00ca9b18
- 49 SecurityDescriptor: 00cac6f0
- 50 Module Name
- 51 6d041000 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll
- 52
- 53 Assembly: 00cabee0 [C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll]
- 54 ClassLoader: 00cb3bd0
- 55 SecurityDescriptor: 00cabe48
- 56 Module Name
- 57 6bc01000 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Xml\v4.0_4.0.0.0__b77a5c561934e089\System.Xml.dll
- 58
- 59 Assembly: 00cb8b80 [C:\Windows\Microsoft.Net\assembly\\System.Configuration.dll]
- 60 ClassLoader: 00cb7538
- 61 SecurityDescriptor: 00cb8ae8
- 62 Module Name
- 63 6dab1000 C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll
- 64
- 65 Assembly: 00cc3418 (Dynamic) []
- 66 ClassLoader: 00cc34e0
- 67 SecurityDescriptor: 00cc3380
- 68 Module Name
- 69 00c08b6c Dynamic Module
- 70
- 71 Assembly: 00cc8f58 (Dynamic) []
- 72 ClassLoader: 00cc9020
- 73 SecurityDescriptor: 00cc8ec0
- 74 Module Name
- 75 00c0954c Dynamic Module
- 76
- 77 Assembly: 00cd49e0 (Dynamic) []
- 78 ClassLoader: 00cd4aa8
- 79 SecurityDescriptor: 00cd4948
- 80 Module Name
- 81 00c09ce4 Dynamic Module
- 82
- 83 ......(我清理了大量的动态模块,显示太多了)
复制代码 我们查看一下模块的方法表,使用【!dumpmodule -mt】命令。- 1 0:006> !dumpmodule -mt 09e65b2c
- 2 Name: Unknown Module
- 3 Attributes: Reflection
- 4 Assembly: 097f86c0
- 5 LoaderHeap: 00000000
- 6 TypeDefToMethodTableMap: 09e8195c
- 7 TypeRefToMethodTableMap: 09e81970
- 8 MethodDefToDescMap: 09e81984
- 9 FieldDefToDescMap: 09e819ac
- 10 MemberRefToDescMap: 00000000
- 11 FileReferencesMap: 09e819fc
- 12 AssemblyReferencesMap: 09e81a10
- 13
- 14 Types defined in this module
- 15
- 16 MT TypeDef Name
- 17 ------------------------------------------------------------------------------
- 18 <strong>09e65f68</strong> 0x02000002 <Unloaded Type>
- 19 09e6605c 0x02000003 <Unloaded Type>
- 20 09e660e4 0x02000004 <Unloaded Type>
- 21 09e66184 0x02000005 <Unloaded Type>
- 22 09e66260 0x02000006 <Unloaded Type>
- 23
- 24 Types referenced in this module
- 25
- 26 MT TypeRef Name
- 27 ------------------------------------------------------------------------------
复制代码 我们查找红色标记 09e65f68 方法表的所有方法描述符。- 1 0:006> !dumpmt -md 09e65f68
- 2 EEClass: 09e82bf8
- 3 Module: 09e65b2c
- 4 Name: <Unloaded Type>
- 5 mdToken: 02000002
- 6 File: Unknown Module
- 7 BaseSize: 0x44
- 8 ComponentSize: 0x0
- 9 Slots in VTable: 8
- 10 Number of IFaces in IFaceMap: 0
- 11 --------------------------------------
- 12 MethodDesc Table
- 13 Entry MethodDe JIT Name
- 14 6dfc97b8 6dbcc838 PreJIT System.Object.ToString()
- 15 6dfc96a0 6dd08978 PreJIT System.Object.Equals(System.Object)
- 16 6dfd21f0 6dd08998 PreJIT System.Object.GetHashCode()
- 17 6df84f2c 6dd089a0 PreJIT System.Object.Finalize()
- 18 09e4a47d 09e65f44 NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCustomer.InitCallbacks()
- 19 09e4a481 09e65f4c NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCustomer..ctor()
- 20 09e4a475 09e65f2c NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCustomer.Write3_FabrikamCustomer(...)
- 21 09e4a479 09e65f38 NONE Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterCustomer.Write2_Customer(... Boolean)
复制代码 这里大量的有 Serialization.GeneratedAssembly 这个方法,估计判断是程序集泄露。既然这么多程序集产生,应该就有一个创建程集的方法,我们使用【x】命令,查找一下。- 1 0:006> x clr!*CreateDynamic*
- 2 6f385b06 clr!Assembly::CreateDynamicModule (public: class ReflectionModule * __thiscall Assembly::CreateDynamicModule(unsigned short const *,unsigned short const *,int,int *))
- 3 6f5b0aff clr!HENUMInternal::CreateDynamicArrayEnum (public: static long __stdcall HENUMInternal::CreateDynamicArrayEnum(unsigned long,struct HENUMInternal * *))
- 4 6f166b49 <strong>clr!Assembly::CreateDynamic</strong> (public: static class Assembly * __stdcall Assembly::CreateDynamic(class AppDomain *,struct CreateDynamicAssemblyArgs *))
- 5 6f167440 clr!AppDomainNative::CreateDynamicAssembly (public: static class Object * __fastcall AppDomainNative::CreateDynamicAssembly(class AppDomainBaseObject *,class AssemblyNameBaseObject *,enum SecurityContextSource,int,int,class Array<unsigned char> *,class Array<unsigned char> *,class Object *,class Object *,class Object *,enum StackCrawlMark *,class Object *))
- 6 6f3dd86f clr!DynamicMethodTable::CreateDynamicMethodTable (public: static void __stdcall DynamicMethodTable::CreateDynamicMethodTable(class DynamicMethodTable * *,class Module *,class AppDomain *))
复制代码 就是这个方法创建程序集,我们可以对这个方法下一个断点。- 1 0:006> bp clr!Assembly::CreateDynamic
复制代码 【g】继续运行,在 CreateDynamic 方法内暂停。- 1 0:006> g
- 2 Breakpoint 0 hit
- 3 eax=00c4d7c8 ebx=00000000 ecx=00c4d7c8 edx=008fecb4 esi=02ad7a5c edi=00000000
- 4 eip=6f166b49 esp=008fec3c ebp=008fed00 iopl=0 nv up ei pl nz na po nc
- 5 cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
- 6 <strong>clr!</strong><strong>Assembly::CreateDynamic</strong>:(成功断住)
- 7 6f166b49 6814070000 push 714h
复制代码 然后我们使用【k】命令,查看是谁调用了这个函数。- 1 0:000> k
- 2 *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\System.Xml\040fa6ee0be6d987f3e8edf9010ce68a\System.Xml.ni.dll
- 3 *** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\System.Core\44e36f78b5e2f34aba2d7b5667796954\System.Core.ni.dll
- 4 # ChildEBP RetAddr
- 5 00 010feef0 6f167543 clr!Assembly::CreateDynamic
- 6 01 010feef0 6dfef391 clr!AppDomainNative::CreateDynamicAssembly+0xdf
- 7 02 010fef78 6dfef261 mscorlib_ni!System.Reflection.Emit.AssemblyBuilder..ctor+0xed [f:\dd\ndp\clr\src\BCL\system\reflection\emit\assemblybuilder.cs @ 424]
- 8 03 010fefd4 6e016558 mscorlib_ni!System.Reflection.Emit.AssemblyBuilder.InternalDefineDynamicAssembly+0x89 [f:\dd\ndp\clr\src\BCL\system\reflection\emit\assemblybuilder.cs @ 569]
- 9 04 010feffc 6e016527 mscorlib_ni!System.AppDomain.InternalDefineDynamicAssembly+0x28 [f:\dd\ndp\clr\src\BCL\system\appdomain.cs @ 1515]
- 10 05 010ff02c 6bd100bd mscorlib_ni!System.AppDomain.DefineDynamicAssembly+0x2b [f:\dd\ndp\clr\src\BCL\system\appdomain.cs @ 1221]
- 11 06 010ff044 6bd0fa1b System_Xml_ni!System.Xml.Serialization.CodeGenerator.CreateAssemblyBuilder+0x8d
- 12 07 010ff0dc 6bd0f696 System_Xml_ni!System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly+0xcf
- 13 08 010ff110 6c09db2a System_Xml_ni!System.Xml.Serialization.TempAssembly..ctor+0xae
- 14 09 010ff138 6c09da72 System_Xml_ni!System.Xml.Serialization.XmlSerializer.GenerateTempAssembly+0x6a
- 15 0a 010ff164 6c09d8e7 System_Xml_ni!System.Xml.Serialization.XmlSerializer..ctor+0xd2
- 16 0b 010ff18c 017710aa System_Xml_ni!System.Xml.Serialization.XmlSerializer..ctor+0x2f
- 17 WARNING: Frame IP not in any known module. Following frames may be wrong.
- 18 0c 010ff1e8 01770ffc 0x17710aa
- 19 0d 010ff200 01770f88 0x1770ffc
- 20 0e 010ff210 6dfaaa67 0x1770f88
- 21 0f 010ff244 6c77c5de mscorlib_ni!System.Collections.Generic.List`1..ctor+0xf7 [f:\dd\ndp\clr\src\BCL\system\collections\generic\list.cs @ 99]
- 22 10 010ff258 017708f2 System_Core_ni+0x1ec5de
- 23 11 010ff288 6efdf036 0x17708f2
- 24 12 010ff294 6efe22da clr!CallDescrWorkerInternal+0x34
- 25 13 010ff2e8 6efe859b clr!CallDescrWorkerWithHandler+0x6b
- 26 14 010ff354 6f18b11b clr!MethodDescCallSite::CallTargetWorker+0x16a
- 27 15 010ff478 6f18b7fa clr!RunMain+0x1b3
- 28 16 010ff6e4 6f18b727 clr!Assembly::ExecuteMainMethod+0xf7
- 29 17 010ffbc8 6f18b8a8 clr!SystemDomain::ExecuteMainMethod+0x5ef
- 30 18 010ffc20 6f18b9ce clr!ExecuteEXE+0x4c
- 31 19 010ffc60 6f187305 clr!_CorExeMainInternal+0xdc
- 32 1a 010ffc9c 721bfa84 clr!_CorExeMain+0x4d
- 33 1b 010ffcd4 7224e81e mscoreei!_CorExeMain+0xd6
- 34 1c 010ffce4 72254338 MSCOREE!ShellShim__CorExeMain+0x9e
- 35 1d 010ffcfc 765ff989 MSCOREE!_CorExeMain_Exported+0x8
- 36 1e 010ffcfc 77b27084 KERNEL32!BaseThreadInitThunk+0x19
- 37 1f 010ffd58 77b27054 ntdll!__RtlUserThreadStart+0x2f
- 38 20 010ffd68 00000000 ntdll!_RtlUserThreadStart+0x1b
复制代码 或者我们使用【!clrstack】命令,查看一下调用堆栈,这个命令看的更清楚。- 1 0:000> !clrstack
- 2 OS Thread Id: 0x3534 (0)
- 3 Child SP IP Call Site
- 4 010fee44 6f166b49 [HelperMethodFrame_PROTECTOBJ: 010fee44] System.Reflection.Emit.AssemblyBuilder.nCreateDynamicAssembly(System.AppDomain, System.Reflection.AssemblyName, System.Security.Policy.Evidence, System.Threading.StackCrawlMark ByRef, System.Security.PermissionSet, System.Security.PermissionSet, System.Security.PermissionSet, Byte[], Byte[], System.Reflection.Emit.AssemblyBuilderAccess, System.Reflection.Emit.DynamicAssemblyFlags, System.Security.SecurityContextSource)
- 5 010fef20 6dfef391 System.Reflection.Emit.AssemblyBuilder..ctor(System.AppDomain, System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess, System.String, System.Security.Policy.Evidence, System.Security.PermissionSet, System.Security.PermissionSet, System.Security.PermissionSet, System.Threading.StackCrawlMark ByRef, System.Collections.Generic.IEnumerable`1<System.Reflection.Emit.CustomAttributeBuilder>, System.Security.SecurityContextSource) [f:\dd\ndp\clr\src\BCL\system\reflection\emit\assemblybuilder.cs @ 424]
- 6 010fefa8 6dfef261 System.Reflection.Emit.AssemblyBuilder.InternalDefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess, System.String, System.Security.Policy.Evidence, System.Security.PermissionSet, System.Security.PermissionSet, System.Security.PermissionSet, System.Threading.StackCrawlMark ByRef, System.Collections.Generic.IEnumerable`1<System.Reflection.Emit.CustomAttributeBuilder>, System.Security.SecurityContextSource) [f:\dd\ndp\clr\src\BCL\system\reflection\emit\assemblybuilder.cs @ 569]
- 7 010feffc 6e016558 System.AppDomain.InternalDefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess, System.String, System.Security.Policy.Evidence, System.Security.PermissionSet, System.Security.PermissionSet, System.Security.PermissionSet, System.Threading.StackCrawlMark ByRef, System.Collections.Generic.IEnumerable`1<System.Reflection.Emit.CustomAttributeBuilder>, System.Security.SecurityContextSource) [f:\dd\ndp\clr\src\BCL\system\appdomain.cs @ 1515]
- 8 010ff028 6e016527 System.AppDomain.DefineDynamicAssembly(System.Reflection.AssemblyName, System.Reflection.Emit.AssemblyBuilderAccess) [f:\dd\ndp\clr\src\BCL\system\appdomain.cs @ 1221]
- 9 010ff038 6bd100bd System.Xml.Serialization.CodeGenerator.CreateAssemblyBuilder(System.AppDomain, System.String)
- 10 010ff04c 6bd0fa1b System.Xml.Serialization.TempAssembly.GenerateRefEmitAssembly(System.Xml.Serialization.XmlMapping[], System.Type[], System.String, System.Security.Policy.Evidence)
- 11 010ff0ec 6bd0f696 System.Xml.Serialization.TempAssembly..ctor(System.Xml.Serialization.XmlMapping[], System.Type[], System.String, System.String, System.Security.Policy.Evidence)
- 12 010ff128 6c09db2a System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(System.Xml.Serialization.XmlMapping, System.Type, System.String, System.String, System.Security.Policy.Evidence)
- 13 010ff14c 6c09da72 <strong>System.Xml.Serialization.XmlSerializer..ctor</strong>(System.Type, System.Xml.Serialization.XmlAttributeOverrides, System.Type[], System.Xml.Serialization.XmlRootAttribute, System.String, System.String, System.Security.Policy.Evidence)
- 14 010ff184 6c09d8e7 <strong>System.Xml.Serialization.XmlSerializer..ctor</strong>(System.Type, System.Xml.Serialization.XmlRootAttribute)
- 15 010ff198 017710aa <strong>Example_10_1_4.Program.GetCustomer(</strong>Int32, System.String, System.String) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_10_1_4\Program.cs @ 27]
- 16 010ff1f4 01770ffc Example_10_1_4.Program+c__DisplayClass0_0.b__0(Int32) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_10_1_4\Program.cs @ 18]
- 17 010ff208 01770f88 System.Linq.Enumerable+WhereSelectEnumerableIterator`2[[System.Int32, mscorlib],[System.__Canon, mscorlib]].MoveNext()
- 18 010ff218 6dfaaa67 System.Collections.Generic.List`1[[System.__Canon, mscorlib]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>) [f:\dd\ndp\clr\src\BCL\system\collections\generic\list.cs @ 99]
- 19 010ff24c 6c77c5de System.Linq.Enumerable.ToList[[System.__Canon, mscorlib]](System.Collections.Generic.IEnumerable`1<System.__Canon>)
- 20 010ff260 017708f2 Example_10_1_4.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_10_1_4\Program.cs @ 17]
- 21 010ff3f8 6efdf036 [GCFrame: 010ff3f8]
复制代码 我们终于找到问题了。代码中把解决办法已经写好了。
四、总结
终于写完了,写作的过程是累并快乐着。学习过程真的没那么轻松,还好是自己比较喜欢这一行,否则真不知道自己能不能坚持下来。老话重谈,《高级调试》的这本书第一遍看,真的很晕,第二遍稍微好点,不学不知道,一学吓一跳,自己欠缺的很多。好了,不说了,不忘初心,继续努力,希望老天不要辜负努力的人。
来源:https://www.cnblogs.com/PatrickLiu/archive/2023/11/21/17838106.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作! |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
x
|