托管文件模块组成
PE表头: 文件类型:(GUI,CUI,DLL),时间标记(文件创建时间)
.text部分:包含JMP _CorExeMain指令
.idata部分,保护MSCorEE.dll引用
CLR表头: 包含托管模块信息:CLR版本号,Main方法的MethodDef元数据标记,托管模块的元数据,资源,强命名,标记信息的位置和尺寸
元数据: 包括元数据表,有两种,一种描述源代码中定义的类型和成员,一种描述源代码中应用的类型和成员
中间语言:编译器编译代码时产生的指令。
清单(manifest)
元数据构成:
元数据是一块二进制数据,包含一些表
定义表:
1. ModuleDef: 包括托管模块的条目,条目主要包括模块的文件名,扩展名等
2. TypeDef:托管代码定义的每一个类型都在TypeDef中一个对应条目,包括类型的名称,基类型,类似public访问标记,以及一些指针(指针指向MethodDef表中该类型的方法,FieldDef中该类型的字段以及PropertyDef和EventDef表中属于该类型的成员)
3. MethodDef:托管模块中定义的每个方法在MethodDef中都对应一个条目,包括方法名,访问标记,方法签名,已经该方法的IL代码在模块中的偏移量,还包括一个指向ParamDef的指针
4. FieldDef:
5. ParamDef:
6. PropertyDef:
7. EventDef:
引用表:
1. AssemblyRef:托管模块中每一个引用在AssemblyRef中都有一个对应条目,记录程序集名称,版本,语言文化以及一个公有密钥标记
2. ModuleRef:用来记录实现在同一程序集中的其它不同模块的一些类型
3. TypeRef:
4. MemberRef:
清单表:
1. AssemblyDef:
2. FileDef:
3. ManifestResourceDef:
4. ExportedTypesDef:
程序集执行:
编译期:
1. 编译器编译程序时,再PE文件的.text部分嵌入指令 JMP _CorExeMain
2. _CorExeMain函数存在MSCorEE.dll中,所以MSCorEE.dll将在PE文件的.idata部分被引用
执行期:
1. Windows加载该PE文集
2. 发现其.idata部分存在MSCorEE.dll引用,于是加载MSCorEE.dll,获取_CorExeMain函数地址,用此地址修正JMP指令。
3. 进程开始执行修正后的JMP指令, 该指令跳转倒MSCorEE.dll中的_CorExeMain函数,此函数初始化CLR, 然后此函数查看CLR表头的Main函数位置。
4. 找到Main函数后,PE文件的IL被JIT编译成CPU指令。
5. CLR跳转到编译后的指令,程序开始运行。
托管方法的调用:
1. 方法执行之前,CLR会检查方法中引用到的所有类型。
2. CLR为方法分配一个内部数据结构,用于管理所有引用类型。该方法引用到的所有类都会被分配,该类中的每个方法都有一个对应的条目,每个条目都将被初始化为一个没有记录的函数(JITCompiler函数)
3. 当调用到该条目的时候,JITCompiler被调用,其搜索元数据,找到IL位置其将验证IL并编译为CPU指令(即所谓的及时编译JIT)
4. JITCompiler用CPU指令的位置替换该调用方法的地址
5. JITCompiler跳转到该内存代码上开始执行
6.如果第二次调用将直接使用该方法的地址