Net 高级调试之二:CLR和Windows加载器及应用程序域介绍

一、简介
    今天是 Net 高级调试的第二篇文章,第一篇文章记录了自己学习 Net 高级调试的第一步,认识一些调试工具,有了工具的倚仗,我们开始仗剑走天涯了,开始Net 高级调试正式的征程了。我先说一下,我的文章,【调试测试】这部分一般分为两个部分,第一部分是要用到的所有测试代码样例,也为大家提供方便,我第一次做测试还是走了不少弯路的。第二部分,就是使用 Windbg 调试器调试代码的部分,但是,需要说明一下,使用 Windbg还是有一些技巧的,或者说是方法的,如果大家不熟悉,建议提前熟悉一下,因为我的测试过程,不会把所有的过程都照搬下来,会省略一下不太重要的步骤,但是,如果是第一次使用这个软件的,调试的时候,得到的结果可能就和我的不一样,这也是我的一步一步的、痛苦的经验。
    如果在没有说明的情况下,所有代码的测试环境都是 Net Framewok 4.8,但是,有时候为了查看源码,可能需要使用 Net Core 的项目,我会在项目章节里进行说明。好了,废话不多说,开始我们今天的调试工作。
    调试环境我需要进行说明,以防大家不清楚,具体情况我已经罗列出来。
          操作系统:Windows Professional 10
          调试工具:Windbg Preview(可以去Microsoft Store 去下载)
          开发工具:Visual Studio 2022
          Net 版本:Net Framework 4.8
          CoreCLR源码:源码下载二、相关概念    1、Net 框架
        Net 是一个虚拟的运行时环境,包含了一个虚拟的执行引擎(CLR)和一组相关的框架类库,如图:
        
        1.1、宏观概念
            
            a)、ECMA
                C# 语言和公共语言基础结构 (CLI) 规范通过 Ecma International® 进行标准化。用通俗的话来说,ECMA是一个标准,它就是一个CLR的开发规范,或者说是一个设计文档。
                https://learn.microsoft.com/zh-cn/dotnet/fundamentals/standards

b)、CLR
                公共语言运行时。是我们 C#,VB.Net,F#的运行时环境,当然,这也是高级调试要关注的部分。
                CLR 处理内存分配和管理。
                CLR 也是一种虚拟机,不仅可执行应用,还可使用 JIT 编译器快速生成和编译代码。
                最后,我们总结一下,CLR是针对 ECMA 标准的落地实现。

c)、NET 框架
                NET框架有很多,比如:WPF,WinForm,WebForm,Mvc,WebAPI 等。

d)、Net应用程序
                NET 应用程序,更多的指的是用户编写的应用程序,比如:基于 Winform 的ERP,基于 MVC、API 实现的网站系统。

1.2、Net程序的编译过程
            Net程序的编译一般分为两个阶段,第一个阶段就是编译器编译,将C# 源码编译成为 IL 代码,第二个阶段就是 JIT 编译,将 IL 代码编译成为可以直接运行的机器代码。
            a)、编译器编译
                将我们的C#、VB.Net、F#等源码使用 Visual Studio,或者是 CSC 等类似的工具转换为 IL 代码。当然 IL 代码是不能直接运行的。
                当然,IL 代码也是可以看到的,我们可以使用 ILSpy,或者DnSpy工具,加载相应的程序集,就可以查看了,很简单,就不细说了。

b)、JIT编译
                CLR 运行时会将 IL 代码转换成 机器代码。

流程如下:【C# 源码】======》【编译器】=======》【Net 程序集(Exe或者Dll)】=====》【JIT即时编译(CLR)】=====》【机器代码】
                

2、PE头及Windows 加载器
        
        2.1、什么是PE文件
            PE文件的全称是Portable Executable,意为可移植的可执行的文件,常见的EXE、DLL、OCX、SYS、COM都是PE文件,PE文件是微软Windows操作系统上的程序文件,它的一个非常大的作用就是帮助 Windows 加载器 执行程序的入口。
            对于 Net 的 PE 文件,有几点需要注意:
            a)、AddressOfEntryPoint
               程序的入口点相对偏移地址,即(exe+AddressOfEntryPoint)。
              

b)、DIRECTORY_ENTRY_COM_DESCRIPTOR
               Net 程序独有的节点配置。
              

ILSpy 查看 Example_2_1_1.exe 的元数据。
                

c)、EntryPointToken
                这个标签的地址,就是我们程序 Program.Main 方法的入口点地址。
                IL 代码里面也是有标记的。
                 
             
        2.2、小知识
             Windbg 有一个伪寄存器命令 ? $exentry,可以直接告诉我们 exe 程序的入口点地址。

3、应用程序域
      3.1、简介
          对于 Windows 上的应用程序,大家都知道是按照【进程】进行隔离的。Net 将这种进程隔离缩小到了【应用程序域】层,即一个进程会有多个【应用程序域】,然后将应用程序部署在【应用程序域】上。
          在 CLR 上,应用程序域分为三类,分别是:SystemDomain、SharedDomain、Domain1。当然,这是说的在 Net Framework 的情况下,在 Net Core 框架下,只有两个应用程序域,风别是:SystemDomain、Domain1,去掉了 SharedDomain 这个应用程序域。

3.2、应用程序域
           a)、SystemDomain
系统及作用域,用于创建其他作用域。将 mscorlib.dll 加载到 SharedDomain 共享及应用程序域。
                记录字符串池中字符串常量。
                初始化特定异常(OutOfMemoryException、StackOverflowException)。

b)、SharedDomain
                加载 System 命名空间下的基本类型(String,Enum,ValueType)。

c)、Domain1                 用户的应用程序都是在这个域中运行。

三、调试测试
    这个章节里,很简单,一共分为两个部分,第一部分是要用到的测试代码的样例,第二部分,就是具体的测试操作过程,说明一下,假设大家对 Windbg 有些熟悉。

1、测试代码

1.1、代码样例1

复制代码
 1 namespace Example_2_1_1
 2 {
 3     internal class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             Console.WriteLine("Hello World");
 8             Console.ReadLine();
 9         }
10     }
11 }

2、调试过程
        2.1、验证 CLR 和 JIT 的 存在。
            验证代码:Example_2_1_1
            操作描述:编译 Example_2_1_1 项目,打开 Windbg,通过【launch executable】加载我们的程序集。当我们成功加载程序集,还必须通过【g】命令,或者【Go】按钮执行程序,这个时候,才能加载所有的东西。当我们运行完之后,就能看到运行界面,就可以看到和 CLR 和 JIT 有关的东西。红色字体表明加载了 CLR 和 JIT 两个组件。

复制代码
 1 0:000> g
 2 ModLoad: 74ec0000 74f39000   C:\Windows\SysWOW64\ADVAPI32.dll
 3 ModLoad: 771f0000 772af000   C:\Windows\SysWOW64\msvcrt.dll
 4 ModLoad: 757e0000 75855000   C:\Windows\SysWOW64\sechost.dll
 5 ModLoad: 753c0000 7547a000   C:\Windows\SysWOW64\RPCRT4.dll
 6 ModLoad: 711c0000 7124d000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
 7 ModLoad: 771a0000 771e5000   C:\Windows\SysWOW64\SHLWAPI.dll
 8 ModLoad: 757d0000 757df000   C:\Windows\SysWOW64\kernel.appcore.dll
 9 ModLoad: 74eb0000 74eb8000   C:\Windows\SysWOW64\VERSION.dll
10 ModLoad: 70a10000 711c0000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll(CLR组件,加载的起始地址:70a10000)
11 ModLoad: 75870000 75a04000   C:\Windows\SysWOW64\USER32.dll
12 ModLoad: 77070000 77088000   C:\Windows\SysWOW64\win32u.dll
13 ModLoad: 70940000 709eb000   C:\Windows\SysWOW64\ucrtbase_clr0400.dll
14 ModLoad: 75fb0000 75fd3000   C:\Windows\SysWOW64\GDI32.dll
15 ModLoad: 709f0000 70a04000   C:\Windows\SysWOW64\VCRUNTIME140_CLR0400.dll
16 ModLoad: 750c0000 7519b000   C:\Windows\SysWOW64\gdi32full.dll
17 ModLoad: 76390000 7640b000   C:\Windows\SysWOW64\msvcp_win.dll
18 ModLoad: 75550000 75670000   C:\Windows\SysWOW64\ucrtbase.dll
19 ModLoad: 74fa0000 74fc5000   C:\Windows\SysWOW64\IMM32.DLL
20 ModLoad: 75ff0000 76270000   C:\Windows\SysWOW64\combase.dll
21 (7fc.b18): Unknown exception - code 04242420 (first chance)
22 ModLoad: 6f530000 7093e000   C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\218db16dceaef380c6daf35c6a48f313\mscorlib.ni.dll
23 ModLoad: 76490000 76573000   C:\Windows\SysWOW64\ole32.dll
24 ModLoad: 75ff0000 76270000   C:\Windows\SysWOW64\combase.dll
25 ModLoad: 752a0000 752fc000   C:\Windows\SysWOW64\bcryptPrimitives.dll
26 ModLoad: 6f4a0000 6f52a000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll(这个就是JIT编译器组件,在进程中的起始地址:6f4a0000)
27 ModLoad: 751a0000 7523b000   C:\Windows\SysWOW64\OLEAUT32.dll

2.2、证明 Windows 加载器如何加载一个 Net 的程序集。

验证代码:Example_2_1_1

PPEE是绿色软件,不用安装,直接下载就可以使用。打开 PPEE 软件,打开 Example_2_1_1.exe,就可以看到 PE 文件。

当我们双加一个 Net 的Exe应用程序的时候,操作系统会做很多工作,比如:在内核态生成进行的地址空间,地址空间生成成功后,然后在生成一个Process 的进程,再给这个进程生成一个主线程,在内核态还要针对进程生成 EProcess 的数据结构,针对线程生成一个 ETHREAD 的数据结构。当所有的准备工作都完成后,要开始执行程序,从哪里开始执行程序呢?我们来证明一下,或者说熟悉一下,这个过程就和 PE 头文件有关了。

a、Windows 加载器会读取 PE 文件头里面的数据,来确定从哪里开始执行,第一步,我们通过 PPEE 查看 Example_2_1_1.exe PE 文件,在 PE 头里依次点击【NT Header】-->【Optional Header】,在窗体的右侧就可以看到,有一项【AddressOfEntryPoint】,它的值是:00002782,这个地址是相对地址,是相对程序进程起始地址来说的。

如图:

我们有了入口程序的相对起始地址,我们找一下应用程序的进程起始地址,二者相加,就是 Windows 加载器要执行的地址。想要查看 Example_2_1_1.exe 进程地址,需要借助 Windbg,红色部分就是 Example_2_1_1.exe 进程的起始地址。

代码如下:

复制代码
1 Executable search path is: 
2 ModLoad: 00ca0000 00ca8000   Example_2_1_1.exe
3 ModLoad: 770d0000 77272000   ntdll.dll
4 ModLoad: 71050000 710a2000   C:\Windows\SysWOW64\MSCOREE.DLL
5 ModLoad: 74cc0000 74db0000   C:\Windows\SysWOW64\KERNEL32.dll
6 ModLoad: 767a0000 769b3000   C:\Windows\SysWOW64\KERNELBASE.dll

二者相加就是WIndows 加载器的入口点地址,还不是我们的 Program.Main的地址,00ca0000(Example_2_1_1进程起始地址),00002782 是 PE 头告诉的入口点地址,我们通过 U 命令,可以查看汇编代码。通过代码我们可以看到,执行了 jmp 指令,跳转的地址是:402000h。

复制代码
 1 0:000> u 00ca0000+00002782
 2 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x2782(PE 头里的值)):
 3 00ca2782 ff2500204000    jmp(执行跳转指令)     dword ptr ds:[402000h]
 4 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x2788):
 5 00ca2788 0000            add     byte ptr [eax],al
 6 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x278a):
 7 00ca278a 0000            add     byte ptr [eax],al
 8 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x278c):
 9 00ca278c 0000            add     byte ptr [eax],al
10 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x278e):
11 00ca278e 0000            add     byte ptr [eax],al
12 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x2790):
13 00ca2790 0000            add     byte ptr [eax],al
14 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x2792):
15 00ca2792 0000            add     byte ptr [eax],al
16 Example_2_1_1!COM+_Entry_Point <PERF> (Example_2_1_1+0x2794):
17 00ca2794 0000            add     byte ptr [eax],al

接下来,我们看看 402000h 这个地址有什么东西。

再次执行 Windbg,重新加载 Example_2_1_1.exe,通过【g】命令继续运行,暂停时,【break】开始调试状态,必须切换到主线程,也就是 0号线程。

复制代码
 1 //g 继续运行
 2 0:000> g
 3 ModLoad: 76c30000 76ca9000   C:\Windows\SysWOW64\ADVAPI32.dll
 4 ModLoad: 765b0000 7666f000   C:\Windows\SysWOW64\msvcrt.dll
 5 ModLoad: 76e30000 76ea5000   C:\Windows\SysWOW64\sechost.dll
 6 ModLoad: 75c40000 75cfa000   C:\Windows\SysWOW64\RPCRT4.dll
 7 ModLoad: 70fc0000 7104d000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscoreei.dll
 8 ModLoad: 75d20000 75d65000   C:\Windows\SysWOW64\SHLWAPI.dll
 9 ModLoad: 75530000 7553f000   C:\Windows\SysWOW64\kernel.appcore.dll
10 ModLoad: 74cb0000 74cb8000   C:\Windows\SysWOW64\VERSION.dll
11 ModLoad: 70810000 70fc0000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
12 ModLoad: 76410000 765a4000   C:\Windows\SysWOW64\USER32.dll
13 ModLoad: 707f0000 70804000   C:\Windows\SysWOW64\VCRUNTIME140_CLR0400.dll
14 ModLoad: 76180000 76198000   C:\Windows\SysWOW64\win32u.dll
15 ModLoad: 70740000 707eb000   C:\Windows\SysWOW64\ucrtbase_clr0400.dll
16 ModLoad: 75b80000 75ba3000   C:\Windows\SysWOW64\GDI32.dll
17 ModLoad: 76670000 7674b000   C:\Windows\SysWOW64\gdi32full.dll
18 ModLoad: 76b30000 76bab000   C:\Windows\SysWOW64\msvcp_win.dll
19 ModLoad: 75550000 75670000   C:\Windows\SysWOW64\ucrtbase.dll
20 ModLoad: 754f0000 75515000   C:\Windows\SysWOW64\IMM32.DLL
21 Breakpoints 3 and 0 match
22 ModLoad: 75f00000 76180000   C:\Windows\SysWOW64\combase.dll
23 (3624.36d8): Unknown exception - code 04242420 (first chance)
24 ModLoad: 6f330000 7073e000   C:\Windows\assembly\NativeImages_v4.0.30319_32\mscorlib\218db16dceaef380c6daf35c6a48f313\mscorlib.ni.dll
25 ModLoad: 76cb0000 76d93000   C:\Windows\SysWOW64\ole32.dll
26 ModLoad: 75f00000 76180000   C:\Windows\SysWOW64\combase.dll
27 ModLoad: 76fa0000 76ffc000   C:\Windows\SysWOW64\bcryptPrimitives.dll
28 ModLoad: 6f2a0000 6f32a000   C:\Windows\Microsoft.NET\Framework\v4.0.30319\clrjit.dll
29 ModLoad: 76eb0000 76f4b000   C:\Windows\SysWOW64\OLEAUT32.dll
30 (3624.20f0): Break instruction exception - code 80000003 (first chance)
31 eax=0106b000 ebx=00000000 ecx=7717cee0 edx=7717cee0 esi=7717cee0 edi=7717cee0
32 eip=77143410 esp=05a2fa9c ebp=05a2fac8 iopl=0         nv up ei pl zr na pe nc
33 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
34 ntdll!DbgBreakPoint:
35 77143410 cc              int     3
36 
37 //切换到主线程
38 0:006> ~0s
39 eax=00000000 ebx=0000009c ecx=00000000 edx=00000000 esi=0138ee3c edi=00000000
40 eip=771410fc esp=0138ed24 ebp=0138ed84 iopl=0         nv up ei pl nz na pe nc
41 cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
42 ntdll!NtReadFile+0xc:
43 771410fc c22400          ret     24h

我们通过【k】命令,查看显示调用栈

复制代码
 1 0:000> k
 2  # ChildEBP RetAddr      
 3 00 0138ed84 768af25c     ntdll!NtReadFile+0xc
 4 01 0138ed84 6f7e9b71     KERNELBASE!ReadFile+0xec
 5 02 0138edf4 6ff1b275     mscorlib_ni+0x4b9b71
 6 03 0138ee20 6ff1b17b     mscorlib_ni!System.IO.__ConsoleStream.ReadFileNative+0x89 [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205] 
 7 04 0138ee4c 6f7ce6a3     mscorlib_ni!System.IO.__ConsoleStream.Read+0x9f [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134] 
 8 05 0138ee64 6f7ceb5b     mscorlib_ni!System.IO.StreamReader.ReadBuffer+0x33 [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595] 
 9 06 0138ee80 70063786     mscorlib_ni!System.IO.StreamReader.ReadLine+0xe3 [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748] 
10 07 0138ee90 6fec1845     mscorlib_ni!System.IO.TextReader.SyncTextReader.ReadLine+0x1a [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363] 
11 08 0138ee98 03250876     mscorlib_ni!System.Console.ReadLine+0x15 [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984] 
12 WARNING: Frame IP not in any known module. Following frames may be wrong.
13 09 0138eea8 7081f036     0x3250876
14 0a 0138eeb4 708222da     clr!CallDescrWorkerInternal+0x34
15 0b 0138ef08 7082859b     clr!CallDescrWorkerWithHandler+0x6b
16 0c 0138ef7c 709cb11b     clr!MethodDescCallSite::CallTargetWorker+0x16a
17 0d 0138f0a0 709cb7fa     clr!RunMain+0x1b3
18 0e 0138f30c 709cb727     clr!Assembly::ExecuteMainMethod+0xf7
19 0f 0138f7f0 709cb8a8     clr!SystemDomain::ExecuteMainMethod+0x5ef
20 10 0138f848 709cb9ce     clr!ExecuteEXE+0x4c
21 11 0138f888 709c7305     clr!_CorExeMainInternal+0xdc
22 12 0138f8c4 70fcfa84     clr!_CorExeMain+0x4d
23 13 0138f8fc 7105e81e     mscoreei!_CorExeMain+0xd6
24 14 0138f90c 71064338     MSCOREE!ShellShim__CorExeMain+0x9e
25 15 0138f924 74cdf989     MSCOREE!_CorExeMain_Exported+0x8
26 16 0138f924 77137084     KERNEL32!BaseThreadInitThunk+0x19
27 17 0138f980 77137054     ntdll!__RtlUserThreadStart+0x2f
28 18 0138f990 00000000     ntdll!_RtlUserThreadStart+0x1b

以上就是线程的调用栈,我们查找一下 _CorExeMain,这个方法,可以在 PPEE 软件里,在查看 Example_2_1_1.exe 的PE 头文件 DIRECTORY_ENTRY_IAT 里,效果如图:

接着上面的说,我们在【k】命令的结果中查找 _CorExeMain 方法。这里的执行结果是一部分,红色部分是重点,MSCOREE 就是 mscoree.dll

复制代码
 1 0a 0138eeb4 708222da     clr!CallDescrWorkerInternal+0x34
 2 0b 0138ef08 7082859b     clr!CallDescrWorkerWithHandler+0x6b
 3 0c 0138ef7c 709cb11b     clr!MethodDescCallSite::CallTargetWorker+0x16a
 4 0d 0138f0a0 709cb7fa     clr!RunMain+0x1b3(运行 Main 方法)
 5 0e 0138f30c 709cb727     clr!Assembly::ExecuteMainMethod+0xf7(加载必须的 dll 程序集)
 6 0f 0138f7f0 709cb8a8     clr!SystemDomain::ExecuteMainMethod+0x5ef(初始化系统程序域)
 7 10 0138f848 709cb9ce     clr!ExecuteEXE+0x4c(开始执行 exe)
 8 11 0138f888 709c7305     clr!_CorExeMainInternal+0xdc
 9 12 0138f8c4 70fcfa84     clr!_CorExeMain+0x4d(从这里开始执行入口地址的方法)
10 13 0138f8fc 7105e81e     mscoreei!_CorExeMain+0xd6(加载 CLR)
11 14 0138f90c 71064338     MSCOREE!ShellShim__CorExeMain+0x9e
12 15 0138f924 74cdf989     MSCOREE!_CorExeMain_Exported+0x8(这里就是在 PPEE 看到的入口地址 AddressOfEntryPoint:00002782)
13 16 0138f924 77137084     KERNEL32!BaseThreadInitThunk+0x19
14 17 0138f980 77137054     ntdll!__RtlUserThreadStart+0x2f
15 18 0138f990 00000000     ntdll!_RtlUserThreadStart+0x1b(ntdll是windows 32位的API)

第12行代码就是 mscoree.dll 执行 _CorExeMain 方法,初始化环境,10 行代码加载 CLR,CLR 从第9 行执行入口函数,知道最后进入我们的托管层,我们可以使用 !clrstack 命令查看托管栈。

复制代码
 1 0:000> !clrstack
 2 OS Thread Id: 0x36d8 (0)
 3 Child SP       IP Call Site
 4 0138eda4 771410fc [InlinedCallFrame: 0138eda4] 
 5 0138eda0 6f7e9b71 DomainNeutralILStubClass.IL_STUB_PInvoke(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
 6 0138eda4 6ff1b275 [InlinedCallFrame: 0138eda4] Microsoft.Win32.Win32Native.ReadFile(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte*, Int32, Int32 ByRef, IntPtr)
 7 0138ee08 6ff1b275 System.IO.__ConsoleStream.ReadFileNative(Microsoft.Win32.SafeHandles.SafeFileHandle, Byte[], Int32, Int32, Boolean, Boolean, Int32 ByRef) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 205]
 8 0138ee3c 6ff1b17b System.IO.__ConsoleStream.Read(Byte[], Int32, Int32) [f:\dd\ndp\clr\src\BCL\system\io\__consolestream.cs @ 134]
 9 0138ee5c 6f7ce6a3 System.IO.StreamReader.ReadBuffer() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 595]
10 0138ee6c 6f7ceb5b System.IO.StreamReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\streamreader.cs @ 748]
11 0138ee88 70063786 System.IO.TextReader+SyncTextReader.ReadLine() [f:\dd\ndp\clr\src\BCL\system\io\textreader.cs @ 363]
12 0138ee98 6fec1845 System.Console.ReadLine() [f:\dd\ndp\clr\src\BCL\system\console.cs @ 1984]
13 0138eea0 03250876 Example_2_1_1.Program.Main(System.String[]) [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_2_1_1\Program.cs @ 10]
14 0138f020 7081f036 [GCFrame: 0138f020] 

系统进入了托管栈,那它怎么知道要执行程序的 Main 方法呢?其实,在 PE 头文件里也有说明,在【DIRECTORY_ENTRY_COM_DESCRIPTOR】配置项里,我们点击该节点,在右侧显示详情,请注意【EntryPointToken】,值是:06000001,这个标记就是 Main方法。

效果如图:

我们为了证明 06000001就是 Main 方法,我们使用 ILSpy 或者 DnSpy 查看程序集的元数据,效果如图:

其实,我们在 Main 方法的 IL 代码里也有标记,红色部分就是,注意。

复制代码
 1 .method private hidebysig static 
 2     void Main (
 3         string[] args
 4     ) cil managed 
 5 {
 6     // Method begins at RVA 0x2050
 7     // Header size: 1
 8     // Code size: 19 (0x13)
 9     .maxstack 8
10     .entrypoint
11 
12     IL_0000: nop
13     IL_0001: ldstr "Hello World"
14     IL_0006: call void [mscorlib]System.Console::WriteLine(string)
15     IL_000b: nop
16     IL_000c: call string [mscorlib]System.Console::ReadLine()
17     IL_0011: pop
18     IL_0012: ret
19 } // end of method Program::Main

2.3、查看应用程序域

验证代码:Example_2_1_1

EECLASS 存放在 LowFrequencyHeap ,MethodTable 存放在 HighFrequencyHeap

复制代码
 1 0:000> !dumpdomain(执行的命令)
 2 --------------------------------------
 3 System Domain:      7115caf8(系统级程序域)
 4 LowFrequencyHeap:   7115ce1c(低频堆)
 5 HighFrequencyHeap:  7115ce68(高频堆)
 6 StubHeap:           7115ceb4(桩堆)
 7 Stage:              OPEN
 8 Name:               None
 9 --------------------------------------
10 Shared Domain:      7115c7a8(共享程序域)
11 LowFrequencyHeap:   7115ce1c(低频堆)
12 HighFrequencyHeap:  7115ce68(高频堆)
13 StubHeap:           7115ceb4(桩堆)
14 Stage:              OPEN
15 Name:               None
16 Assembly:           00b4f3d8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
17 ClassLoader:        00b4de08
18   Module Name
19 6f531000    C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
20 
21 --------------------------------------
22 Domain 1:           00b01518(应用程序域)
23 LowFrequencyHeap:   00b01984(低频堆)
24 HighFrequencyHeap:  00b019d0(高频堆)
25 StubHeap:           00b01a1c(桩堆)
26 Stage:              OPEN
27 SecurityDescriptor: 00b02a58
28 Name:               Example_2_1_1.exe
29 Assembly:           00b4f3d8 [C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll]
30 ClassLoader:        00b4de08
31 SecurityDescriptor: 00b4f340
32   Module Name
33 6f531000    C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
34 
35 Assembly:           00b5b4d8 [E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_2_1_1\bin\Debug\Example_2_1_1.exe]
36 ClassLoader:        00b5afa8
37 SecurityDescriptor: 00b5aea0
38   Module Name
39 00d44044    E:\Visual Studio 2022\Source\Projects\AdvancedDebug.NetFramework.Test\Example_2_1_1\bin\Debug\Example_2_1_1.exe

2.4、通过命令 ? $exentry 查看入口点。

复制代码
0:000> ?  $exentry
Evaluate expression: 5711746 = 00572782

三、总结
    今天的内容真不少,完全记录下来还是挺费劲的,俗话说,没有苦哪里来的甜呢,一天的辛苦还是值得的,自己的收获也不少。学习如逆水行舟,不进则退,但是也也有另外一句话,学的越多,好像懂得越少。不管如何,不忘初心,继续努力,老天不会辜负努力的人。

相关推荐
贩卖纯净水.2 小时前
白月光git
git·github
爱吃瓜的猹z6 小时前
git reset 几点疑问
git·源代码管理
悟空201613 小时前
001、Git开发流程规范
git
Li小李同学Li13 小时前
git学习【持续更新中。。。】
git·学习·elasticsearch
晨春计14 小时前
【git】
android·linux·git
念幽15 小时前
Git常用命令
git
神技圈子15 小时前
【git系列】git中的那些迷惑的术语以及概念详解
git
benben04416 小时前
Photoshop使用方法大全
git
ou.cs17 小时前
git 删除远程分支的几种写法
git
atlanteep17 小时前
Linux·权限与工具-git与gdb
linux·git