Windows软件调试初探-托管
碎碎念
C#和Visual Basic .NET等编程语言由公共基础设施支持,被公共类型系统CTS编译为微软中间语言MSIL(也叫公共中间语言CIL)。CIL再由公共运行时CLR或及时编译器JIT在操作系统服务上的本地代码上运行。
CIL是一种面向对象、基于栈的字节码,有200多条指令分为10组。.NET框架中有ILASM和ILDASM用于CIL指令与CIL字节码转化。
.NET将源程序中类型定义、名称等信息以元数据形式保存,并可在运行期使用。元数据与CIL字节码组合起来叫做程序集。
开发.NET程序的语言叫托管语言,用.NET技术开发的程序叫托管程序,CLR有时也叫托管运行时。.NET程序运行时,系统加载MSCOREE.dll,用于解析.NET程序中信息并为其加载合适版本的CLR。
执行引擎EE模块MSCOREE通过注册表确定所需版本CLR,加载其接口模块mscoreei.dll,要加载的CLR主模块为clr.dll。在.NET 2.0下CLR主模块桌面/工作站版本为mscorwks.dll,服务器版本为mscorwks.dll。.NET运行时经常原位更新,所以运行时目录中版本号可能不准确。
类和方法表
例如程序:
1 2 3 4 5 6 7 8 9 10 11
| using System; using System.Collections.Generic; using System.Text; namespace CliHello { public class CliHello2 { static void Main(string[] args) { Console.WriteLine("C#/CLI Hello, World!"); Console.ReadLine(); } } }
|
调试方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
| 0:000> sxe ld:clr //在收到clr模块加载事件时中断 0:000> g ModLoad: 75cf0000 75d6c000 C:\Windows\SysWOW64\ADVAPI32.dll ModLoad: 76190000 76254000 C:\Windows\SysWOW64\msvcrt.dll ModLoad: 769e0000 76a62000 C:\Windows\SysWOW64\sechost.dll ModLoad: 75630000 756e9000 C:\Windows\SysWOW64\RPCRT4.dll ModLoad: 754c0000 75548000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll ModLoad: 776a0000 776eb000 C:\Windows\SysWOW64\SHLWAPI.dll ModLoad: 754a0000 754b3000 C:\Windows\SysWOW64\kernel.appcore.dll ModLoad: 75490000 75498000 C:\Windows\SysWOW64\VERSION.dll ModLoad: 74cd0000 7548a000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll eax=c0000034 ebx=0075f248 ecx=00000000 edx=00000000 esi=0000016c edi=00a5b190 eip=778f67ec esp=0075f1fc ebp=0075f240 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!NtMapViewOfSection+0xc: 778f67ec c22800 ret 28h 0:000> bp clr!RunMain breakpoint 0 redefined 0:000> g ModLoad: 76260000 762fc000 C:\Windows\SysWOW64\OLEAUT32.dll ModLoad: 77420000 7769c000 C:\Windows\SysWOW64\combase.dll ModLoad: 74b50000 74b6a000 C:\Windows\SysWOW64\bcrypt.dll ModLoad: 76970000 769d2000 C:\Windows\SysWOW64\bcryptprimitives.dll ModLoad: 776f0000 7783d000 C:\Windows\SysWOW64\ole32.dll ModLoad: 74b30000 74b45000 C:\Windows\SysWOW64\CRYPTSP.dll ModLoad: 74b00000 74b30000 C:\Windows\SysWOW64\rsaenh.dll ModLoad: 74af0000 74afb000 C:\Windows\SysWOW64\CRYPTBASE.dll Breakpoint 0 hit eax=0075f1e4 ebx=00000000 ecx=00c845dc edx=00000000 esi=00000000 edi=00c845dc eip=74dc6bce esp=0075f1a4 ebp=0075f40c iopl=0 nv up ei pl zr na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246 clr!RunMain: 74dc6bce 68f8000000 push 0F8h 0:000> k # ChildEBP RetAddr 00 0075f1a0 74d9c671 clr!RunMain 01 0075f40c 74d9c4e9 clr!Assembly::ExecuteMainMethod+0xf7 02 0075f8f0 74d9c8f4 clr!SystemDomain::ExecuteMainMethod+0x61c 03 0075f948 74d9c83a clr!ExecuteEXE+0x4c 04 0075f988 74ddb62c clr!_CorExeMainInternal+0xd8 05 0075f9c4 754ca36e clr!_CorExeMain+0x4d 06 0075fa00 7555fb6e mscoreei!_CorExeMain+0x100 07 0075fa10 75565708 MSCOREE!ShellShim__CorExeMain+0x9e 08 0075fa18 76057ba9 MSCOREE!_CorExeMain_Exported+0x8 09 0075fa28 778ebb9b KERNEL32!BaseThreadInitThunk+0x19 0a 0075fa80 778ebb1f ntdll!__RtlUserThreadStart+0x2b 0b 0075fa90 00000000 ntdll!_RtlUserThreadStart+0x1b 0:000> .loadby sos clr //加载托管观察扩展命令模块SOS 0:000> !name2ee CliHello.exe CliHello.CliHello2 //获取类的执行引擎信息 Module: 00c84044 Assembly: CliHello.exe Token: 02000002 MethodTable: 00c845f0 //方法表地址 EEClass: 00c81d08 //类信息地址 Name: CliHello.CliHello2 0:000> !DumpMT -MD 00c845f0 EEClass: 00c81d08 Module: 00c84044 Name: CliHello.CliHello2 mdToken: 02000002 File: D:\Downloads\软件调试随书附件\附件\ch207\CliHello\CliHello\bin\Debug\CliHello.exe BaseSize: 0xc ComponentSize: 0x0 Slots in VTable: 6 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table //方法表 Entry MethodDe JIT Name 02810039 00fa6744 NONE System.Object.ToString() 0281003d 00fa674c NONE System.Object.Equals(System.Object) 02810049 00fa676c NONE System.Object.GetHashCode() 0281a440 00fa6784 JIT System.Object.Finalize() 0281e528 00c845e8 NONE CliHello.CliHello2..ctor() 0281e520 00c845dc NONE CliHello.CliHello2.Main(System.String[]) 0:000> !DumpMD /d 00c845dc Method Name: CliHello.CliHello2.Main(System.String[]) Class: 00c81d08 MethodTable: 00c845f0 mdToken: 06000001 Module: 00c84044 IsJitted: no CodeAddr: ffffffff Transparency: Not calculated 0:000> u 0281e520 0281e520 e8cb404c72 call clr!PrecodeFixupThunk (74ce25f0) 0281e525 5e pop esi 0281e526 0001 add byte ptr [ecx],al 0281e528 e8c3404c72 call clr!PrecodeFixupThunk (74ce25f0) 0281e52d 5e pop esi 0281e52e 0300 add eax,dword ptr [eax] 0281e530 dc45c8 fadd qword ptr [ebp-38h] 0281e533 0000 add byte ptr [eax],al
|
方法表中前4个是编译器自动赋予的基类方法,所有类必须从System.Object派生来。第5个事编译器自动产生的构造函数。
方法表共有4列。第一列为方法入口,若还未编译则为触发及时编译的桩代码。第二列为方法描述。第三列为编译状态,PreJIT表示已预编译为机器码,None还未编译,JIT已及时编译。第四列方法名。
例如Main方法反编译后有clr!PrecodeEixupThunk
即为桩代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| 0:000> sxe ld:clrjit 0:000> g ModLoad: 75cf0000 75d6c000 C:\Windows\SysWOW64\ADVAPI32.dll ModLoad: 76190000 76254000 C:\Windows\SysWOW64\msvcrt.dll ModLoad: 769e0000 76a62000 C:\Windows\SysWOW64\sechost.dll ModLoad: 75630000 756e9000 C:\Windows\SysWOW64\RPCRT4.dll ModLoad: 754c0000 75548000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll ModLoad: 776a0000 776eb000 C:\Windows\SysWOW64\SHLWAPI.dll ModLoad: 754a0000 754b3000 C:\Windows\SysWOW64\kernel.appcore.dll ModLoad: 75490000 75498000 C:\Windows\SysWOW64\VERSION.dll ModLoad: 74cd0000 7548a000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll ModLoad: 75810000 759b6000 C:\Windows\SysWOW64\USER32.dll ModLoad: 76950000 7696a000 C:\Windows\SysWOW64\win32u.dll ModLoad: 75ca0000 75cc2000 C:\Windows\SysWOW64\GDI32.dll ModLoad: 74cb0000 74cc5000 C:\Windows\SysWOW64\VCRUNTIME140_CLR0400.dll ModLoad: 770d0000 771ad000 C:\Windows\SysWOW64\gdi32full.dll ModLoad: 755b0000 75629000 C:\Windows\SysWOW64\msvcp_win.dll ModLoad: 00ec0000 00f73000 C:\Windows\SysWOW64\ucrtbase_clr0400.dll ModLoad: 74bf0000 74ca3000 C:\Windows\SysWOW64\ucrtbase_clr0400.dll ModLoad: 75a80000 75b92000 C:\Windows\SysWOW64\ucrtbase.dll ModLoad: 77840000 77866000 C:\Windows\SysWOW64\IMM32.DLL ModLoad: 77420000 7769c000 C:\Windows\SysWOW64\combase.dll (5d88.5630): Unknown exception - code 04242420 (first chance) ModLoad: 04e50000 053b8000 mscorlib.dll ModLoad: 053c0000 05928000 mscorlib.dll ModLoad: 74b70000 74bee000 C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll eax=c0000034 ebx=008fdee8 ecx=00000000 edx=00000000 esi=00000264 edi=00c63800 eip=778f67ec esp=008fde9c ebp=008fdee0 iopl=0 nv up ei pl nz na pe nc cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206 ntdll!NtMapViewOfSection+0xc: 778f67ec c22800 ret 28h 0:000> k # ChildEBP RetAddr 00 008fde98 7790d03c ntdll!NtMapViewOfSection+0xc 01 008fdee8 778c10ea ntdll!LdrpMapViewOfSection+0x77 02 008fdf30 778c0de6 ntdll!LdrpMinimalMapModule+0xe4 03 008fdf58 778bdd53 ntdll!LdrpMapDllWithSectionHandle+0x15 04 008fdfc0 778ba262 ntdll!LdrpMapDllNtFileName+0x13e 05 008fe0f0 778c76bd ntdll!LdrpMapDllFullPath+0xb8 06 008fe138 7790b7a9 ntdll!LdrpProcessWork+0x6e 07 008fe184 778c75aa ntdll!LdrpLoadDllInternal+0x1f2 08 008fe328 772ce203 ntdll!LdrLoadDll+0x14a 09 008fe368 754ccc4e KERNELBASE!LoadLibraryExW+0x153 0a 008fe4c0 754d2601 mscoreei!RuntimeDesc::LoadLibrary+0xa5 0b 008fe524 74e2e07c mscoreei!CLRRuntimeInfoImpl::LoadLibrary+0xda 0c 008fe5b8 74e2e238 clr!LoadAndInitializeJIT+0x7e 0d 008fe5f8 74d2f102 clr!EEJitManager::LoadJIT+0x9c 0e 008fe9a0 74d2eef5 clr!UnsafeJitFunction+0xc6 0f 008fea9c 74d2e9e1 clr!MethodDesc::MakeJitWorker+0x48c 10 008feb0c 74d0673c clr!MethodDesc::DoPrestub+0x596 11 008feb84 74ce299b clr!PreStubWorker+0xef 12 008feba8 74ce2516 clr!ThePreStub+0x11 13 008febc4 74cee549 clr!CallDescrWorkerInternal+0x34 14 008fec18 74cef217 clr!CallDescrWorkerWithHandler+0x6b 15 008fec94 74ddc01e clr!MethodDescCallSite::CallTargetWorker+0x170 16 008fed8c 74de1c8a clr!AppDomain::InitializeDomainContext+0x1f4 17 008ff218 74d9c15a clr!SystemDomain::InitializeDefaultDomain+0x288 18 008ff6f8 74d9c8f4 clr!SystemDomain::ExecuteMainMethod+0x1e2 19 008ff750 74d9c83a clr!ExecuteEXE+0x4c 1a 008ff790 74ddb62c clr!_CorExeMainInternal+0xd8 1b 008ff7cc 754ca36e clr!_CorExeMain+0x4d 1c 008ff808 7555fb6e mscoreei!_CorExeMain+0x100 1d 008ff818 75565708 MSCOREE!ShellShim__CorExeMain+0x9e 1e 008ff820 76057ba9 MSCOREE!_CorExeMain_Exported+0x8 1f 008ff830 778ebb9b KERNEL32!BaseThreadInitThunk+0x19 20 008ff888 778ebb1f ntdll!__RtlUserThreadStart+0x2b 21 008ff898 00000000 ntdll!_RtlUserThreadStart+0x1b
|
用SOS扩展的bpmd命令对Main方法下端点,因其未编译,下的是未决端点。命中该未决端点后,异常代码0xe0444141即.DAC,表示数据访问组件DAC。DAC为.NET框架中供各种工具访问.NET内部的组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| 0:000> .loadby sos clr 0:000> !name2ee CliHello.exe CliHello.CliHello2 Module: 00f84044 Assembly: CliHello.exe Token: 02000002 MethodTable: 00f845f0 EEClass: 00f81d08 Name: CliHello.CliHello2 0:000> !DumpMT -MD 00f845f0 EEClass: 00f81d08 Module: 00f84044 Name: CliHello.CliHello2 mdToken: 02000002 File: D:\Downloads\软件调试随书附件\附件\ch207\CliHello\CliHello\bin\Debug\CliHello.exe BaseSize: 0xc ComponentSize: 0x0 Slots in VTable: 6 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDe JIT Name 02e20039 01226744 NONE System.Object.ToString() 02e2003d 0122674c NONE System.Object.Equals(System.Object) 02e20049 0122676c NONE System.Object.GetHashCode() 02e2a440 01226784 JIT System.Object.Finalize() 02e2e528 00f845e8 NONE CliHello.CliHello2..ctor() 02e2e520 00f845dc NONE CliHello.CliHello2.Main(System.String[]) 0:000> !bpmd -MD 00f845dc MethodDesc = 00f845dc Adding pending breakpoints... //可对Main进行命中 Main变为JIT 0:000> !DumpMT -MD 00f845f0 EEClass: 00f81d08 Module: 00f84044 Name: CliHello.CliHello2 mdToken: 02000002 File: D:\Downloads\软件调试随书附件\附件\ch207\CliHello\CliHello\bin\Debug\CliHello.exe BaseSize: 0xc ComponentSize: 0x0 Slots in VTable: 6 Number of IFaces in IFaceMap: 0 -------------------------------------- MethodDesc Table Entry MethodDe JIT Name 02e20039 01226744 NONE System.Object.ToString() 02e2003d 0122674c NONE System.Object.Equals(System.Object) 02e20049 0122676c NONE System.Object.GetHashCode() 02e2a440 01226784 JIT System.Object.Finalize() 02e2e528 00f845e8 NONE CliHello.CliHello2..ctor() 02e2f940 00f845dc JIT CliHello.CliHello2.Main(System.String[]) 0:000> u 02e2f940 L14 02e2f940 55 push ebp 02e2f941 8bec mov ebp,esp 02e2f943 83ec08 sub esp,8 02e2f946 33c0 xor eax,eax 02e2f948 8945f8 mov dword ptr [ebp-8],eax 02e2f94b 894dfc mov dword ptr [ebp-4],ecx 02e2f94e 833df042f80000 cmp dword ptr ds:[0F842F0h],0 02e2f955 7405 je 02e2f95c 02e2f957 e844041e72 call clr!JIT_DbgIsJustMyCode (7500fda0) 02e2f95c 90 nop 02e2f95d 8b0db023ef03 mov ecx,dword ptr ds:[3EF23B0h] 02e2f963 ff156050e802 call dword ptr ds:[2E85060h] 02e2f969 90 nop 02e2f96a ff15b84fe802 call dword ptr ds:[2E84FB8h] 02e2f970 8945f8 mov dword ptr [ebp-8],eax 02e2f973 90 nop 02e2f974 90 nop 02e2f975 8be5 mov esp,ebp 02e2f977 5d pop ebp 02e2f978 c3 ret
|
注意这里触发的断点应该在02e2f95c处,其前面代码判断一个全局表示,为0则用clr!JIT_DbgIsJustMyCode
调用调试接口,否则在02e2f95c断下。下面栈帧#01~#03即为CLR通过“方法描述”来调用.NET方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 0:000> k # ChildEBP RetAddr 00 00eff114 74cee549 clr!CallDescrWorkerInternal 01 00eff168 74cef217 clr!CallDescrWorkerWithHandler+0x6b 02 00eff1d4 74d9c573 clr!MethodDescCallSite::CallTargetWorker+0x170 03 00eff2f8 74d9c671 clr!RunMain+0x1c6 04 00eff564 74d9c4e9 clr!Assembly::ExecuteMainMethod+0xf7 05 00effa48 74d9c8f4 clr!SystemDomain::ExecuteMainMethod+0x61c 06 00effaa0 74d9c83a clr!ExecuteEXE+0x4c 07 00effae0 74ddb62c clr!_CorExeMainInternal+0xd8 08 00effb1c 754ca36e clr!_CorExeMain+0x4d 09 00effb58 7555fb6e mscoreei!_CorExeMain+0x100 0a 00effb68 75565708 MSCOREE!ShellShim__CorExeMain+0x9e 0b 00effb70 76057ba9 MSCOREE!_CorExeMain_Exported+0x8 0c 00effb80 778ebb9b KERNEL32!BaseThreadInitThunk+0x19 0d 00effbd8 778ebb1f ntdll!__RtlUserThreadStart+0x2b 0e 00effbe8 00000000 ntdll!_RtlUserThreadStart+0x1b
|
在SOS模块中用!clrstack
获取.NET中栈,用!dumpstack
获取.NET方法和非托管函数。
辅助调试线程
在托管程序初始化时,.NET运行时创建两个工作线程。一个是负责内存回收机制的终结器进程,另一个是支持调试的调试辅助线程RCThread。
在被调试进程中,依次有:非托管代码、托管代码、托管调试API 运行时控制器。在调试器进程,依次有:非托管调试器、托管调试器、托管调试API接口。.NET下调试托管程序有3种模式,WinDBG为第1种,Visual Studio默认用第2种,第3种需在“调试”中启用“启用本地代码调试”。
- 用非托管调试器调试托管程序中非托管代码。既可调试托管进程中本地模块,也可调试及时编译后的托管代码。需要借助扩展插件才能观察托管中名称和数据结构。灵活性大但不容易观察顶层托管语义,适合调试复杂问题。
- 纯托管调试。用托管调试器通过托管调试API 接口与托管调试API运行时控制器通信,来访问托管代码和数据,并调试托管代码。只能跟踪托管代码,不能跟踪本地代码。最常用,适合开发阶段源代码级调试。
- 混合调试。同时调试托管代码和非托管代码。此时Enc不可使用,单步跟踪时速度慢,通常用于托管代码与非托管代码交互调用。
对于RCThread调试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| 0:006> ~*k //该进程所有线程栈回溯
0 Id: 1f38.3920 Suspend: 1 Teb: 00e79000 Unfrozen # ChildEBP RetAddr 00 00dfee6c 750129ec ntdll!NtReadFile+0xc 01 00dfeed0 057c0edb KERNELBASE!ReadFile+0x5c WARNING: Frame IP not in any known module. Following frames may be wrong. 02 00dfef40 057cb540 0x57c0edb 03 00dfef68 057cb3e9 0x57cb540 04 00dfef94 057cb2c3 0x57cb3e9 05 00dfefac 057cb088 0x57cb2c3 06 00dfefc8 057cb039 0x57cb088 07 00dfefd8 057ca77b 0x57cb039 08 00dfefe0 0575f9a9 0x57ca77b 09 00dff028 73d02516 0x575f9a9 0a 00dff034 73d0e549 clr!CallDescrWorkerInternal+0x34 0b 00dff088 73d0f217 clr!CallDescrWorkerWithHandler+0x6b 0c 00dff0f8 73dbc573 clr!MethodDescCallSite::CallTargetWorker+0x170 0d 00dff21c 73dbc671 clr!RunMain+0x1c6 0e 00dff488 73dbc4e9 clr!Assembly::ExecuteMainMethod+0xf7 0f 00dff96c 73dbc8f4 clr!SystemDomain::ExecuteMainMethod+0x61c 10 00dff9c4 73dbc83a clr!ExecuteEXE+0x4c 11 00dffa04 73dfb62c clr!_CorExeMainInternal+0xd8 12 00dffa40 744ea36e clr!_CorExeMain+0x4d 13 00dffa7c 74d1fb6e mscoreei!_CorExeMain+0x100 14 00dffa8c 74d25708 MSCOREE!ShellShim__CorExeMain+0x9e 15 00dffa94 75c77ba9 MSCOREE!_CorExeMain_Exported+0x8 16 00dffaa4 770abb9b KERNEL32!BaseThreadInitThunk+0x19 17 00dffafc 770abb1f ntdll!__RtlUserThreadStart+0x2b 18 00dffb0c 00000000 ntdll!_RtlUserThreadStart+0x1b
1 Id: 1f38.26ac Suspend: 1 Teb: 00e7d000 Unfrozen # ChildEBP RetAddr 00 015ef800 7707ee48 ntdll!NtWaitForWorkViaWorkerFactory+0xc 01 015ef9c8 75c77ba9 ntdll!TppWorkerThread+0x338 02 015ef9d8 770abb9b KERNEL32!BaseThreadInitThunk+0x19 03 015efa30 770abb1f ntdll!__RtlUserThreadStart+0x2b 04 015efa40 00000000 ntdll!_RtlUserThreadStart+0x1b
2 Id: 1f38.4780 Suspend: 1 Teb: 00e81000 Unfrozen # ChildEBP RetAddr 00 0171fa44 7707ee48 ntdll!NtWaitForWorkViaWorkerFactory+0xc 01 0171fc0c 75c77ba9 ntdll!TppWorkerThread+0x338 02 0171fc1c 770abb9b KERNEL32!BaseThreadInitThunk+0x19 03 0171fc74 770abb1f ntdll!__RtlUserThreadStart+0x2b 04 0171fc84 00000000 ntdll!_RtlUserThreadStart+0x1b
3 Id: 1f38.2960 Suspend: 1 Teb: 00e85000 Unfrozen # ChildEBP RetAddr 00 0185f79c 7707ee48 ntdll!NtWaitForWorkViaWorkerFactory+0xc 01 0185f964 75c77ba9 ntdll!TppWorkerThread+0x338 02 0185f974 770abb9b KERNEL32!BaseThreadInitThunk+0x19 03 0185f9cc 770abb1f ntdll!__RtlUserThreadStart+0x2b 04 0185f9dc 00000000 ntdll!_RtlUserThreadStart+0x1b
4 Id: 1f38.da4 Suspend: 1 Teb: 00e89000 Unfrozen # ChildEBP RetAddr 00 0328fb5c 75011aaf ntdll!NtWaitForMultipleObjects+0xc 01 0328fcec 73df9a7f KERNELBASE!WaitForMultipleObjectsEx+0x18f 02 0328fd58 73df99d1 clr!DebuggerRCThread::MainLoop+0x9c 03 0328fd88 73df990d clr!DebuggerRCThread::ThreadProc+0xd0 04 0328fdb4 75c77ba9 clr!DebuggerRCThread::ThreadProcStatic+0x6c 05 0328fdc4 770abb9b KERNEL32!BaseThreadInitThunk+0x19 06 0328fe1c 770abb1f ntdll!__RtlUserThreadStart+0x2b 07 0328fe2c 00000000 ntdll!_RtlUserThreadStart+0x1b
5 Id: 1f38.fc4 Suspend: 1 Teb: 00e8d000 Unfrozen # ChildEBP RetAddr 00 053cf69c 75011aaf ntdll!NtWaitForMultipleObjects+0xc 01 053cf82c 73d5abf9 KERNELBASE!WaitForMultipleObjectsEx+0x18f 02 053cf858 73d5ceaf clr!FinalizerThread::WaitForFinalizerEvent+0x86 03 053cf884 73d13b74 clr!FinalizerThread::FinalizerThreadWorker+0x40 04 053cf89c 73d13beb clr!ManagedThreadBase_DispatchInner+0x71 05 053cf924 73d13c9b clr!ManagedThreadBase_DispatchMiddle+0x8f 06 053cf980 73dbca48 clr!ManagedThreadBase_DispatchOuter+0x6d 07 053cf9a8 73dbcb1d clr!ManagedThreadBase::FinalizerBase+0x33 08 053cf9e4 73d4ab44 clr!FinalizerThread::FinalizerThreadStart+0xe2 09 053cfa80 75c77ba9 clr!Thread::intermediateThreadProc+0x58 0a 053cfa90 770abb9b KERNEL32!BaseThreadInitThunk+0x19 0b 053cfae8 770abb1f ntdll!__RtlUserThreadStart+0x2b 0c 053cfaf8 00000000 ntdll!_RtlUserThreadStart+0x1b
# 6 Id: 1f38.3ad8 Suspend: 1 Teb: 00e91000 Unfrozen # ChildEBP RetAddr 00 0597fcb0 770f38d9 ntdll!DbgBreakPoint 01 0597fce0 75c77ba9 ntdll!DbgUiRemoteBreakin+0x39 02 0597fcf0 770abb9b KERNEL32!BaseThreadInitThunk+0x19 03 0597fd48 770abb1f ntdll!__RtlUserThreadStart+0x2b 04 0597fd58 00000000 ntdll!_RtlUserThreadStart+0x1b
|
其中包含DebuggerRCThread的线程4即为RCThread,可用~l4
将其挂起。RCThread以及被调试进程内部的其他内部类和函数被称为调试器左端LS,位于调试器进程中的通信和接口函数被称为调试器右端RS。调试器LS与RS之间通过进程间通信IPC机制协作,其使用了以下几个命名事件对象和内存映射对象,其中“%d”表示对应进程ID。微软公开了CLI源程序,即SSCLI,又称ROTOR,源代码包含了RCThread和IPC通信机制源代码。如ipcman目录包含IPC使用的共享内存块有关源文件,rcthread.cpp包含辅助线程的实现,还有调试器CorDbg。
1 2 3 4 5 6 7 8 9 10 11
| 被调试进程: CorDBIPCSetupSyncEvent_%d CorDBIPCLISEventAvailName_%d CorDBIPCLSEventReadName_%d CorDBDebuggerAttacheedEvent_%d
调试进程: Cor_Private_IPCBlock_%d Cor_Public_IPCBlock_%d CLR_PRIVATE_RS_IPCBlock_%d CLR_PUBLIC_IPCBlock_%d
|
调试时LS和RCThread需要访问托管程序中资源,访问前可能需要先获取该资源保护锁。普通线程需由RCThread唤醒后才可释放锁,因此可能导致死锁。RCThread创建刺探线程,来了解哪些情况需要获取锁,哪些情况下不要去冒险。刺探线程的栈回溯中出现clr!HelperCanary::ThreadProc
等。
CLR4调试模型重构
.NET 4.0引入CLR4调试模型,调试器进程与被调试进程直接通过NT内核的用户态调试子系统DbgK协调。调试器附加到被调试托管进程时,用本地用户态调试APIkernel32!DebugActiveProcess
建立调试会话。
SOS扩展
.NET 4.0的没搞懂先不写了。