• 托管PE文件


    文/玄魂

     

     中间语言

    在.NET框架中,公共语言基础结构使用CLS来绑定不同的语言。通过要求不同的语言至少要实现CTS包含在CLS中的部分,公共语言基础结构允许不同的语言使用.NET框架。因此,在.NET框架中,所有的语言(C#、VB.NET、Effil.NET等)最后都被转换为了一种通用语言:微软中间语言(Microsoft Intermediate Language,MSIL,以下简称IL)。

    IL是一种介于高级语言和基于Intel的汇编语言的中间语言,是.NET平台的汇编语言。当用户编译一个.NET程序时,编译器将源代码翻译成一组可以有效地转换为本机代码且独立于CPU的指令。当执行这些指令时,实时编译器将它们转化为CPU特定的代码。由于CLR支持多种实时编译器,因此同一段IL代码可以被不同的编译器实时编译并运行在不同的结构上。

    IL包括用于加载、存储和初始化对象以及对对象调用方法的指令,还包括用于算术和逻辑运算、控制流、直接内存访问、异常处理和其他操作的指令。要使代码可运行,必须先将 IL 转换为特定于 CPU 的代码,这通常是通过实时(JIT) 编译器来完成的。由于CLR为它支持的每种计算机结构都提供了一种或多种JIT编译器,因此同一组IL可以在所支持的任何结构上JIT编译和运行。

    当编译器产生IL时,它也产生元数据。元数据描述代码中的类型,包括每种类型的定义、每种类型的成员的签名、代码引用的成员和运行库在执行时使用的其他数据。IL和元数据包含在一个可移植可执行(PE)文件中,下面重点介绍托管PE文件,以及元数据的相关知识。

    1.3.1       托管PE文件

    PE(Portable Execute,可移植执行体)是微软Windows操作系统上的程序文件,常见的如EXE、DLL、OCX、SYS、COM。图1-3展示了标准的PE/COFF文件头部格式。

     

    图1-3 标准的PE/COFF文件头部格式

    MS DOS头是DOS系统的遗传内容,表示一个应用程序可以在DOS环境下运行。MS DOS根(stub)是一段代码,如果Windows程序在DOS环境下运行,会给出“该程序不能在DOS环境下运行”(This program cannot be run in DOS mode)的提示。在偏移量0x3c处,MS DOS头指向了PE标识(PE Signature)的地址。

    PE标识表示该文件是一个PE文件。其值始终为00004550h,45h代表字符E,50h代表字符P。

    COFF头(COFF Header)提供了COFF或者可执行文件的最一般的信息。

    PE头(PE Header)提供了操作系统加载文件所需的信息。这对于PE文件是最重要的地方,其间包含了数据索引表和节。

    关于标准PE文件的详细内容请读者阅读相关资料,本节内容只关注托管PE文件的特殊信息。CLR对传统的PE文件进行了扩展,如图1-4所示是托管PE文件的格式。

     

    图1-4 托管PE文件的格式

    标准的Windows PE文件头和COFF(通用对象文件格式)头类似,分为PE32和PE32+两种。如果文件头采用PE32格式,则该文件可运行在32位或64位操作系统上。如果文件头采用PE32+格式,则该文件只能在64位的操作系统上运行。PE32 或者 PE32+ 头也包含文件类型信息:GUI、CUI或者DLL。如果包含本地CPU代码的模块,则PE32或者PE32+ 头将包含有关本地CPU代码的相关信息。

    CLR头包含使这个模块被托管的相关信息。这些信息包括CLR需要的版本信息、一些标识、入口方法的元数据信息、模块的元数据位置和大小信息、资源信息、强名称和一些其他信息。

    每一个托管模块都包含元数据表。元数据表有两种,一种是描述源代码中的类型描述和成员描述的元数据表,另一种是包含源代码引用的类型描述和成员描述的元数据表。

    IL代码是编译器编译产生的中间代码,程序运行时CLR负责将中间代码编译成本地代码执行。

    CLR头定义在.NET Framework的CorHdr.h中,代码如代码清单1-4所示。

    代码清单1-4 CLR 头定义

     1 typedef struct IMAGE_COR20_HEADER
     2 
     3 {
     4 
     5 ULONG cb;
     6 
     7 USHORT MajorRuntimeVersion;
     8 
     9 USHORT MinorRuntimeVersion;
    10 
    11 // Symbol table and startup information
    12 
    13 IMAGE_DATA_DIRECTORY MetaData;
    14 
    15 ULONG Flags;
    16 
    17 union {
    18 
    19 DWORD EntryPointToken;
    20 
    21 DWORD EntryPointRVA;
    22 
    23 };
    24 
    25 // Binding information
    26 
    27 IMAGE_DATA_DIRECTORY Resources;
    28 
    29 IMAGE_DATA_DIRECTORY StrongNameSignature;
    30 
    31 // Regular fixup and binding information
    32 
    33 IMAGE_DATA_DIRECTORY CodeManagerTable;
    34 
    35 IMAGE_DATA_DIRECTORY VTableFixups;
    36 
    37 IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
    38 
    39 IMAGE_DATA_DIRECTORY ManagedNativeHeader;
    40 
    41 } IMAGE_COR20_HEADER;

    关于CLR头中的各个字段的解释见表1-1,后文会对PE文件中的节信息做简要介绍,关于PE文件的详细信息参看书后附录中的参考书籍。

    表1-1  CLR头字段说明

    偏移(offset

    大小(size

    字段(field

    描述(description

    0

    4

    Cb

    头的长度(bytes)

    4

    2

    MajorRuntimeVersion

    CLR运行程序所必须的最小版本(Minimum Version)信息的主码(Major Number)

    6

    2

    MinorRuntimeVersion

    CLR运行程序所需要的版本信息中的次要编码(Minor Number)

    8

    8

    MetaData

    相对虚拟地址(RAV)和元数据的大小

    16

    4

    Flags

    二进制标志位组合,包含系统相关,程序调用等相关信息

    20

    4

    EntryPointToken/EntryPointRVA

    文件的入口点元数据标识符,对于DLL文件可以被设置为0

    24

    8

    Resources

    托管资源的大小和相对虚拟地址

    32

    8

    StrongNameSignature

    当前PE问件的hash数据的大小和相对偏移地址,被加载器用来绑定和版本验证

    40

    8

    CodeManagerTable

    Code Manager table的大小和相对偏移地址。目前为保留字段被设置为0

    48

    8

    VTableFixups

    一组V-Table的大小和相对虚拟地址信息

    56

    8

    ExportAddressTableJumps

    用于C++的输出跳转地址表的RVA和size,大多数情况为0

    64

    8

    ManagedNativeHeader

    为本地映像的保留字段,设置为0

           

    下面通过ILDasm查看HelloWorld.exe的文件头信息。单击菜单“view-headers”,如图1-5所示。

     

    图1-5 查看文件头信息

    头信息的主要代码如代码清单1-5所示。

    代码清单1-5 HelloWorld.exe 头信息

    ----- DOS Header:
    
    Magic:                      0x5a4d
    
    Bytes on last page:         0x0090
    
    ......(省略)
    
    File addr. of COFF header:  0x0080
    
    ----- COFF/PE Headers:
    
    Signature:                  0x00004550
    
    ----- COFF Header:
    
    Machine:                    0x014c
    
    Number of sections:         0x0003
    
    Time-date stamp:            0x4b1b1d3a
    
    Ptr to symbol table:        0x00000000
    
    Number of symbols:          0x00000000
    
    Size of optional header:    0x00e0
    
    Characteristics:            0x0102
    
    ----- PE Optional Header (32 bit):
    
    Magic:                          0x010b
    
    ......(省略)
    
    Directory:      
    
    ......(省略)
    
    Table:     
    
    0x00000000 [0x00000000] address [size] of Delay Load IAT:           
    
    0x00002008 [0x00000048] address [size] of CLR Header:               
    
    ......(节信息,略)
    
     
    
    Base Relocation Table
    
                  0x00002000 Page RVA
    
                  0x0000000c Block Size
    
                  0x00000002 Number of Entries
    
                  Entry 1: Type 0x3 Offset 0x000007a0
    
                  Entry 2: Type 0x0 Offset 0x00000000
    
     
    
    Import Address Table
    
         DLL : mscoree.dll
    
              ......(省略)
    
     
    
    Delay Load Import Address Table
    
    // No data.
    
     
    
    Entry point code:
    
    FF 25 00 20 40 00
    
     
    
     
    
    ----- CLR Header:
    
    Header size:                        0x00000048
    
    Major runtime version:              0x0002
    
    Minor runtime version:              0x0005
    
    ......(省略)        
    
     
    
    Metadata Header
    
        Storage Signature:
    
                 ......(省略)
    
        Storage Header:
    
                        0x00 Flags
    
                      0x0005 Number of Streams
    
        Stream 1:
    
                  0x0000006c Offset
    
                  0x000001e8 Size
    
                  '#~' Name
    
       ......(省略)
    
        Stream 5:
    
                  0x00000510 Offset
    
                  0x00000130 Size
    
                  '#Blob' Name
    
     
    
        Metadata Stream Header:
    
                  0x00000000 Reserved
    
                        0x02 Major
    
                        0x00 Minor
    
                        0x00 Heaps
    
                        0x01 Rid
    
          0x0000000900001547 MaskValid
    
          0x000016003325fa00 Sorted
    
    Code Manager Table:
    
      default
    
    Export Address Table Jumps:
    
    // No data.

    上面代码中涉及很多节信息,下面做简要论述。

    1.       Relocation(重定位)

    映像文件的.reloc节包括了Fixup表,它包含了映像文件中的所有定位项。.reloc节的RVA和大小都由PE头的基地址重定位(Base Relocation)表目录定义。Fixup表由定位块组成,每个块都包括了一个4KB页的定位。这些块都是4字节对齐的。

    每个定位都描述了映像文件中特定地址的位置,以及操作系统加载程序在将映像文件载入内存的时候,应该如何修改这个位置上的地址。

    每个定位块都开始于两个4字节无符号整数:页面的RVA,这个页面包含了需要定位的地址、块的大小。紧随其后的是页面的定位项每个项都是16位宽的,其中的4个最高权重位包括了所需要的重定位类型,剩下的12位包括了页面中重定位地址的偏移量。

    为了对地址进行重定位,操作系统加载程序会计算出首选的基地址(PE头的ImageBase字段)和实际加载映像文件的基地址之间的差异(delta)。接着根据重定位的类型,将这个delta应用到地址上。如果在首选位置加载映像文件,就不需要进行定位。

    说明 Windows XP或者更新的版本都是支持CLR的操作系统,既不需要CLR的启动Stub,也不需要IAT来调用CLR。因此,如果CLR头标志指出映像文件是纯IL(COMIMAGE_FLAGS_ ILONLY),那么操作系统就会完全地忽略.reloc节。

    2.       Text(文本)

    PE文件的.text节是只读节。在托管PE文件中,它包括了元数据表、IL代码、导入表、CLR头、CLR非托管启动Stub。在由IL汇编器生成的映像文件中,这个节还包括了托管资源、强签名的散列值、调试数据以及非托管导出Stub。所以.text节是托管PE文件对传统PE文件改变最多的地方。

    图1-6总结了由IL汇编器生成的映像文件的.text节的通用结构。

     

    图1-6 .text节的通用结构

    3.       Data(数据)

    由IL汇编器生成的映像文件的数据节(.sdata)是可读写的节,它包括了数据常量、V表、非托管导出表以及TLS的目录结构。声明为特定于线程的数据位于一个不同的节,也就是.tls节。

    4.       Data Constants(数据常量)

    数据常量代表了静态字段的映射,通常包括映射字段的初始化数据。

    字段映射是一种使用ANSI字符串、Blob或结构来初始化静态字段的方法。另一种初始化静态字段的方法(对于CLR来说更正式的方法)是通过在类的构造函数中显式地进行初始化。

    一方面,映射到数据节的字段就像类型控制和垃圾收集那样,是CLR控制机制所触及不到的;另一方面,它是完全开放的,可以不受限制地访问和修改。这将导致加载程序阻止特定的字段类型被映射。映射字段的类型不能包括对象引用、向量、数组或任何非公共的子结构。如果为静态字段初始化使用类的构造函数,就不会出现这样的问题。

    5.       V-Table(V表)

    在纯粹的托管代码模块中,V表用于将托管方法公开给非托管代码来调用。V表由一些项组成,每个项又由一个或多个槽组成。V表的这些项和槽都定义在V表定位中。每个定位指定了每个项中槽的数量和宽度(4字节或8字节)。V表的每个槽都包含各个方法的元数据标记,这些元数据标记在运行期间将会替换成方法本身的地址或者封送thunk,用于提供方法的非托管入口。因为这些定位是在运行期间执行的,所以托管PE文件的V表必须驻留于可读写的节中。IL汇编器将这个V表放在了.sdata节中,不像VTFixup表是驻留于.text节中的。

    非托管映像文件的V表完全在链接期间定义,并只需操作系统加载程序执行的基地址重定位。因为在执行期间不需要改变V表(例如把方法标记替换成托管映像中的地址),所以非托管映像文件可以把它们的V表放在只读节中。

    6.       Unmanaged Export Table(非托管导出表)

    在非托管映像文件中的非托管导出表占据一个单独的节——.edata。在IL汇编器生成的映像文件中,非托管导出表和它引用的V表都驻留于.sdata节中。

    7.       Thread Local Storage(线程局部存储)

    ILAsm和VC++允许用户定义属于TLS的数据常量,并将静态字段映射到这些数据常量上。TLS是一种特殊的存储类,类中的数据对象不是栈变量而是各个独立线程的局部变量。因此,每个线程都可以为这样的变量维护不同的值。

    TLS数据在TLS目录中描述,IL汇编器将其放置于.sdata节中。32位映像文件的TLS目录结构定义在Winnt.h中,如代码清单1-6所示。

    代码清单1-6 32位映像文件的TLS目录结构

    typedef struct _IMAGE_TLS_DIRECTORY32 {
    
    ULONG StartAddressOfRawData;
    
    ULONG EndAddressOfRawData;
    
    ULONG AddressOfIndex;
    
    ULONG AddressOfCallBacks;
    
    ULONG SizeOfZeroFill;
    
    ULONG Characteristics;
    
    } IMAGE_TLS_DIRECTORY32;

    64位映像(IMAGE_TLS_DIRECTORY64)的TLS目录结构是类似的,除了开头的4个字段是8字节无符号整数(ULONGLONG),而不是4字节无符号整数(ULONG)。

    TLS目录结构的RVA和大小存储在PE头的第10个数据目录(TLS)中。构成了TLS模板的TLS数据常量,驻留于映像文件的.tls节中。

    8.       Resources(资源)

    在托管PE文件中可以嵌入两种不同的资源:特定于平台的非托管资源和特定于CLR的托管资源。它们驻留于托管映像文件的不同节,并通过不同的API进行访问。

    (1)       Unmanaged Resources(非托管资源)

    非托管资源在PE文件的.rsrc节中。嵌入的非托管资源的起始RVA和大小都在PE头的资源数据目录中表示。

    非托管资源由类型、名称和语言进行索引,并根据这三个特征的顺序进行二进制排序。

    IL汇编器创建.rsrc节,并且会嵌入命令行选项指定的.res文件中的非托管资源。编译器只能为每个模块嵌入一个非托管资源文件。

    当IL反汇编器分析托管PE文件并找到.rsrc节的时候,它会从这个节中读取数据和结构,并流并释放出包括在PE文件中所有非托管资源的.res文件。

    (2)       Managed Resources(托管资源)

    CLR头的Resource字段包括了内嵌在PE文件中的托管资源的RVA和大小。它与PE头的Resource目录无关,后者指定了特定于平台的非托管资源的RVA和大小。

    在IL汇编器生成的PE文件中,非托管资源驻留于映像文件的.rsrc节中,而托管资源和元数据、IL代码等都位于.text节中。托管资源在.text节中连续地存放。元数据携带着ManifestResource记录,每一笔记录对应着一个托管资源,包括了托管资源的名称以及从CLR头的Resources字段中指定的起始RVA算起的资源开始处的偏移量。在这个偏移位置上,会使用4字节无符号整数指出资源的字节长度。紧跟其后的是资源本身。

    当IL反汇编器处理托管映像文件并找到嵌入的托管资源时,它会将每个资源各自写到根据资源名称命名的单独文件中。

    当IL汇编器创建PE文件时,它会根据资源名称读取在源代码中定义为嵌入资源的所有托管资源,将它们写到.text节中,并在每个资源的前面放置该资源的指定长度。

    -----------------------注:本文改编自《.net 安全揭秘》1.3节

     


    作者:玄魂
    出处:http://www.cnblogs.com/xuanhun/
    原文链接:http://www.cnblogs.com/xuanhun/ 更多内容,请访问我的个人站点 对编程,安全感兴趣的,加qq群:hacking-1群:303242737,hacking-2群:147098303,nw.js,electron交流群 313717550。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    关注我:关注玄魂的微信公众号

  • 相关阅读:
    OCP-1Z0-053-V12.02-597题
    OCP-1Z0-053-V12.02-599题
    OCP-1Z0-053-V12.02-609题
    OCP-1Z0-053-V12.02-604题
    OCP-1Z0-053-V12.02-607题
    OCP-1Z0-053-V12.02-588题
    OCP-1Z0-053-V12.02-592题
    OCP-1Z0-053-V12.02-587题
    OCP-1Z0-053-V12.02-582题
    OCP-1Z0-053-V12.02-583题
  • 原文地址:https://www.cnblogs.com/xuanhun/p/2515392.html
Copyright © 2020-2023  润新知