科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道浅析.NETFramework对PE文件格式的扩展

浅析.NETFramework对PE文件格式的扩展

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

   Microsoft .NET Framework出来小阵子了,我也自从其Beta 1以来,第一次接触。本文将从.NET生成的一个小PE文件着手,旨在理解.NET Framework对PE文件格式的扩展。

作者:中国IT实验室 来源:中国IT实验室 2007年9月30日

关键字: PE .NET Framework 编程

  • 评论
  • 分享微博
  • 分享邮件
  

Microsoft .NET Framework出来小阵子了,我也自从其Beta 1以来,第一次接触。本文将从.NET生成的一个小PE文件着手,旨在理解.NET FrameworkPE文件格式的扩展。这种扩展目的是让Windows系统识别Common Language Runtime(CLR)

 

PE文件是Windows系列操作系统的可执行文件格式。本文假设您对这一文件格式有相当的理解,文中未涉及PE在之前的win16及之后的win64上的讨论。在CLR出现之前,PE文件格式仅简单的由PE HeaderNative Image(相对于以下介绍的CLR HeaderCLR Data部分)组成。Native Image由各个section组成,如.text.data.rdate等等,需要指出的是PE文件的这些section名命名规则并不要求一定要以句点开头,事实上这只是Microsoft的对于代码段或数据段的默认说法,像Borland等其他编译器则相应分别命名为CODEDATA等等。Native Image含有已编译的相应处理器的机器代码。

 

CLR出现后PE文件扩展出了另外一部分,即CLR HeaderCLR Data组成的供.NET Framework运行的支撑部分。CLR Header.NET Framework SDKCorHdr.h中的IMAGE_COR20_HEADER结构定义。从CorHdr.h或是IMAGE_COR20_HEADER的命名中Cor的全称Com+ Runtime即可隐隐约约的看到.NET Framework的发展过程,其与COM+的渊源关系了。事实上IMAGE_COR20_HEADER在平台SDKwinnt.h中也有定义,我查阅的了随Windows XP DDK Build 2505发行的winnt.hMicrosoft在给出这个定义时的注释为COM+ 2.0 header structure,而在.NET Framework SDK中即修改为CLR 2.0 header structure了。CLR Data则包含.NET metadata, IL method bodies等等。metadataIL method.NET中很关键的术语。ILMicrosoft Intermediate Language的缩写。她是为了.NET跨平台、跨语言的特性而引入的,有其自身的指令集。.NET SDK中的opcode.def列出的其支持的指令集。粗粗看来这些指令集与IntelX86指令集十分的相像,也是由Prefix指定的的双字节进行编码。

 

下面的我将通过底下列出的这一段C# Console代码来简述C#编译器生成的PE文件的执行流程及PE文件的on disk结构。代只是简单的输出Hi,如下所示:

 

   public class App {

     static public void Main(System.String[] args) {

      System.Console.WriteLine("Hi");

     }

   }

 

我们简单的使用csc /out:app.exe app.cs对其编译。生成的PE文件,与.NET出现前传统的编译器生成的PE文件一致,也含有IMAGE_DOS_HEADER,我们知道这部分的作用即是早期的DOS在遇到PE文件格式时,能判定这个可执行文件不能执行于DOS下而存在的。IMAGE_DOS_HEADER与将要谈及的一些结构在winnt.h中均有详细定义。Windows OS Loader根据IMAGE_DOS_HEADER中的e_lfanew成员定位紧挨着的IMAGE_NT_HEADERS。其定义如下:

 

   typedef struct _IMAGE_NT_HEADERS {

      DWORD Signature;

      IMAGE_FILE_HEADER FileHeader;

      IMAGE_OPTIONAL_HEADER32 OptionalHeader;

   } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

 

我们知道IMAGE_OPTIONAL_HEADER32的成员AddressOfEntryPoint PE可执行文件的入口,在.NET中其仍为执行入口,这应该是很好理解的。对于一个COMIMAGE_FLAGS_ILONLY(IMAGE_COR20_HEADER 的成员Flags 指定)Image,如我们生成的App.exe,这个入口也即间接定位至App.exeImport表的_CorExeMain函数。_CorExeMain对应EXE文件,由mscoree.dll导出。mscoree.dll位于%WINNT%\system32下,是Microsoft .NET Runtime Execution Engine,应该指出的她是一个Native Image,负责调用IMAGE_COR20_HEADER中的 EntryPointToken 指定的.NET Token。这才是真正IL语言的入口。

 

Native Image部分的各个Section的定位,已经有很多文档介绍,而且winnt.h中都有详细的定义。我只简单的阐述一下:

 

.text.datasection定位是由IMAGE_OPTIONAL_HEADER32中的DataDirectory成员指定。DataDirectory是一个IMAGE_DATA_DIRECTORY数组,个数为MAGE_NUMBEROF_DIRECTORY_ENTRIES(当前为16)个。各个DataDirectory功能分别由IMAGE_DIRECTORY_ENTRY_***指定,如EXPORTIMPORT等等。因为IMAGE_DATA_DIRECTORYVirtualAddress(RVA)Size组成,所以我们即可以很容易的找到这些Section的位置。与这些Section一样,CLR Header的定位也是DataDirectory指定,其为IMAGE_DIRECTORY_ENTRY_COMHEADER(值为14.NET Framework SDK V1 CorHdr.h中称谓,在DDK 2505winnt.h中为IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR)。我们生成的App.exe有如下的格式:

 

         .

         .

         .

   AddressOfEntryPoint: 0x000022CE (+0x10)

         .

         .

         .

   DataDirectory[0] - IMAGE_DIRECTORY_ENTRY_EXPORT

     VirtualAddress: 0x00000000 (+0x60)

      Size: 0x00000000 (+0x64)

   DataDirectory[1] - IMAGE_DIRECTORY_ENTRY_IMPORT

     VirtualAddress: 0x0000227C (+0x68)

     Size: 0x0000004F (+0x6C)

         .

         .

         .

   DataDirectory[14] - IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR

     VirtualAddress: 0x00002008 (+0xD0)

     Size: 0x00000048 (+0xD4)

         .

         .

         .

OK,从DataDirectory[14]我们即可以很容易的定位CLR HeaderCLR Header可以被合并到其它任何为只读属性的Section中。前面已经提及到CLR HeaderIMAGE_COR20_HEADER结构定义。

 

   // CLR 2.0 header structure.

   typedef struct IMAGE_COR20_HEADER

   {

      // Header versioning

      ULONG cb;

      USHORT MajorRuntimeVersion;

      USHORT MinorRuntimeVersion;

 

      // Symbol table and startup information

      IMAGE_DATA_DIRECTORY MetaData;

      ULONG Flags;

      ULONG EntryPointToken;

      // 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;

 

      // Precompiled image info (internal use only - set to zero)

      IMAGE_DATA_DIRECTORY ManagedNativeHeader;

 

   } IMAGE_COR20_HEADER;

这个结构的FlagsEntryPointToken上面已经提及。从这么多的IMAGE_DATA_DIRECTORY上看,这个定义很像IMAGE_OPTIONAL_HEADER32,后者可以理解成PE文件头的精华,其用于定位.textSection,由Windows OS Loader执行。而前者用于定位.NET CLR Data,如MetaDataResourcesStrongNameSignature等等。不同的是IMAGE_COR20_HEADER是由mscoree.dll中的_CorExeMain(对应于EXE文件)负责调用(MSIL语言需经过JIT编译成机器码才可执行)

 

虽然EnrtyPointToken与上面的AddressOfEntryPoint均是执行入口,但却有非常大的区别。AddressOfEntryPoint是一RVA,直接指向执行地址(相对于Image Base),其只能指向一本地机器代码用于装载NET Runtime(mscoree.dll中的_CorExeMain,对于DLL文件其可以置为0)。而EntryPointToken只是一个.NET TOKENTOKEN.NET Type的唯一识别,是一个DWORD值。其最高的8bit指明何种TOKEN。其由CorHdr.h中的CorTokenType enum定义。如mdtMethodDef0x06000000mdtEvent0x14000000等等,而余下的24bit则为此类TOKEN的唯一识别。EnrtyPointToken只能是一METHOD,而不能是EVENT等等。如App.ExeEnrtyPointTokeno0x06000001,其对应于Main Method。您可以使用ildasm.exe(.NET Framework SDK提供)进行验证。

 

App.exeCLR Header如下(只列出了部分非空字段)

 

    Size: 0x00000048

    MajorRuntimeVersion: 0x0002

    MinorRuntimeVersion: 0x0000

    MetaData

    VirtualAddress: 0x0000207C

    Size: 0x00000200

    Flags: 0x00000001

    COMIMAGE_FLAGS_ILONLY

    EntryPointToken: 0x06000001

 

.NET MetaDataMetaData成员指定。MicrosoftCorHdr.h中给出了ILMETHODon disk组织结构(IMAGE_COR_ILMETHOD)。随.NET Framework SDK也提供了一个例子metainfo用于分析Metadata。随QuickStart例子的Class BrowserASP.NET范例也是.NET Framework很好的学习材料。Metainfo使用常规的COM方法,而Class Browser使用.NET FrameworkSystem.Reflection Namespace。关于.NETSOAPWeb ServicesWeb FormsXML等等QuickStart真不愧为QuickStart.NET看来是下阵子学习的方向啊。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件