淘先锋技术网

首页 1 2 3 4 5 6 7

2015-11-2 09:22:02 Am Monday
嗨,周一。

一个新的工作周的开始。

在Windows操作系统下,可执行文件的存储格式是PE(Portable Executable)格式的;在Linux下,可执行文件的存储格式是WLF格式的。它们都是COFF格式文件的变种。

在Windows下,目标文件(.obj)、静态库(.lib)使用COFF格式存储;可执行文件(.exe),动态链接库文件(.dll)使用PE格式存储。静态库是一些目标文件的集合。

在 “winnt.h”头文件里定义了COFF格式文件和PE格式文件的数据结构。这些定义是一系列的结构体,枚举和#define宏定义。在ImageHlp.dll里定义了编辑读取PE文件内容的Win32API。

在64位Windows操作系统下,PE格式文件被做了少部分修改。没有新的字段定义被加入,并且去除了一些字段的定义,同时将字段的宽度从32位扩充到64位。64位windows操作系统下的PE格式文件被命名为:PE32+。

COFF文件是什么样子的?
可以看看它的总体结构:
这里写图片描述

从文件内容上来看,COFF文件由二进制数据组成。这些二进制数据从文件的零位置开始,依次存储,直到文件末尾。从数据结构的角度来看,这些二进制数据又分别属于不同的结构体或者结构体数组。这些结构体被定义在“winnt.h”头文件中。

在COFF文件中,这些结构体或结构体数组分别表示不同的含义,记录着COFF文件中的不同内容。结构体分成:文件头、可选头、段表、段数据、重定位表、行号表、符号表、符号表和字符串表。这些结构体数据之间存在关联关系。比如:文件头信息中存储了符号表的开始位置,以及段表中数组元素的个数;在段表中存储了各个段的位置,重定位表的位置,行号表的位置;重定位表中的项会关联到符号表中的某个符号;而符号表中某个符号的名称可能会存储在字符串表中。

使用dumpbin.exe工具可以把目标文件的内容导出,它在“C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin”目录里面。
具体命令如下:

"C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\bin\dumpbin.exe" /all c_demo.exe > c_demo.exe.txt

在上面的命令中,将exe文件”c_demo.exe”的所有内容导出到文本文件”c_demo.exe.txt”。命令选项“/all”表示导出所有内容,命令选项“>”表示将导出的内容存储到文件中。

文件头以一个结构体的形式存储在COFF文件的开始位置,占20个字节的大小。每一个COFF格式的二进制文件都必须包含一个文件头,它用来保存COFF文件的基本信息,如:文件标识,各个表的位置等。

使用dumpbin工具导出“c_demo.exe.txt”目标文件的内容后,文件头部分的信息内容如下:

Dump of file c_demo.exe

PE signature found

File Type: EXECUTABLE IMAGE

FILE HEADER VALUES
             C machine (x86)
                number of sections
        C776 time date stamp Mon Nov  :: 
                file pointer to symbol table
                number of symbols
              E0 size of optional header
             E characteristics
                   Executable
                   Line numbers stripped
                   Symbols stripped
                    bit word machine

OPTIONAL HEADER VALUES
             B magic # (PE32)
             linker version
           F000 size of code
             size of initialized data
                size of uninitialized data
             entry point ()
             base of code
             base of data
           image base ( to FFF)
             section alignment
             file alignment

在“winnt.h”头文件中,文件头被定义为IMAGE_FILE_HEADER类型,具体的定义形式如下:

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;
    WORD    NumberOfSections;
    DWORD   TimeDateStamp;
    DWORD   PointerToSymbolTable;
    DWORD   NumberOfSymbols;
    WORD    SizeOfOptionalHeader;
    WORD    Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

#define IMAGE_SIZEOF_FILE_HEADER             20

可选头,该数据结构为可选数据,在目标文件中不存在此数据结构。只有当COFF文件作为可执行文件存在的时候,该数据结构才有意义。

段表是各个段的目录,用于检索各个段的信息。它以结构体数组的形式存储在可选头或者文件头的后面。在段表中,每一项的大小是36个字节,数组元素的个数记录在文件头的“NumberOfSections”字段中。

段的划分是基于各组数据的共同属性,而不是逻辑概念。每段是一块拥有共同属性的数据,比如代码/数据、读/写等。如果COFF文件中的数据/代码拥有相同属性,它们就能被归入同一段中。

在段表中记录了各个段在段数据区域中的位置(相对文件首位置的绝对偏移),以及各段重定位信息在重定位表中的位置。

在COFF格式的目标文件中,每一个函数形成一个.text段,因此会有多个名为.text的段。在使用工具dumpbin导出“c_demo.exe”目标文件的内容后,除了列出.text段的同时,也将与该段相对应的重定位段一起列出。

在“winnt.h”头文件中,文件头被定义为IMAGE_SECTION_HEADER类型。

重定位表
在编译阶段,将某些源文件编译成目标文件的时候,在目标文件中,某些被调用函数或者数据的位置是无法确定的。这时候,编译器将这些被调用的函数或者数据的地址设定为一个默认的假值。在链接阶段,当能够确定这些被调用函数或数据的地址的时候,再用真实的地址来替换这些假值。我们将这个过程叫做重定位。

使用工具dumpbin将目标文件“c_demo.exe”的内容输出为汇编格式的文件后,可以观察到这些假值的设定情况,以及需要重定位的位置。命令格式如下:

"C:\Program Files (x86)\Microsoft VisualStudio 12.0\VC\bin\dumpbin.exe" /disasm c_demo.exe > c_demo.exe.asm.txt

行号表

行号表描述了二进制代码与源代码行号之间的关系,调试阶段使用。在“WinNT.h”头文件中,文件头被定义为IMAGE_RELOCATION类型,

符号表
在编译阶段的词法分析过程中,编译器扫描整个C++源代码,将源代码中的函数名称,变量名称收集起来,然后写入符号表中。在符号表中主要包含如下内容:函数名称,变量名称,段的名称,以及一些常量信息,这些名称被统称为符号。

符号表中的信息被用于静态链接阶段,用来进行被引用的函数或变量的地址重定位。每一个目标文件中都会包含一个符号表。在该符号表中的符号,要么是在该目标文件中定义的函数名称或变量名称;要么是被该目标文件引用的,定义于其他目标文件中的函数名称或变量名称。在静态链接阶段,多个目标文件进行链接的时候,存在于这些目标文件中的符号表会被合并到一起,形成一个全局符号表。在C++源代码中出现的所有符号都应该能在全局符号表中被查找到。

将符号表中的符号进行分类,具体的分类情况如下:

定义在本目标文件中的全局符号,该符号可能会被其他目标文件引用;
在本目标文件中引用的全局符号,该符号定义在其他目标文件中,该符号被称为外部符号;
段的名称,由编译器加入到符号表中。该符号的值就是段的起始地址;
局部符号。在编译单元内部可见,链接的时候忽略。
在执行链接的时候,只关注前两种类型的符号。

如果符号的名称小于8个字节,那么将该符号的名称直接存储在符号表中;如果符号的名称大于8个字节,那么将符号的名称存储在字符串表中,原来符号表中存储符号名称的地方存储了一个地址偏移量,该地址偏移量指向了字符串表中符号名称的位置。

根据符号存储类型以及符号在段中位置的不同,符号的值有不同的解释。

字符串表

字符串表用来保存长度大于8个字节的符号名称。字符串表的前4个字节表示字符串的长度,后面的紧跟字符串的内容,它以字节为单位,以’\0’作为字符串的结束符。这里的字符串长度不仅仅是字符串自身的长度(字符串内容+’\0’),还包括前面4个字节的该数据自身的长度。

重定位表和符号表之间通过符号表的索引进行关联;在文件头中保存了可选头的大小和段表所包含项目的数量,通过计算可以确定段表的起始位置和结束位置。段表起始位置=文件头大小+可选头大小;其他关系通过相对文件首位置的偏移表示。

Lib文件的结构
静态链接库就是一组目标文件的集合,当执行静态链接的时候,被选定的目标文件的内容就会被合并到相关的Pe文件中去。静态链接库的总体结构如下图所示:

这里写图片描述

静态链接库以签名开始,签名的数据内容为“(!\n”,长8个字节。紧跟在签名后面的是三个特别成员,分别是第一链接器节,第二链接器节,以及长名称节。在这三个特别成员之后,直到文件结束,存储的都是目标文件节的内容。

第一链接器节,第二链接器节,长名称节,以及目标文件节的数据结构都是由头数据+节数据这样的数据结构组成的。

第一链接器节。在静态链接库中必须存在该节,它包含了静态链接库中所有的符号名以及这些符号在静态链接库文件中的偏移;

第二链接器节。在静态链接库中该节可选,它包含了与第一链接器节相同的内容,但是它的内容是有序的,通过它查找符号要比在第一链接器节中查找的快;

长名称节。在静态链接库中该节可选,它是一个字符串表,用于存储名称大于16个字节的目标文件的名称。在目标文件节中,如果目标文件的名称小于16个字节,那么这个名称会被存储在头文件的名称域;如果这个名称大于16个字节,那么这个名称就会被存储到这里。而在头文件的名称域存储的则是该字符串在长名称节的偏移。
目标文件节。该节是静态链接库的主要内容,节的数量不定,它存储了若干各目标文件的内容,每一节都是头信息+目标文件的结构。目标文件的结构与前面文件结构描述的一致。

PE文件的结构
总体结构图
这里写图片描述

从文件内容上来看,PE文件由二进制数据组成。这些二进制数据从文件的零位置开始,依次存储,直到文件末尾。从数据结构的角度来看,这些二进制数据又分别属于不同的结构体或者结构体数组。这些结构体被定义在“winnt.h”头文件中。

在PE文件中,这些结构体或结构体数组分别表示不同的含义,记录着PE文件中的不同内容。从文件的顶端开始,依次存储了DOS头,PE头,段表,各段详细数据等信息。在进行信息字段定位的时候,PE文件采用两种方式:1利用指针。比如:在Dos头中存储一个指向PE头的指针;2利用数据结构的大小。在PE的头部信息中,一些数据结构的大小是固定的。在数据存储的时候,各个数据结构紧凑存放,中间没有空隙。在这种情况下,以一个数据结构的字段为基点,通过计算数据结构占用空间的大小,就可以定位另外一个数据结构的位置。

DOS头
所有 PE文件都必须以DOS MZ header开始,它是一个IMAGE_DOS_HEADER的结构。有了它,一旦程序在DOS下执行,DOS就能识别出这是有效的执行体,然后运行紧随MZ Header之后的DOS Stub。
在“winnt.h”头文件中,DOS MZ 头被定义为IMAGE_DOS_HEADER类型

在DOS头中,第一个域“e_magic”被称为魔术数字,它用于表示一个MS-DOS兼容的文件类型。所有MS-DOS兼容的可执行文件都将这个值设为0x5A4D,表示ASCII字符MZ。MS-DOS头部之所以有的时候被称为MZ头部,就是这个缘故。

对于MS-DOS操作系统来说,许多其他的域都是有用的。但是对于 Windows NT来说,只有最后一个域e_lfnew是有用的,该域是一个指针,占用4个字节,用于指明PE头在文件中的位置。

DOS Stub

DOS Stub实际上是个有效的EXE,在不支持PE文件格式的操作系统中,它将简单显示一个错误提示,类似于字符串“This program requires Windows”,或者程序员可根据自己的意图实现完整的DOS代码。大多数情况下DOS Stub由汇编器/编译器自动生成。

PE头

PE头紧跟在DOS MS头以及实模式程序残余之后,在winnt.h头文件中,PE头被定义为IMAGE_NT_HEADER类型.

在该结构体数据中,除了包含PE头标识外,又嵌套了两个结构体数据,分别是PE文件头的信息,以及PE可选头的信息。

PE头标识

在一个有效的PE文件中,PE头标识字段的值是0x00004550,用ASCII表示就是“PE00”。 #define IMAGE_NT_SIGNATURE定义了这个值。
PE中的文件头与COFF中的文件头定义一致。

可选头

在PE文件头之后,是PE可选头。该头224字节大小,它包含了许多重要的信息,例如:初始堆栈大小,程序的入口地址,首选加载基地址,操作系统版本,段对齐等。该头并非可选,而是必须要有的头。

在可选头中,包含了三类主要信息,分别是:标准域信息,WinNT附加信息,以及数据目录信息。

谓标准域就是指和UNIX可执行文件的COFF格式所公共的部分,虽然标准域中保留了COFF文件中定义的名称,但是WindowsNT仍然将它用作了不同的目的。

在操作系统的加载器加载PE文件的时候,WinNT附件域的信息为加载器提供了支持。

在可执行文件中有许多数据结构需要被快速定位,数据目录提供了这种支持。数据目录是一个指针列表,在该列表中保存了一系列的指针值,这些指针指向了其他的数据表。如:导入表,导出表,资源表,重定位表等。数据目录以指针的形式提供了一种信息查找的方式。

使用工具dumpbin可以将PE文件的内容导出,在该内容中包含了描述可选头的摘要信息。

在winnt.h头文件中,可选头被定义为IMAGE_OPTIONAL_HEADER类型,具体的定义内容描述如下:

//数据目录中数据元素的个数

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16

//可选头的定义

typedef struct _IMAGE_OPTIONAL_HEADER

{

    //标准域

    WORD    Magic;  //魔法数字

    BYTE    MajorLinkerVersion;//链接器的最大版本号

    BYTE    MinorLinkerVersion;//链接器的最小版本号

    DWORD   SizeOfCode;//可执行代码长度

    DWORD   SizeOfInitializedData;//初始化数据的长度(data段)

    DWORD   SizeOfUninitializedData;//未初始化数据的长度(bss段)

    DWORD   AddressOfEntryPoint;//代码的入口地址,程序从此处开始执行

    DWORD   BaseOfCode;//可执行代码的起始位置

    DWORD   BaseOfData;//初始化数据的起始位置

    //NT附件域

    DWORD   ImageBase;//载入程序首选的相对虚拟地址

    DWORD   SectionAlignment;//段加载到内存以后的对齐方式

    DWORD   FileAlignment;//段在文件中的对齐方式

    WORD    MajorOperatingSystemVersion;//操作系统最大版本号

    WORD    MinorOperatingSystemVersion;//操作系统最小版本号

    WORD    MajorImageVersion;//程序最大版本号

    WORD    MinorImageVersion;//程序最小版本号

    WORD    MajorSubsystemVersion;//子程序最大版本号

    WORD    MinorSubsystemVersion;//子程序最小版本号

    DWORD   Win32VersionValue;//这个值一直为零

    DWORD   SizeOfImage;//程序加载到内存以后,占用内存的大小。

    DWORD   SizeOfHeaders;//文件头部总大小

    DWORD   CheckSum;//校验和

    WORD    Subsystem;//一个标明可执行文件所期望的子系统的枚举值

    WORD    DllCharacteristics;//dll状态

    DWORD   SizeOfStackReserve;//保留栈大小

    DWORD   SizeOfStackCommit;//启动后实际申请栈数

    DWORD   SizeOfHeapReserve;//保留堆的大小

    DWORD   SizeOfHeapCommit;//启动后实际申请堆数

    DWORD   LoaderFlags;

DWORD   NumberOfRvaAndSizes;

//数据目录的定义

    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

在IMAGE_OPTIONAL_HEADER32可选头文件结构里BYTE类型字段MajorLinkerVersion创建可执行文件的链接器的主版本号。对于Microsoft的链接器生成的PE文件,这个版本号的Visual Studio的版本号相一致。
BYTE类型字段MinorLinkerVersion表示创建可执行文件的链接器的次版本号。

导入表

当一个可执行程序或者动态链接库调用另外一个动态链接库中的变量或者函数的时候,必须将这些被调用函数或者变量导入。这些被导入的变量或函数是必须是被调用动态链接库导出表中的一个子集。也就是说,只有当这些变量或者函数被导出以后,才能被其他可执行程序或者动态链接库导入。

在PE文件中,我们将这些变量和函数统称为符号,这些被导入的符号的信息被存储在导入表中。在PE文件中,导入表位于.idata段中,该段可以单独存在。

对于每一个可执行程序或者动态链接库,只要它调用了其他动态链接库中的符号,那么就会有一个或多个IMAGE_IMPORT_DESCRIPTOR类型的数组与之对应。该数组元素的个数与该可执行程序或者动态链接库所依赖的动态链接库的数量有关。

在IMAGE_IMPORT_DESCRIPTOR类型的数据结构中,存储的是与导入表相关的信息。该数据结构与其他数据结构发生关联,与该数据结构发生关联的数据实体包括:数据目录,字符串表,导入地址表,导入名称表。它们之间的关系如下图所示:
这里写图片描述

在可选头中包含了数据目录,在数据目录的第二项中,存储了指向导入表的位置的指针,以及该数据结构的大小。在数据目录的第十二项中,存储了指向第一个IAT的位置的指针,以及所有IAT数组的大小。

导入表是一个数组,该数据元素的类型是IMAGE_IMPORT_DESCRIPTOR。在该数组中,每一个数组元素都会对应一个被导入的DLL。在该数组的末尾,存储了一个所有的域为零的IMAGE_IMPORT_DESCRIPTOR类型的数组元素,表示该导入表结束。因此,在一个可执行程序或者动态链接库的导入表中,至少会包含两个数据元素。一个对应被导入符号的信息,一个所有域为零。

在每个导入表的数组的元素中,拥有两个地址字段,它们分别指向了导入地址表(IAT)和导入名称表(INT)。在导入地址表和导入名称表中,存储了被导入符号的名称或地址,它们是一个拥有多个数据元素的数组。因为导入表的数组元素可能是多个(当前可执行程序或动态链接库依赖多个其他的动态链接库),所以在一个动态链接库的导入信息中也会存在多个IAT数组以及INT数组。当PE文件被加载到内存以后,这些IAT数组会被存储在一块连续地内存区域中,它们首尾相连,中间没有空隙。在数据目录的第十二项中,存储了第一个IAT的位置,以及所有IAT数组的大小。导入表的结构布局如下图所示:

这里写图片描述
导入地址表和导入名称表的数据结构是一致的,它们均为IMAGE_THUNK_DATA类型,并以一个所有域均为零的数组元素结尾。导入地址表用于动态链接,而导入名称表用于符号地址绑定。在PE文件中,导入地址表和导入名称表中的数据内容是一样的,它们都存储着被导入符号的名称或者序号;在程序加载阶段,导入名称表的数据内容不会发生变化,而导入地址表中的被导入符号的序号或者名称将会被替换为该符号的真实虚拟内存地址。导入名称表的数据内容永远不会发生变化。
对于每一个动态链接库,都会有一个IMAGE_EXEPORT_DIRECTORY类型的数据结构与之对应。该数据结构保存了与导出表有关的信息,并与其他数据结构发生关联。与该数据结构有关联的数据实体包括:数据目录,字符串表,符号地址表,符号名称表,名称序号对应关系表。这些实体之间的关系如下图所示:
这里写图片描述

可选头中包含了数据目录,数据目录的第一项指向了导出表。导出表是一个结构体类型,在该结构体的字段中保存了与导出表相关的信息,如:DLL名称字段指向了一个字符串表,在该字符串表中保存了DLL的名称;符号地址表地址字段指向了一个地址数组,该数组中保存了符号的地址;符号名称表地址字段指向了符号名称数组,该数组中保存了符号名称字符串的地址;名称序号对应关系表地址指向了名称序号对应关系的数组,该数组中存储了符号的名称与序号之间的对应关系。

 符号地址表是一个DWORD类型的数组,在该数组中存储了被导出符号的相对虚拟地址,数组中每一个非零的数组元素都指向了一个符号的相对虚拟地址;符号名称表是一个DWORD类型的数组,数组中保存的是相对虚拟地址,该相对虚拟地址指向了字符串表中的相关位置,这些位置中保存了符号的名称;名称序号表是一个WORD类型的数组,该数组中保存了符号的序号。符号名称表中的元素与名称序号表中的元素通过数组下标对应。比如:在符号名称表中,数组下标为1的元素,它的序号存在在名称序号表中,数组下标也为1。

 序号的计算公式为:序号 = 符号地址表数组下标 + Base字段值。在Windows16位时代,由于受到硬件大小的限制,在执行动态链接的时候,使用序号查找符号的地址,即:用序号的值减去Base的值,获得符号地址表数组的下标,进而获得符号的相对虚拟内存地址。这种方式节省了内存空间以及符号查找的时间,但是易读性差。随着时间的发展,当硬件的物理内存不在是问题的时候,开始使用符号名称查找符号的地址,具体的查找过程是:通过符号名称在名称序号对应关系表中查找到符号的序号,然后再用符号的序号查找符号的地址。虽然引入了符号名称表,但是这个表不是必须的,依然可以通过序号查找符号的地址。在一个DLL中,每一个导出符号都有一个唯一对应的序号,而导出符号名是可选的。

在动态链接的时候,可以通过两种方式进行符号地址的查找,一种是直接利用符号的序号直接查找,另外一种是利用符号的名称间接查找。在进行符号地址查找的时候,符号地址表,符号名称表,名称序号对应表之间的关系如下图所示:

这里写图片描述

基址重定位表

在目标文件中,所有符号的地址都是基于文件头或者文件中某个位置的偏移地址。在将目标文件链接以后,在输出的PE文件中,这些偏移地址会被转化成相对虚拟内存地址,并且再加上一个默认内存加载位置的地址值,形成符号的基于默认内存加载位置的虚拟内存地址。基于默认内存加载位置的虚拟内存地址的计算公式为:

可执行程序默认加载到内存的0x0400000位置,而动态链接库默认加载到内存的0x10000000位置。

当PE文件被加载到内存以后,如果该文件不能被加载到默认的内存位置,那么在指令代码中,所有使用绝对地址表示的符号的地址都需要被重定位。在Windows中,这一地址重定位的过程被叫做重定基地址。具体的操作过程是:在每一个需要进行地址重定位的符号处,将该符号当前地址的数值上再加上一个固定的数值,这个新获得的地址值就是该符号正确的虚拟内存地址。

这个固定值的计算公式是:

地址重定位工作由操作系统的加载器来完成,在基地址重定位表中,记录了每一个需要进行地址重定位的符号的地址。在地址重定位的时候,加载器读取该表中的数值,然后查找到需要进行地址重定位的符号的位置,最后修正该符号的虚拟内存地址。

在PE文件被加载到内存以后,这些文件内容是以页为单位存储在内存中,每个内存页的大小是4KB。在基址重定位表中,数据表中的数据被分割成一个个数据块,每一个数据块会对应一个虚拟内存页,表示在该虚拟内存页中的符号的地址重定位信息。
基址重定位表的结构如下图所示
这里写图片描述

可选头中包含了数据目录,数据目录的第五项数据中包含了指向了基址重定位表的指针,以及基址重定位表的大小。基址重定位表以内存页的大小为依据进行分块,在每一个块中,都以IMAGE_BASE_RELOCATION类型的数据结构开头,后面跟随着每个符号的基地址重定位信息。这些符号的重定位信息是一系列的WORD值。这些WORD值的高4位指出了重定位的类型,而低12位是一个地址偏移。将该地址偏移数值与数据块的虚拟内存地址数值(即:IMAGE_BASE_RELOCATION. VirtualAddress)相加,可以得到该符号需要进行重定位的位置。
在基址重定位表的数据块中,所包含的重定位信息的个数的计算公式为:

重定位信息个数 = (块大小 – sizeof(IMAGE_BASE_RELOCATION))/2

因为块大小以字节为单位表示,而重定位信息以字为单位表示,转化成字需要除2

重定位地址的计算公式为:默认加载位置 + 数据块相对虚拟内存地址 + 偏移 = 0x10000000 + 0x11000 + 0x54F = 0x1001154F。处于虚拟内存地址0x1001154F处的地址值需要被重定位。

各数据结构之间的关系

在PE文件中,各个数据结构之间的关系如下图所示:
这里写图片描述

Windows里的二进制可执行文件exe和dll文件是COFF/PE文件格式的文件,通过了解Windows的编译、链接里的COFF/PE文件结构,可以知道,COFF/PE文件结构里有一个可选文件头。
在IMAGE_OPTIONAL_HEADER32可选头文件结构里BYTE类型字段MajorLinkerVersion创建可执行文件的链接器的主版本号。对于Microsoft的链接器生成的PE文件,这个版本号的Visual Studio的版本号相一致。
BYTE类型字段MinorLinkerVersion表示创建可执行文件的链接器的次版本号。

使用vc6.0和vs2103编译一样的代码,dumpbin.exe的结构比较如下图:
这里写图片描述

vs2013对应的c运行时库是msvcr120.dll,
vc6.0对应的c运行时库是msvcrt.dll。

exe或者dll根据entry point选择要链接的c运行时库来连接。