一直都想写篇文章来说说那些尘封在PE/Coff文件格式下的那些事,还有Metadata和EEClass是如何表现了一个静态的PE格式文件在内存中的映射结构。
   在这篇文章里,我不去介绍windows下PE文件的具体格式,也不去介绍一个托管或者是非托管PE文件的加载运行方式,更加不去介绍一个PE文件里面的各个头部以及整体结构的各个部分的含义。

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

而是侧重于介绍,基于托管环境下,DotNet对基本的PE/CoFF文件格式做了那些扩充,CLR头部介绍,以及元数据和IL代码详析解析。主要侧重从静态文件的角度,来剖析DotNet下最基本的模块的结构,以及这样的结构如何适应一个托管的环境。

拟把PE文件格式里里外外从上到下一点一点的完全解剖一遍,当然,不可能做到很全面,不然,如果想知道PE文件的各个方面的具体的细节,可以参阅文章底部推荐的那个白皮书文档,这份文档相当详尽介绍了PE文件格式的点点滴滴。

首先,还是从一段C#代码开始:

class Program

{

public const int conField=122*1119;

public readonly int roField;

private int _property;

public int Property

{

get {return _property; }

set{_property = value; }

}

static void <?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />Main(string[] args)

{

(new Program()).Method();

}

public void Method()

{

System.Console.ReadLine();

}        
    }

之所以定义这么多类型和字段,主要是为了在解说托管PE文件格式的时候,元数据表中相关的表都会出现相关记录。

编译之后,得到一个叫做TestConsoleApp.exe的托管PE文件。在继续下面的叙述之间,首先先概括的说一下Metadata。不说IL语言是因为,在我以前的博文中,已经有相关的介绍。

一个托管PE文件,粗略的讲,由4个部分组成。PE32(+)的头部,CLR头部,Metadata以及IL。

首先,我们来说说CLR的头部。这个东西,是托管的PE文件所特有的东西。

我们打开DotNet Framework里面的include文件夹里面一个叫做CorHdr.h的文件,找到一个叫做IMAGE_COR20_HEADER这个数据结构的定义。这个数据结构,定义的就是CLR的header里面内容。下面对其的定义:

// COM+ 2.0 header structure.

typedef struct IMAGE_COR20_HEADER

{

// Header versioning

DWORD                   cb;

//CLR的主版本号

WORD                    MajorRuntimeVersion;

//CLR的副版本号

WORD                    MinorRuntimeVersion;

// Symbol table and startup information

//标识元数据在这个PE文件里面起始位置。

IMAGE_DATA_DIRECTORY    MetaData;

//标识这个runtime的Flags

DWORD                   Flags;           
            // DDBLD - Added next section to replace following lin
            // DDBLD - Still verifying, since not in NT SDK
            // DWORD                   EntryPointToken;

// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint.

// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint.
            //EnterPoint Token,这个定义的是这个image的MethodDef的入口点。

union {

DWORD               EntryPointToken;

DWORD               EntryPointRVA;

};
           // DDBLD - End of Added Area

// Binding information

//标识CLI资源的目录

IMAGE_DATA_DIRECTORY    Resources;

//强命名的签名文件。标识对这个PE文件计算得到的一个Hash文件的地址。这个是在CLI的loader在加载一个PE文件的时候,验证版本和加载的时候需要使用的。可以为空。

IMAGE_DATA_DIRECTORY    StrongNameSignature;

// Regular fixup and binding information

//代码管理表的地址

IMAGE_DATA_DIRECTORY    CodeManagerTable;

//这个module里面的一个包含地址的数组,数组的每一项,都包含了对一个founction的指针。

IMAGE_DATA_DIRECTORY    VTableFixups;

//这个也是包含的一个数组,数组里面都是方法需要jump的地址。

IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;

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

//这个地址,保存的是这个Module对应的在本机上面的Jit过后了的本地代码的目录。

IMAGE_DATA_DIRECTORY    ManagedNativeHeadesr;

} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;

接下来,我们就直接打开上面编译好的那个托管模块的CLR头部,看看里面有些什么:

----- CLR Header:

Header size:                       0x00000048

Major runtime version:              0x0002

Minor runtime version:              0x0005

0x00002094 [0x00000660] address [size] of Metadata Directory:

Flags:                            0x00000001

Entry point token:                  0x06000003

0x00000000 [0x00000000] address [size] of Resources Directory:

0x00000000 [0x00000000] address [size] of Strong Name Signature:

0x00000000 [0x00000000] address [size] of CodeManager Table:

0x00000000 [0x00000000] address [size] of VTableFixups Directory:

0x00000000 [0x00000000] address [size] of Export Address Table:

0x00000000 [0x00000000] address [size] of Precompile Header:

从上面的这个托管模块的头部可以看到,这个头部里面包含的内容,和这个头部的结构体所定义的东西,是完全一致的。

几个需要说明的,一个是Major runtime version,以及Minor runtime version标识的是不同的runtime的版本。在上面的头部中,主要是一个文件的偏移的offset,就是Entry point token的地址。

这里要特别提出来一点,这里的Entry point token表示的是入口点,是MethodDef的入口点。

而不是整个托管PE文件的入口点。整个PE文件的入口点,在这里,用PEID打开可以看到,是

Addr. of entry point:           0x000027be

这个EnterPoint是在一个32位的PE Optional Header里面定义的。这个入口点,才是整个应用程序的入口点。

这个入口点里面,我们使用IDA之类的逆向工程工具可以看到,托管PE模块在这个地址上面的代码:

004027BE: FF 25 00 20 40 00              jmp dword ptr ds:[402000] ; _CorExeMain

在这里,我们看到了熟悉的CorExeMain这个入口函数 ^_^ 关于这个函数,我就不多说了,在前面的博文里面有详析的分析。参见那篇探索托管模块加载过程的文章。

接下来,我们介绍下元数据表,以及一些对这个PE文件的统计信息,首先查看这个托管PE文件的统计信息,使用的还是ildasm工具,我的最爱:

File size            : 16384

PE header size         : 4096 (496 used)    (25.00%)

PE additional info      : 1075              ( 6.56%)

Num.of PE sections   : 3

CLR header size        : 72                 ( 0.44%)

CLR meta-data size      : 1632               ( 9.96%)

CLR additional info      : 0                  ( 0.00%)

CLR method headers    : 16                 ( 0.10%)

Managed code         : 49                ( 0.30%)

Data                 : 8192              (50.00%)

Unaccounted          : 1252              ( 7.64%)

Num.of PE sections   : 3

.text    - 4096

.rsrc    - 4096

.reloc   - 4096

CLR meta-data size  : 1632

Module        -    1 (10 bytes)

TypeDef       -    2 (28 bytes)      0 interfaces, 0 explicit layout

TypeRef       -   18 (108 bytes)

MethodDef     -    5 (70 bytes)      0 abstract, 0 native, 5 bodies

FieldDef      -    3 (18 bytes)      0 constant

MemberRef     -   17 (102 bytes)

ParamDef      -    2 (12 bytes)

Constant      -    1 (6 bytes)

CustomAttribute-   13 (78 bytes)

StandAloneSig -    1 (2 bytes)

PropertyMap   -    1 (4 bytes)

Property      -    1 (6 bytes)

MethodSemantic-    2 (12 bytes)

Assembly      -    1 (22 bytes)

AssemblyRef   -    1 (20 bytes)

Strings       -   680 bytes

Blobs         -   236 bytes

UserStrings   -     8 bytes

Guids         -    16 bytes

Uncategorized -   194 bytes

CLR method headers : 16

Num.of method bodies  - 5

Num.of fat headers    - 1

Num.of tiny headers   - 4

Managed code : 49

Ave method size - 9

可以看到,在上面的统计信息中,显示了一个托管的PE模块的各个部分的组成。从各个部分的统计的大小里面,PE Header和CLR的Metadata占据了相当大的比例,而IL代码,仅仅占据了整个托管模块大小的0.3%。只有49个字节。

顺便提一下,如果Unaccounted显示的是负数,是不能相信的,那是以前的版本存在的一个bug。

就写到这里吧,如果觉得看了上面的东西还是不知所云或者觉得不完整,那我推荐一本MS的白皮技术文档:Visual Studio, Microsoft Portable Executable and Common Object File Format Specification

可以在msdn上面下载到,它完整的讲述了PE文件格式的各个部分的细节。

接下来的一篇博文,就说说元数据以及元数据表的内存结构,逻辑结构和在SSCLI中的设计和实现。

最后做个广告:   
   欢迎园子里面的朋友加入SSCLI团队,这里,我们致力于对.Net底层核心技术及其实现的研究。如果你想真正的了解.Net最核心的实现,我们热忱的欢迎你的加入:
   圈子地址:http://sscli.cnblogs.com
   加入地址:http://www.cnblogs.com/lbq1221119/archive/2008/03/10/1097627.html
   
   圈子刚刚建立,希望园子里的朋友多多支持,:)

CLR探索系列:托管PE/COFF文件格式侧窥相关推荐

  1. CLR探索系列:Windbg+SOS动态调试分析托管代码

    http://blog.csdn.net/garyye/article/details/4788070   在使用VS进行托管应用程序的调试的时候,有的时候总感觉有些力不从心.譬如查看一个托管堆或者计 ...

  2. PE文件和COFF文件格式分析--概述

    刚工作的时候,我听说某某大牛在做病毒分析时,只是用notepad打开病毒文件,就能大致猜到病毒的工作原理.当时我是佩服的很啊,同时我也在心中埋下了一个种子:我也得有这天.随着后来的工作进行,一些任务的 ...

  3. PE文件和COFF文件格式分析——导出表的应用——一种摘掉Inline钩子(Unhook)的方法

    在日常应用中,某些程序往往会被第三方程序下钩子(hook).如果被下钩子的进程是我们的进程,并且第三方钩子严重影响了我们的逻辑和流程,我们就需要把这些钩子摘掉(Unhook).本件讲述一种在32位系统 ...

  4. PE文件和COFF文件格式分析——导出表的应用——通过导出表隐性加载DLL

    通过导出表隐性加载DLL?导出表?加载DLL?还隐性?是的.如果觉得不可思议,可以先看<PE文件和COFF文件格式分析--导出表>中关于"导出地址表"的详细介绍.(转载 ...

  5. PE文件和COFF文件格式分析——导出表的应用——一种插件模型

    可能在很多人想想中,只有DLL才有导出表,而Exe不应该有导出表.而在<PE文件和COFF文件格式分析--导出表>中,我却避开了这个话题.我就是想在本文中讨论下载Exe中存在导出表的场景. ...

  6. PE文件和COFF文件格式分析——导出表

    在之前的<PE可选文件头>相关博文中我们介绍了可选文件头中很多重要的属性,而其中一个非常重要的属性是(转载请指明来源于breaksoftware的CSDN博客) IMAGE_DATA_DI ...

  7. PE文件和COFF文件格式分析——RVA和RA相互计算

    之前几节一直是理论性质的东西非常多.本文将会讲到利用之前的知识得出一个一个非常有用的一个应用.(转载请指明来源于breaksoftware的csdn博客) 首先我们说下磁盘上A.exe文件和正在内存中 ...

  8. PE文件和COFF文件格式分析——节信息

    在<PE文件和COFF文件格式分析--签名.COFF文件头和可选文件头3>中,我们看到一些区块的信息都有偏移指向.而我们本文讨论的节信息是没有任何偏移指向的,所以它是紧跟在可选文件头后面的 ...

  9. C 设计语言编译生成的是中间语言IL,一、源代码-面向CLR的编译器-托管模块-(元数据IL代码)...

    本文脉络图如下: 1.CLR(Common Language Runtime)公共语言运行时简介 (1).公共语言运行时是一种可由多种编程语言一起使用的"运行时". (2).CLR ...

最新文章

  1. php透明颜色的代码,PHP 透明水印生成代码参考
  2. CTFshow 信息收集 web12
  3. CentOS下启动oracle数据库(转)
  4. 关于用户画像产品构建和应用的几点经验
  5. 计算机配置里面没有网络选项,主编教您win10网络设置中没有wlan选项怎么办
  6. python requests_小白学 Python 爬虫(18):Requests 进阶操作
  7. ITK:Sobel边缘检测图像滤镜
  8. jstack调试_增压的jstack:如何以100mph的速度调试服务器
  9. 简单聊一下makefile中的 =, :=, ?=和+=
  10. strlen函数_四种好用的PHP自定义加密函数(可逆/不可逆)
  11. 大话集群和负载均衡,太强了!
  12. Linux 命令(119)—— diff 命令
  13. 【全家福】多项式的各种板子
  14. 玩转二叉链表 (20 分)
  15. SWAT模型在水文水资源、面源污染模拟中的实践技术
  16. OpenCV的二值化处理函数threshold()详解
  17. 解决scrapy下载小说乱序
  18. 数字化转型之道:谈谈企业如何落地数据治理
  19. 1_requests请求
  20. 如何实现1分钟写一个API接口

热门文章

  1. U3D-页游-检测机制-webplayer-调试方法
  2. [转载]java中try 与catch的使用
  3. GPS时间服务器在电力监控系统中的应用方案
  4. Linux 命令(76)—— kill 命令
  5. 常见解压缩软件与其zip格式
  6. JAVA实现图的邻接表以及DFS
  7. win10下添加ssh服务
  8. React 小案例 路由跳转
  9. SEO关键词优化:如何理解被百度快速索引?
  10. 怎么打开/查看MySQL的SQL记录