*.exe *.com 文件 ── MicroSoft Windows Executable File 可执行文件
*.exe 文件是一种可在操作系统存储空间中,浮动定位的可执行程序。MS-DOS 和 MS-Windows 下,此类文件扩展名通常为 .exe。
可执行文件 (executable file) 指的是:可由操作系统进行加载执行的文件。在不同的操作系统环境下,可执行程序的呈现方式也不一样。在 MicroSoft Windows 操作系统下,可执行程序可以是 .exe 文件、.sys 文件、.com 等类型文件。
Linux 可执行文件格式为 ELF,即 Executable and Linkable Format。Mac 可执行文件格式为 Mach-O,即 Mach Object 格式。
MicroSoft Windows 操作系统中的二进制可执行文件,分两种:一种后辍名为 .com,另一种后辍名为 .exe。
*.com 一般用于 MS-DOS 环境,用以标识可执行文件的扩展名;在 MicroSoft Windows 操作系统中的执行文件,一般是 *.exe 文件。用户在 "提示行" 中输入不带 .exe 扩展名字符的文件名后,按下键盘 Enter 键就能运行此可执行程序。
开发流程在 MS-DOS 环境下,生成一个可执行文件的步骤比较简单,用编译器将源程序编译为 *.obj 文件,再用链接器将 *.obj 文件链接成 *.exe 文件,不同语言的开发过程都差不多。
*.obj 文件是程序编译时,生成的中间代码文件。目标文件,一般是程序编译后的二进制文件,通过链接器链接资源文件,就可形成可执行文件。*.obj 文件只给出了程序的相对地址,而可执行文件是绝对地址。因为每次编译时默认都是采用增量编译,即只重新编译改变了的模块,*.obj 文件会保存各模块的编译结果,可加快编译速度。
MS-DOS 可执行文件中的内容,是由源程序中所写的代码和数据定义转换而来的。惟一的例外是带覆盖部分(Overlay)的 *.exe 文件,MS-DOS 在基本 *.exe 文件后附加了一些自定义数据,其中可执行部分的长度由文件头偏移 0002h 和 0004h 中的长度给出,该长度之后到文件实际长度这部分就是 Overlay 部分。这样,即使一个带覆盖的 *.exe 文件大小远超 640 KB,在 MS-DOS 下也能运行,因为操作系统只需装入真正的可执行部分,然后由程序自己去读取覆盖部分的数据。一些打包软件生成的奇大无比的自解压包就采用的这种结构,可执行部分是解包代码,覆盖部分是被压缩的数据。MS-DOS 对可执行文件覆盖部分的数据格式并没有规定,它是程序员按自己的方式组织的。如程序员愿意,也可把这些数据单独放在另一文件中。
Win32 可执行文件叫做 PE 文件。PE 文件的基本结构和 DOS 可执行文件有很大的不同。它把程序中的不同部分分成各种节区(Section),其中有一个节区是放置各种资源的,如菜单、对话框、位图、光标、图标和声音等。虽可把资源部分理解成类似 DOS 可执行文件中的 “覆盖” 部分,但由于资源是 Win32 可执行文件的标准组成部分,而且是非常重要的组成部分;因此,它的格式是固定的。所以与 MS-DOS 软件的开发过程相比,Win32 软件的开发中多了一个创建资源文件的步骤。
代码部分的开发工作与 MS-DOS 下写代码的步骤,是一样的。程序员用文本编辑器书写汇编源代码(*.asm 文件)。与 C 源代码类似,*.asm 文件中也可以用 include 语句包含数据定义和函数声明的头文件,Win32 汇编的头文件一般用 .inc 作扩展名。大部分的 include 文件是编译器软件包附带提供的。最后,*.asm 文件经汇编编译器,编译成以 .obj 为扩展名的目标文件。
资源文件中可以包括对话框、快捷键、菜单、字符串、版本信息和一些图形资源等内容。资源文件的源文件是一种类似 “脚本” 的文本文件,它的扩展名一般为 .rc,其中用不同的语法定义了不同类型的资源,资源脚本文件最后由资源编译器编译成资源文件 *.res。资源脚本文件同样会用到很多预定义值;所以,软件包中一般也包括资源头文件,供源文件导入。软件包中的资源头文件,名为 Resource.h。
在资源文件中,不同类型资源的记录方式是不同的。对话框资源只记录定义值,如对话框的大小、位置等,并非真正存储对话框最后显示在屏幕上的像素。这些大小、位置等信息最后由 MicroSoft Windows 解释后,才可在屏幕上被绘制成像素。菜单、字符串、快捷键等由文本构成;图形资源则真正由像素组成,它们在资源脚本中被定义为一文件名,由资源编译器从磁盘文件导入。MicroSoft Windows 在资源中支持的图形文件有 bmp 位图文件、cur 光标文件和 ico 图标文件,这些图形文件可用其他图形处理软件生成。另外,wav 声音文件也可以用在资源中。
编译好目标 *.obj 文件和资源 *.res 文件后,最后一步是用链接器将它们链接成可执行文件。链接的时候要会用到函数库。在 MS-DOS 环境下编程时,使用的函数库是静态库。静态库是一些已经编译好的代码模块。当用户在源程序中用到某个函数时,链接器从库文件中将这个函数的二进制代码取出,与 *.obj 文件合在一起生成最终的 *.exe 文件。但在 Win32 环境下,大部分的公用函数封装在 DLL 文件中,以动态链接的方式供用户程序调用。这时候库文件中只需要包含函数在 DLL 中的位置信息,不再需要导入二进制代码部分。所以链接的时候,也只是把库文件中的位置信息取出放入最后的可执行文件中。Win32 中这种只包含位置信息的库文件,又称导入库。
文件结构
*.exe 文件比较复杂,每个 *.exe 文件都有一个文件头,结构如下:
―――――――――――――――――――
├ 偏移量 ┤ 意义 ┤├00h-01h ┤MZ'EXE文件标记 ┤
├2h-03h ┤文件长度除512的余数 ┤
├04h-05h ┤...............商 ┤
├06h-07h ┤重定位项的个数 ┤
├08h-09h ┤文件头除16的商 ┤
├0ah-0bh ┤程序运行所需最小段数 ┤
├0ch-0dh ┤..............大.... ┤
├oeh-0fh ┤堆栈段的段值 (SS) ┤
├10h-11h ┤........sp ┤
├12h-13h ┤文件校验和 ┤
├14h-15h ┤IP ┤
├16h-17h ┤CS ┤
├18h-19h ┤............ ┤
├1ah-1bh ┤............ ┤
├1ch ┤............ ┤
―――――――――――――――――――――――――
*.exe 文件包含一个 "文件头" 和一个" 可重定位程序映象"。文件头包含 MS-DOS 用于加载程序的信息,譬如:程序大小、寄存器初始值。文件头还指向一个重定位表,该表包含指向程序映象中可重定位段地址的指针链表。文件头的形式与 EXEHEADER 结构对应:
EXEHEADER STRUC
exSignature dw 4D5AH ;.EXE标志
exExraBytes dw ? ;最后(部分)页中的字节数
exPages dw ? ;文件中的全部和部分页数
exRelocItems dw ? ;重定位表中的指针数
exHeaderSize dw ? ;以字节为单位的文件头大小
exMinAlloc dw ? ;最小分配大小
exMaxAlloc dw ? ;最大分配大小
exInitSS dw ? ;初始SS值
exInitSP dw ? ;初始SP值
exChechSum dw ? ;补码校验值
exInitIP dw ? ;初始IP值
exInitCS dw ? ;初始CS值
exRelocTable dw ? ;重定位表的字节偏移量
exOverlay dw ? ;覆盖号
执行原理
如记事本程序 notepad.exe,这类程序通常用来处理或辅助处理其它文件。譬如:双击打开 myfile.txt 将由 notepad.exe 记事本程序来进行编辑。
为加载 *.exe 程序,MS-DOS 首先会读取文件头以确定 *.exe 标志并计算程序映象的大小。然后 MS-DOS 会试图申请内存。
首先,MS-DOS 会计算程序映象文件的大小加上 PSP 的大小再加上 EXEHEADER 结构中的 exMinAlloc 域说明的内存大小这三者之和,如总和超过最大可用内存块大小。则 MS-DOS 会停止加载程序并返回一个出错值。否则,MS-DOS 会计算程序映象的大小加上 PSP 的大小再加上 EXEHEADER 结构中 exMaxAlloc 域说明的内存大小之和,如第二总和小于最大可用内存块的大小,则 MS-DOS会分配计算得到的内存量。否则,MS-DOS 会分配最大可用内存块。分配完内存后,MS-DOS 会确定段地址,也称为起始段地址,MS-DOS 从此处加载程序映象。
如 exMinAlloc 域和 exMaxAlloc 域中的值都为零,则 MS-DOS 会把映象尽可能地加载到内存最高端。否则,MS-DOS 会把映象加载到紧挨着 PSP 域之上。接下来,MS-DOS 会读取重定位表中的项目,调整所有由可重定位指针说明的段地址。对于重定位表中的每个指针,MS-DOS 会寻找程序映象中相应的可重定位段地址,并把起始段地址加到它之上。一旦调整完毕,段地址便指向了内存中被加载程序的代码和数据段。
MS-DOS 在所分配内存的最低部分建立 256 字节的 PSP,把 AL 和 AH 设置为加载 *.com 程序时所设置的值。MS-DOS 使用文件头中的值设置 SP 与 SS,调整 SS 初始值,把起始地址加到它之上。MS-DOS 还会把 ES 和 DS 设置为 PSP 的段地址。
最后,MS-DOS 从程序文件头读取 CS 和 IP 初始值,把起始段地址加到 CS 之 上,把控制转移到位于调整后地址处的程序。
长按二维码关注 "德云社区" ,点击最下方 "阅读原文" 了解更多资讯
版权声明:
本文为独家原创稿件,版权归 德云社区,未经许可不得转载;否则,将追究其法律责任。
|