溪叟:Windows可执行文件简述

来源:百度文库 编辑:中财网 时间:2024/05/04 16:47:29
Windows可执行文件简述(一)
操作系统中的文件是一种抽象的机制,提供了一种在磁盘上保存信息而且方便以后读取的方法。在Windows操作系统中,一个用户可以最直接体会到的文件的形式就是以.exe、.dll等为扩展名的可执行文件。伴随着Windows操作系统的不断进步,其可执行文件的格式也发生了巨大变化。这期间主要有4个过程:DOS中出现的最简单的以.com为扩展名的可执行文件和以.exe为扩展名的MZ格式(MZ是MZ格式的主要作者Mark Zbikowski的名字的缩写)的可执行文件,Win 3.x下出现的NE(New Executable:分段可执行文件)格式的.exe和.dll文件,Win 3.x和Win9x所专有的LE(Linear Executable:线性可执行文件,专用于VxD文件),Win9x和Win NT/2000/XP下的32位的可执行文件PE(Portable Executable:可移动的可执行文件)。这里面com、MZ和NE属于Win16,PE属于Win32,LE可以兼容Win16和Win32。
在一个操作系统中,可执行的代码最终被装入内存执行之前是以文件的方式存放在磁盘中的,也就是以可执行文件的方式。下面是Microsoft Windows操作系统中的可执行文件的概述。
1.com格式
Windows下最简单的可执行文件就是DOS下的以.com为扩展名的com文件。com文件是旧有的只有64kb内存的cp/m机器的产物。com格式文件最大64K,com文件内含16位程序的二进制代码映像,没有重定位信息。
com文件包含程序的二进制代码的一个绝对映像。也就是说,为了运行程序准确的处理器指令和内存中的数据,MS-DOS通过直接把该映像从文件拷贝到内存而加载com程序;它不作任何改变。
为加载一个com程序,MS-DOS首先试图分配内存,因为com程序必须位于一个64K的段中,所以com文件的大小不能超过65,024(64K减去用于PSP的256字节和用于一个起始堆栈的至少256字节)。如果MS-DOS不能为程序、一个PSP、一个起始堆栈分配足够内存,则分配尝试失败。否则,MS-DOS分配尽可能多的内存(直至所有保留内存)。即使com程序本身不能大于64K。在试图运行另一个程序或分配另外的内存之前,大部分com程序释放任何不需要的内存。
分配内存后,MS-DOS在该内存的头256字节建立一个PSP(Program Segment Prefix,程序段前缀),PSP结构如下:
偏移
大小(Byte)
说 明
0000h
02
中断20H
0002h
02
以节计算的内存大小(利用这个可看出是否感染引导型病毒)
0004h
01
保留
0005h
05
至DOS的长调用
000Ah
02
INT 22H 入口 IP
000Ch
02
INT 22H 入口 CS
000Eh
02
INT 23H 入口 IP
0010h
02
INT 23H 入口 CS
0012h
02
INT 24H 入口 IP
0014h
02
INT 24H 入口 CS
0016h
02
父进程的PSP段值(可测知是否被跟踪)
0018h
14
存放20个SOFT号
002Ch
02
环境块段地址(从中可获知执行的程序名)
002Eh
04
存放用户栈地址指针
0032h
1E
保留
0050h
03
DOS调用(INT 21H / RETF)
0053h
02
保留
0055h
07
扩展的FCB头
005Ch
10
格式化的FCB1
006Ch
10
格式化的FCB2
007Ch
04
保留
0080h
80
命令行参数长度
0081h
127
命令行参数
Windows可执行文件简述(二)
如果PSP中的第一个FCB含有一个有效驱动器标识符,则置AL为00h,否则为0FFh。MS-DOS还置AH为00h或0FFh,这依赖于第二个FCB是否含有一个有效驱动器标识符。
建造PSP后,MS-DOS在PSP后立即开始(偏移100h)加载com文件,它置SS、DS和ES为PSP的段地址,接着创建一个堆栈。为了创建这个堆栈,MS-DOS置SP为0000h。如果没有分配64K内存,则要置寄存器为比所分配的字节总数大2的值。最后,它把0000h推进栈,这是为了保证与在早期MS-DOS版本上设计的程序的兼容性。
MS-DOS通过把控制传递偏移100h处的指令而启动程序。程序设计者必须保证com文件的第一条指令是程序的入口点。
注意,因为程序是在偏移100h处加载,因此所有代码和数据偏移也必须相对于100h.汇编语言程序设计者可通过置程序的初值为100h而保证这一点(例如通过在原程序的开始使用语句org 100h)。
下面是一个简单的com文件Win32 ASM的例子:
MAN STRUCT                           ;定义一个结构
W  dw 1234h                ;dw 也可用 word
B  db 9 dup(?)               ;db 也可用 byte
MAN ENDS
.model tiny                          ;com格式文件
.data                                   ;对于com格式文件,数据段的内容会自动放到代码段后
zz  MAN <>,<1,"abcd">,<3,"Ldf">
.code                                  ;代码段
.startup                               ;可使下面的指令从0100H 开始(com格式文件要求)
mov ax,3031h
mov zz.W,ax                ;对结构赋值
mov zz.B,'1'
mov ax,type(MAN)       ;取结构所占字节数
.exit                                  ;可设置返回码
end
2.MZ格式
com发展下去就是MZ格式的可执行文件,这是DOS所能了解的具有重定位功能的可执行文件格式。MZ可执行文件内含16位代码,在这些代码之前加了一个文件头,文件头中包括各种说明数据,如第一句可执行代码执行指令时所需要的文件入口点、堆栈的位置、重定位表等,操作系统根据文件头重的信息将代码部分装入内存,让后根据重定位表修正代码,最后在设置好堆栈后从文件头中指定的入口开始执行。所以DOS可以把程序放在任何它想要的地方。下图是MZ格式的可执行文件的简单结构示意图:
MZ标志
MZ文件头
其它信息
重定位表的字节偏移量
重定位表
重定位表
可重定位程序映像
二进制代码
3.NE格式
为了保持对DOS的兼容性和保证Windows的需要,在Win 3.x中出现的NE格式的可执行文件中保留了MZ格式的头(具体原因后面会说,这里就不赘述了),同时NE文件又加了一个自己的头,之后才是可执行文件的可执行代码。Win 3.x中的16位Windows程序或OS/2程序都有可能是NE格式的。NE类型包括了.exe、.dll、.drv和.fon四种类型的文件。NE格式的关键特性是:它把程序代码、数据、资源隔离在不同的可加载区块中。它也藉由符号输入和输出,实现所谓的执行时期动态链接。16位的NE格式文件装载程序(NE Loader)读取部分磁盘文件,并生成一个完全不同的数据结构,在内存中建立模块。当代码或数据需要装入时,装载程序必须从全局内存中分配出一块,查找原始数据在文件的什么地方,找到位置后再读取原始的数据,最后再进行一些修整。还有,每一个16位的Module要负责记住现在使用的所有段选择符,该选择符表示该段是否已经被抛弃等等。下图是NE格式的可执行文件的结构示意图:
MS-DOS头
DOS文件头
保留区域
Windows头偏移
DOS Stub程序
信息块
NE文件头
段表
资源表
驻留名表
模块引用表
引入名字表
入口表
非驻留名表
代码段和数据段
程序区
重定位表
其他:Windows可执行文件简述(一)
Windows可执行文件简述(三)
Windows可执行文件简述(三)
4.LE格式
在Windows3.x的时代,从DOS启动Windows,Windows在把机器转到保护模式之前需要在实模式下做一些初始化。实模式的16位代码必须和32位代码一起放在可执行文件中。旧的DOS下的可执行文件和NE格式的可执行文件无法满足这个要求,于是从Win 3.x起到Win 9x,产生了一种新型的可执行文件格式LE,它只适用于工作于系统底层的、同时包含16位代码和32位代码的VxD驱动程序。OS/2 2.x也使用LE格式。
一般而言,保护模式下的可执行文件会在不同的Sections中放置程序代码和数据,利用可执行文件头中的各种属性标志来告诉可执行文件加载器在面对这些Sections时的各种细节动作。但是VxD却将程序代码和数据混杂在段之中,通过不同段前的标识来表明该段在运行时的特性。程序代码和数据之所以能够混杂在一起,而仍然能够有效运作,是因为VxD所使用的平坦模式的代码和平坦模式下的数据选择器有相同的基地址与限制因素。因此不论使用上述哪一个寄存器缓存器都可以取用程序代码或数据。
下图是LE格式的可执行文件的结构示意图:
MZ文件头
DOS文件头
DOS Stub程序
LE标志
LE文件头
LE文件头
LCODE
程序段
PCODE
…… ……
16ICODE
MCODE
在LE文件中,代码和数据被存放在几类运行属性不同的段中。下面是LE文件之中一些可用的段类:
LCODE:页面锁定的代码和数据段。这种段被锁定在内存里。换句话说,这段永远不会被放到硬盘上去,所以你一定要谨慎的使用这种段类以免浪费宝贵的内存。那些每时每刻都必须放在内存中的代码和数据应该放在这个段里。尤其是那些硬件中断处理程序。
PCODE:可调页代码段。VMM可以对这种段实行调页处理,在这种段里的代码不必时刻放在内存里,当VMM需要物理内存的时候,它就会把这段放到硬盘上去。
PDATA:可调页数据段。
ICODE:仅用于的初始化段。这种段里的代码仅仅用来进行VxD的初始化。当初始化完成后,VMM就把这段从内存中释放。
DBOCODE:仅用于调试的代码数据段。当你要调试VxD程序时,就要用到这种段里的代码和数据,例如,它包含要调试的消息的处理代码。
SCODE:静态代码和数据段。这种段时刻存在于内存中,即使VxD已经卸载,这种段对某些动态的VxD程序很有用,这些VxD程序需要在某一Windows进程里不停的加载/卸载而又要纪录上次的环境和状态。
RCODE:实模式初始化代码数据段。这种段包含实模式初始化需要的16位代码和数据。
16ICODE:保护模式初始化数据段。这是一个16位的段,它包含VxD要从保护模式拷贝到V86模式的代码。例如,如果你要把一些V86的代码拷贝到一个虚拟机上时,你想拷贝的代码就要放在这里。如果你把它放在其他的段里,编译程序就会产生错误的代码,例如,它会产生32位代码而不是16位代码。
MCODE:锁定的消息字串。这种段包含了由VMM消息宏帮助编译的消息字串,这有助于你构造你的驱程的国际版本。
5.PE格式
16位的程序是不健壮的,因此为了保证Windows的健壮性,Microsoft在Microsoft Windows NT 3.1及其以后的操作系统版本,也就是所有的基于Win32的操作系统中推出了一种新的可执行文件的格式,也就是PE文件格式。PE的意思就是Portable Executable(可移植的可执行文件),内含32位程序代码和数据,是UNIX Common Object File Format(COFF)的演化。PE格式比其它格式优越的关键点在于它有依字母次序排列的Exports,以及一个可以直接将程序影像映像成虚拟内存的内存文件映射功能。与此同时,Microsoft也推出了新的obj文件和lib文件的格式。PE文件结构的总体层次分布图如下:
DOS MZ Header
DOS 实模式存根(Stub)程序
PE Header(PE文件标记等)
Optional Header
.text Section Header
.bss Section Header
.rdata Section Header
……………………
.debug Section Header
.text Section
.bss Section
.rdata Section
………………
.debug Section
从PE开始,可执行文件的结构开始有了正式的文档化。Microsoft在Visual C++ 6的联机文档(即《MSDN Library Visual Studio 6.0》)中给出了以C语言描述的PE结构,这给开发者提供了很大的方便。同时还在VC的Include目录里的WINNT.h文件中提供PE的各结构的定义,提供了PE文件中使用的原始数据结构。
其他:Windows可执行文件简述(一)
Windows可执行文件简述(二)
=======================================================================