(接上文)现在我们知道了我们创建的是什么类型的PE文件了, 但是在Program.exe中真正是什么?
一个托管PE文件有如下四个主要部分: PE32 (+) header, CLR header, metadata和IL。
PE32(+) header是Windows需要的标准信息, CLR header是用于那些需要CLR才能运行的模块(托管模块)的一小块信息, 这个header包括CLR的主版本和次版本号: 一些标志, 一个MethodDef符号用于表示模块的入口函数(如果这个模块是一个CUI或者GUI可执行程序), 和一个可选的强名字数字签名. 最后, header还包括metadata的大小和偏移量.
Metadata是一块二进制的数据, 包含了几个表格. 有3类表格: 定义表格, 引用表格, 和manifest表格. 下表描述了一些常用的定义表格:
Metadata定义表格的名称 |
描述 |
ModuleDef |
总是包含一个条目用来标识模块, 包括模块的文件名和扩展名(没有路径)和模块的版本ID(编译器创建的GUID). 它允许文件被重新命名, 而保持原名字的一个记录. 然而, 强烈不推荐重命名一个文件, 重命名会导致CLR在运行时不能定位程序集, 所以不要这么做. |
TypeDef |
对定义在模块中的每一个类型都包含一个条目, 每个条目包括类型的名称, 基类型, 标志(public, private, 等)和它所包含的函数在MethodDef表格中的索引, 它所包含的字段在FieldDef表格中的索引, 它所包含的属性在PropertyDef表格中的索引, 和它所包含的事件在EventDef表格中的索引. |
MethodDef |
对定义在模块中的每个方法都包含一个条目, 每个条目包括方法的名字, 标志(private, public, virtual, abstract, static, final, 等), 签名, 在模块中的偏移量(IL代码可在此处找到这个函数). 每个条目也指向ParamDef表格的一个条目, 后者记录着函数的参数信息. |
FieldDef |
对定义在模块中的每个字段都包含一个条目, 每个条目包括标志(private, public, 等), 类型和名字. |
ParamDef |
对定义在模块中的每个参数都包含一个条目, 每个条目包含标志(in, out, retval, 等), 类型, 和名字. |
PropertyDef |
对定义在模块中的每个属性都包含一个条目, 每个条目包含标志, 类型, 和名字. |
EventDef |
对定义在模块中的每个事件都包含一个条目, 每个条目包含标志和名字. |
当编译器编译你的源代码时, 你的代码中定义的任何东西都会在上表中描述的一个表格中创建一个条目. 编译器也会检测到的你的代码中引用的类型, 字段, 函数, 属性, 事件, 所以也会对它们创建Metadata表格条目. 创建的metadata包括一组引用表格, 其对每个引用保存一个条目. 下表给出了一些常见的引用metadata表格
Metadata引用表格的名称 |
描述 |
ModuleRef |
对这个模块引用的类型, 都需要引用对应的实现模块PE, 都对应着这个表格中的一个条目, 每个条目包含模块的文件名和扩展名(没有路径). 这个表格被用于绑定在不同模块中实现的类型. |
TypeRef |
对这个模块引用的每个类型都包含一个条目, 每个条目包括类型的名字和一个能找到它的地方的引用. 如果类型是在另一个类型内实现的, 那么这个引用会指明一个TypeRef条目, 如果类型是在相同的模块中实现的, 那么引用会指明一个ModuleDef条目, 如果类型是在另一个模块中实现的, 那么引用会指明一个ModuleRef条目, 如果类型是在不同的程序集中实线的, 那么引用会指明一个AssemblyRef条目. |
MemberRef |
对这个模块引用的每个成员(字段和函数, 属性和事件方法)都包含一个条目, 每个条目包括成员的名字, 签名, 和指向TypeRef条目的指针(定义这个成员的那个type). |
要查看一个程序集的元数据,可以使用ildasm.exe,选择打开一个程序集,运行该程序后,按ctrl+m即可调出相应程序集的元数据信息。