可执行文件的格式是反映一个系统程序运行机制的重要方面,Win32下可执行文件是读者已经非常熟悉的PE格式,
在.NET系统中,运行机制的改变带来了可执行文件格式的扩展。一方面,.NET建立在Win32/64的基础上,兼容性的要求决定了.NET的可执行文
件必须是在PE的基础上进行扩展;另一方面,Win32中PE文件存储的是汇编代码,而.NET中存储的是MSIL与元数据,后者无论是在逻辑结构还是物
理结构上,都与传统PE格式有很大区别。本章将详细讨论元数据的意义及其在可执行文件中的存储,读者需要对传统PE格式有所了解,因为本章会跳过这些基础
内容而直接进入.NET的扩展部分。
如果用普通的PE文件结构工具查看.NET可执行文件,不会发现它有什么特别之处,因为所有的头结构、节名称都和Win32下的相同。但细心的读者会发
现,.NET
PE文件的第15项数据目录COM大多数情况下的RVA为0x2008,大小为0x48,而该地址正指向了.text节。没错,.NET对PE的扩展主要
体现在了.text节的构造上:在Win32下,.text节保存的是汇编;而.NET中保存的是MSIL、元数据、以及各种特殊的.NET结构。本节首
先带领读者认识大变样后的.text节,随后介绍.NET PE中最重要的结构:CLR头。
图3.1为一般情况下.NET PE文件的.text节结构,通常包含八部分内容,比较重要的有以下几项:Common Language
Runtime头(CLR头),这是整个.NET可执行文件综合信息的存放处,其作用类似于Win32下的PE头;MSIL代码和可选的异常处理表,这是
加密软件重点关注的数据,也是解密时最希望得到的数据;强名称及其hash数据,当一个文件被签署了强名称后,相应的强名称数据保存于此;最后则是元数
据。
图3.1 .NET PE文件中的.text节结构
前面叙述了.text节中存储了哪些内容,接着将介绍它们具体的结构及含义。先介绍最重要的结构CLR头,它的定义在SDK安装目录里include目录下的CorHdr.h文件中,结构名叫IMAGE_COR20_HEADER,精简后的C++代码如下:
// COM+ 2.0 header structure.
typedef struct IMAGE_COR20_HEADER
{
DWORD cb;
WORD MajorRuntimeVersion;
WORD MinorRuntimeVersion;
IMAGE_DATA_DIRECTORY MetaData;
DWORD Flags;
union {
DWORD EntryPointToken;
DWORD EntryPointRVA;
};
IMAGE_DATA_DIRECTORY Resources;
IMAGE_DATA_DIRECTORY StrongNameSignature;
IMAGE_DATA_DIRECTORY CodeManagerTable;
IMAGE_DATA_DIRECTORY VTableFixups;
IMAGE_DATA_DIRECTORY ExportAddressTableJumps;
IMAGE_DATA_DIRECTORY ManagedNativeHeader;
} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;
表3-1详细解释了该结构中各项的意义。整个结构的大小为0x48,正是倒数第2项数据目录中指出的大小。
表3-1 CLR头的结构与说明
偏移 |
大小 |
名 称 |
说 明 |
0 |
4 |
Cb |
CLR头的大小,以byte为单位 |
4 |
2 |
MajorRuntimeVersion |
能运行该程序的最小.NET版本的主版本号 |
6 |
2 |
MinorRuntimeVersion |
能运行该程序的.NET版本的副版本号 |
8 |
8 |
MetaData |
元数据的RVA和Size |
16 |
4 |
Flags |
属性字段,可以在IL中以.corflags进行显式设置,也可以在编译时用/FLAGS选项进行设置,其中命令行设置的优先级较高 |
20 |
4 |
EntryPointToken |
入口方法的元数据ID(也就是token),在EXE文件必须有,而DLL文件中此项可以为0(.NET 2.0中,此项还可以是本地入口代码的RVA值) |
24 |
8 |
Resources |
托管资源的RVA和Size |
32 |
8 |
StrongNameSignature |
强名称数据的RVA和Size(强名称的意义在后面介绍) |
40 |
8 |
CodeManagerTable |
CodeManagerTable的RVA与Size,此项暂未使用,为0 |
48 |
8 |
VTableFixups |
v-table项的RVA和Size,主要供使用v-table的C++语言进行重定位 |
56 |
8 |
ExportAddressTableJumps |
用于C++的输出跳转地址表的RVA和Size,大多数情况为0 |
64 |
8 |
ManagedNativeHeader |
仅在由ngen生成本地模块中该项不为0,其余情况均为0 |
其中Flags项定义了该exe文件的最基本性质,包含以下设置:
COMIMAGE_FLAGS_ILONLY =0x00000001,//此程序由纯IL代码组成
COMIMAGE_FLAGS_32BITREQUIRED =0x00000002,//此程序仅在32位系统上运行
COMIMAGE_FLAGS_IL_LIBRARY =0x00000004,//此程序仅作为IL代码库(很少用)
COMIMAGE_FLAGS_STRONGNAMESIGNED =0x00000008,//此程序有强名称(重要)
COMIMAGE_FLAGS_NATIVE_ENTRYPOINT =0x00000008,//此程序入口方法为非托管
COMIMAGE_FLAGS_TRACKDEBUGDATA =0x00010000,//loader和JIT需要追踪调试信息
如果读者是初次接触上述内容,建议使用UltraEdit等编辑工具打开某个.NET
PE文件,在十六进制编码的基础上对照表中各项进行学习。通常在进行软件分析时,该头结构中的大多数信息是不变的,若有特殊需要,可利用专门的文件结构查
看工具进行浏览,如传统的Win32文件结构查看工具如PEiD、PEInfo、LordPE等,以及专为.NET编写的查看工具,如
Researcher .NET、Spices.NET的元数据结构浏览组件、CFF
Explorer等,有兴趣的读者还可以自己编写类似工具。图3.2便是用LordPE载入mscorlib.dll后显示的CLR头的数
据,mscorlib.dll是.NET平台的基础文件之一,位于(系统盘)/WINDOWS/Microsoft.NET/
Framework/v2.0.50727目录下。
图3.2 LordPE显示mscorlib.dll的CLR头结构
CLR头中最重要的项是MetaData,它是IMAGE_DATA_DIRECTORY格式,指出了该文件中元数据存放的位置和大小。而这,就是3.2节将详细阐述的内容。