程序集的加载过程

来源:互联网 发布:广州数控编程代码大全 编辑:程序博客网 时间:2024/06/10 12:50
1.windows加载器是如何加载.net程序集的?

       首先使用dumpbin命令行工具来分析下要分析的程序集BinarySearchPractice.exe。输入如下命令行即可:

     dumpbin -all BinarySearchPractice>d:analysis.txt

      这样,dumpbin就会把BinarySearchPractice中相关的信息输出到d盘的analysis.txt文件了。现在开始分析。

2.第一个执行的指令,已经第一个执行的函数

请看BinarySearchPractice的Optional Header,如下:
OPTIONAL HEADER VALUES
             10B magic # (PE32)
            8.00 linker version      

           1000 size of code
            2000 size of initialized data
               0 size of uninitialized data
            28AE entry point (004028AE)
            2000 base of code
            4000 base of data
          400000 image base (00400000 to 00407FFF)
            2000 section alignment
            1000 file alignment
            4.00 operating system version
            0.00 image version

可以发现,入口点entry point在地址为0x28AE+0x00400000=0x004028AE处。查看地址0x004028AE处,发现这个一条jmp指令,如下:

 004028A0: 63 6F 72 65 65 2E 64 6C 6C 00 00 00 00 00 FF 25  coree.dll.....?%
 004028B0: 00 20 40 00   

0x004028AE处是FF 25 00 20 40 00.事实上,这是一条jmp指令,转换成汇编指令如下:

 jmp dword ptr [00402000]

也就是说跳到[00402000]指向的内存处执行,查看004020000地址,如下:

 00402000: 90 28 00 00 00 00 00 00 48 00 00 00 02 00 05 00

发现,0x40200000处的内容是00002890,现在跳到00002890处,看看到底是什么,如下:

  00402890: 00 00 5F 43 6F 72 45 78 65 4D 61 69 6E 00 6D 73  .._CorExeMain.ms
  004028A0: 63 6F 72 65 65 2E 64 6C 6C 00 00 00 00 00 FF 25  coree.dl
l.....?%
  04028B0: 00 20 40 00      
              

可以发现,00402890处是一个叫做_CorExeMain的函数,这个函数在mscoree.dll中。

现在可以发现,当windows 加载器加载我们的程序集时,执行的第一条指令是jmp dword ptr[.......],紧接着开始执行_CorExeMain函数。事实上,从windows xp开始,这条jmp指令已近废弃了,windows加载器能够识别.net程序集,并自动加载他们,因此那条jmp指令对于xp以后的系统都用不上。

3、看一下导入表

请看 BinarySearchPractice的导入表如下:

  Section contains the following imports:
 mscoree.dll
                402000 Import Address Table
                402888 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference
                0 _CorExeMain

    从这里可以清晰的看到,这个程序集使用了mscoree.dll中的一个叫做_CorExeMain函数,这个函数在虚拟地址空间中的0x00402000中。等等,这个0x00402000不就是刚才jmp指令要跳转的地址么!!

    事实上,这个mscoree.dll是托管程序集唯一依赖的一个外部非托管程序集。对于exe文件,他使用的是mscoree.dll中的_CorExeMain函数,对于dll文件,他使用的mscoree.dll的_CorExeDll函数。

   4._CorExeMain干什么的?

      这个函数复杂加载并初始化CLR,当CLR加载完了以后,所有的工作就都有CLR来执行了。CLR开始正式加载我们的.net程序集,然后执行他们。那么这个函数如何加载CLR呢?

    事实上,他会读取程序集的CLR头,读取要加载的CLR版本,然后去某一个指定的路径去寻找对应的CLR版本,如果找到了就加载,如果没有找到,就提示你得机器上没有暗转.net framword。

   那么clr在什么地方呢?他在这个目下下C:\Windows\Microsoft.NET\Framework\version\,其中version是CLR的版本号。

5.CLR执行的第一个函数:

   查看一下CLR头:

 clr Header:

              48 cb
            2.05 runtime version
            2130 [     6A0] RVA [size] of MetaData Directory
               1 flags
                   IL Only
         6000001 entry point token
               0 [       0] RVA [size] of Resources Directory
               0 [       0] RVA [size] of StrongNameSignature Directory
               0 [       0] RVA [size] of CodeManagerTable Directory
               0 [       0] RVA [size] of VTableFixups Directory
               0 [       0] RVA [size] of ExportAddressTableJumps Directory
               0 [       0] RVA [size] of ManagedNativeHeader Directory


CLR会查看CLR头entry point token,发现这是06000001,这个值是什么呢,这是一个method元数据标记。现在使用ILDAsm查看一下这个元数据标记到底是什么东西,如下:

Method #1 (06000001) [ENTRYPOINT]
    -------------------------------------------------------
        MethodName: Main (06000001)
        Flags     : [Private] [Static] [HideBySig] [ReuseSlot]  (00000091)
        RVA       : 0x00002050
        ImplFlags : [IL] [Managed]  (00000000)
        CallCnvntn: [DEFAULT]
        ReturnType: Void
        1 Arguments
            Argument #1:  SZArray String
        1 Parameters
            (1) ParamToken : (08000001) Name : args flags: [none] (00000000)


可以发现,这是一个叫做Main的方法,他有一个string[]的参数,返回类型为void。那么这个函数就是CLR执行的一个函数。如果感兴趣,可以查看一下这个函数的IL代码,其中的RVA字段就是Main的RVA。你只需要跳到0x2050+0x400000处,便可以发现他的实现。


总结:

  首先调用_CorExeMain函数。

  这个函数复杂加载并初始化CLR。

  CLR读取method元数据标记,然后执行入口点方法。