频道栏目
读书频道 > web开发 > .NET > .NET安全揭秘
1.3.1 托管PE文件
2012-10-24 14:00:15     我来说两句
收藏   我要投稿

本文所属图书 > .NET安全揭秘

全书共分为五个部分。第一部分:.NET安全基础,透彻讲解了.NET体系结构、程序集与反射、应用程序域和CLR寄宿等核心技术,这部分内容是.NET架构的核心,同时也是理解.NET底层安全机制的基础;第二部分:.NET平台安...  立即去当当网订购

PE(Portable Execute,可移植执行体)是微软Windows操作系统上的程序文件,常见的如EXE、DLL、OCX、SYS、COM。图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文件的格式。

标准的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-4CLR 头定义

typedef struct IMAGE_COR20_HEADER
{
ULONG cb;
USHORT MajorRuntimeVersion;
USHORT MinorRuntimeVersion;
// Symbol table and startup information
IMAGE_DATA_DIRECTORY MetaData;
ULONG Flags;
union {
DWORD EntryPointToken;
DWORD EntryPointRVA;
};
// Binding information
IMAGE_DATA_DIRECTORY Resources;
IMAGE_DATA_DIRECTORY StrongNameSignature;
// Regular fixup and binding information
IMAGE_DATA_DIRECTORY CodeManagerTable;
IMAGE_DATA_DIRECTORY VTableFixups;
IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
IMAGE_DATA_DIRECTORY ManagedNativeHeader;
} IMAGE_COR20_HEADER;

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

表1-1CLR头字段说明

偏移(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-5HelloWorld.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表由定位块组成,每个块都包括了一个4 KB页的定位。这些块都是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节的通用结构。


 

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-632位映像文件的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节中,并在每个资源的前面放置该资源的指定长度。

您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:1.3 中间语言
下一篇:1.3.2 元数据
相关文章
图文推荐
排行
热门
最新书评
特别推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站