Exe文件装载过程
一、程序段前缀 PSP. 1
二、 程序加载及实现... 4
三、重定位过程... 4
四、在DOS下,重定位指的是重新定位在你的程序中所引用的段值。... 4
五、exe文件结构... 5
一、程序段前缀 PSP
PSP ( Program segment prefix )
称为程序段前缀,是 DOS 在加载外部命令或应用程序 ( .EXE 或 .COM 文件 ) 时,在加载的程序段前面设置的一个固定长度的信息区
( 共 100H 字节 ), PSP 包括以下四个组成部分:
l 供进程调用的 DOS 入口 PSP+0 , +2 , +5, +50H 和 +2CH 字段
l 供进程使用的传递参数 PSP+5CH, +6CH 和 +80H 字段
l 为 DOS 保存的中断向量 PSP+0AH, +0EH , 和 +12H 字段
l 由 DOS 专用的保留区域 PSP+16H ~ 2BH 和 2EH ~ 37H 字段
PSP 的某些关键字段涉及到系统内部管理,所以使用者不得更改
偏移
|
字节数
|
说 明
|
0000
|
02
|
中断20H
|
0002
|
02
|
以节计算的内存大小(利用这个可看出是否感染引导型病毒)
|
0004
|
01
|
保留
|
0005
|
05
|
至DOS的长调用
|
000A
|
02
|
INT 22H 入口 IP
|
000C
|
02
|
INT 22H 入口 CS
|
000E
|
02
|
INT 23H 入口 IP
|
0010
|
02
|
INT 23H 入口 CS
|
0012
|
02
|
INT 24H 入口 IP
|
0014
|
02
|
INT 24H 入口 CS
|
0016
|
02
|
父进程的PSP段值(可测知是否被跟踪)
|
0018
|
14
|
存放20个SOFT号
|
002C
|
02
|
环境块段地址(从中可获知执行的程序名)
|
002E
|
04
|
存放用户栈地址指针
|
0032
|
1E
|
保留
|
0050
|
03
|
DOS调用 ( INT 21H / RETF )
|
0053
|
02
|
保留
|
0055
|
07
|
扩展的FCB头
|
005C
|
10
|
格式化的FCB1
|
006C
|
10
|
格式化的FCB2
|
007C
|
04
|
保留
|
0080
|
80
|
命令行参数长度(不包含总为最后的0D)及参数
也是程序运行期间缺省的DTA
|
一个操作示例:
D:\Masm615>dir jch ;显示计算阶乘的程序
Volume in drive D has no label
Volume Serial Number is 18F0-186B
Directory of D:\Masm615
JCH OBJ 371 04-10-03 20:58 JCH.obj
JCH ASM 3,637 04-03-03 20:46 jch.asm
JCH COM 12,486 04-10-03 20:58 Jch.com
3 file(s) 16,494 bytes
0 dir(s) 915,632,128 bytes free
D:\Masm615>jch 7 ;计算7的阶乘
5040
D:\Masm615>debug jch.com 7 ;用DEBUG查看命令行参数情况
-d80 L10 ;可看出命令行参数长度为02,参数是:2037 0D
128D:0080 02 20 37 0D 68 2E 63 6F-6D 20 37 0D 00 00 00 00 . 7.h.com 7.....
-
-D0 7F ;显示PSP数据
128D:0000 CD 20 00 9D 00 9A F0 FE-1D F0 4F 03 F2 0B 8A 03
128D:0010 F2 0B 17 03 F2 0B E1 0B-01 01 01 00 02 FF FF FF ;父进程的PSP
128D:0020 FF FF FF FF FF FF FF FF-FF FF FF FF 7E 12 4C 01 ;环境块段地址
128D:0030 F9 0F 14 00 18 00 8D 12-FF FF FF FF 00 00 00 00
128D:0040 07 0A 00 00 00 00 00 00-00 00 00 00 00 00 00 00
128D:0050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 37 20 20
128D:0060 20 20 20 20 20 20 20 20-00 00 00 00 00 20 20 20
128D:0070 20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00
-D127E:0 ;显示环境块的内容(在DOS提示符下可用SET命令查看环境信息)
127E:0000 54 4D 50 3D 43 3A 5C 57-49 4E 44 4F 57 53 5C 54 TMP=C:\WINDOWS\T
127E:0010 45 4D 50 00 54 45 4D 50-3D 43 3A 5C 57 49 4E 44 EMP.TEMP=C:\WIND
127E:0020 4F 57 53 5C 54 45 4D 50-00 50 52 4F 4D 50 54 3D OWS\TEMP.PROMPT=
127E:0030 24 70 24 67 00 77 69 6E-62 6F 6F 74 64 69 72 3D $p$g.winbootdir=
127E:0040 43 3A 5C 57 49 4E 44 4F-57 53 00 50 41 54 48 3D C:\WINDOWS.PATH=
127E:0050 43 3A 5C 57 49 4E 44 4F-57 53 3B 43 3A 5C 57 49 C:\WINDOWS;C:\WI
127E:0060 4E 44 4F 57 53 5C 43 4F-4D 4D 41 4E 44 00 43 4F NDOWS\COMMAND.CO
127E:0070 4D 53 50 45 43 3D 43 3A-5C 57 49 4E 44 4F 57 53 MSPEC=C:\WINDOWS
127E:0080 5C 43 4F 4D 4D 41 4E 44-2E 43 4F 4D 00 77 69 6E \COMMAND.COM.win
127E:0090 64 69 72 3D 43 3A 5C 57-49 4E 44 4F 57 53 00 42 dir=C:\WINDOWS.B
127E:00A0 4C 41 53 54 45 52 3D 41-32 32 30 20 49 35 20 44 LASTER=A220 I5 D
127E:00B0 31 20 54 34 20 50 33 30-30 00 43 4D 44 4C 49 4E 1 T4 P300.CMDLIN
127E:00C0 45 3D 64 65 62 75 67 20-6A 63 68 2E 63 6F 6D 20 E=debug jch.com
127E:00D0 37 00 00 01 00 4A 43 48-2E 43 4F 4D 00 FF 1E 8E 7....JCH.COM....
(环境块中可找到当前执行的程序名)
-d be1:0 ;显示父进程的PSP(利用父进程PSP可测知程序是否被其他程序加载跟踪)
0BE1:0000 CD 20 00 A0 00 9A F0 FE-1D F0 14 03 0F 0A 6D 01 . ............m.
0BE1:0010 0F 0A 78 01 0F 0A 0F 0A-01 01 01 00 02 FF FF FF ..x.............
0BE1:0020 FF FF FF FF FF FF FF FF-FF FF FF FF CF 0B 2E 3E ...............>
0BE1:0030 EF 0B 14 00 18 00 DE 0B-FF FF FF FF 00 00 00 00 ................
0BE1:0040 07 0A 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
0BE1:0050 CD 21 CB 00 00 00 00 00-00 00 00 00 00 4A 43 48 .!...........JCH
0BE1:0060 20 20 20 20 20 43 4F 4D-00 00 00 00 00 37 20 20 .....COM.....7
0BE1:0070 20 20 20 20 20 20 20 20-00 00 00 00 00 00 00 00 ........
0BE1:0080 0A 20 6A 63 68 2E 63 6F-6D 20 37 0D 00 00 00 00 . jch.com 7.....
;父进程中的命令行参数
另外:保存INT 22/INT 23/INT 24H的值使得用户在程序中可修改这些中断的值,病毒就曾利用这种技术防止不能传染时引起出错信息。在程序退出时根据PSP中保存的值恢复各中断的值。
当可执行文件加载后,根据其不同的类型,系统会采用相应的方式进行处理。若用户程序是 .COM 文件,则加载后把程序装入程序段偏移量为 100H
处,并把所有的段寄存器指向 PSP,实际上,由于 .COM 文件是以长度小于 64K 为要求设计的,所有的程序,数据包括 PSP
都在同一段内。对于 .EXE 文件,加载后把程序的 DS 和 ES 指向 PSP 段,若要使用 PSP
,例如,要利用输入参数,可在程序的开始利用 DS或 ES 实现获取。当然,获取 PSP 的段址也可以利用以下介绍的系统调用实现。
对编程者而言,在 PSP
中较为有用的是参数区,参数是通过在程序执行开始时,利用空格与之分隔的字符串,用回车表示结束,具体的说,就是程序和参数在同一行输入。当程序加载时,
系统将参数及参数长度填入 PSP+80H 开始的参数区,其中 80H 处是参数长度,参数由 81H 开始存放。
二、 程序加载及实现
所谓 "加载" 就是在 DOS 环境下,PC 机从磁盘上将可执行文件(即
DOS 下的 .EXE 文件和 .COM 文件)装入到内存的过程。加载的过程包括程序装入内存的分配方案,加载是否成功,装入后 CPU
各寄存器内容及包括的信息等,对编程者而言,掌握一般的加载后信息分配,在某些应用场合中,了解和掌握如何在自己的程序中加载新的程序并使之运行的方法是
非常有用的。
我们知道, DOS 下允许用户键入的命令有 DOS 内部命令,DOS
外部命令,可执行的应用程序及批处理文件,对同名的可执行文件按 .COM 到 .EXE 的优先顺序处理,具体的说,DOS 加载可执行文件是通过
EXEC 子功能实现的 ( 即 INT 21H 的 AH=4BH )。由于 .COM 和 .EXE
文件的结构不同,它们在加载的实际过程及装入内存方式是不同的,所以,对待不同的文件应有不同的处理方式。
加载 .EXE 文件后,各寄存器的初值设置
1) DS 和 ES 指向 PSP 的段地址
2) CS 指向代码段的绝对段址
3) SS 指向堆栈段的绝对段址
4) IP 指向代码段入口时第一条指令的偏移地址
5) SP 指向堆栈段如口时深度,此值由文件头位移 10H 的字域决定
6) BX, CX 是加载程序的字节长度
加载 .COM 文件后,各寄存器的初值设置
1) CS,DS,ES 和 SS 指向 PSP 的段地址
2) IP 固定为 100H
3) SP 位 FFFEH ,并在栈顶出压入一全 0 字
4) BX, CX 是 COM 文件的字节长度
A DOS 下 EXEC 子功能的应用
B 加载程序的设计和实现
三、重定位过程
假如说在生成的exe文件头里的重定位表中有一项是0000:0030,0000H是高字
段,0030H是低字段。用高字段加上起始段值,也就是exe文件加载到内存里的起始地址段(假如是1000H),则结果为1000H,再加上低字段作为
偏移量,则结果为10030H,这个20位的地址指向内存地址为10030H的单元。把指向的这个地址里的字取出来(假设是0010H),加上起始段值,
再放回去。这就完成了一次重定位。即10030H里存放的是1010H。
四、在DOS下,重定位指的是重新定位在你的程序中所引用的段值。
例如,在你的程序(Large Mode)中,有一处需要调用C的库函数,你的程序编译后可能是下面的样子:
0000:0000 9A78563412 call far 1234:5678
~~~~ ~~~~
其中,下划线的段值部分,就是需要在程序加载时重新定位的。
程序加载器所作的重定位工作,就是将程序中需要重定位的地方,都加上程序的加载地址。
还是上面的例子,假如这个程序被加载到了内存中的1111段处。那么完成重定位后,代码应该是这样:
1111:0000 9A78564523 call far 2345:5678
~~~~ ~~~~
OK,DOS下的重定位就是这么简单。那么,程序加载器如何找到重定位项呢?这就需要EXE文件头中的
重定位表的帮助。
重定位表是一个非常简单的4字节结构数组:
typedef struct tagRTABLE
{
WORD wOffset
WORD wSegment
}RELOCATIONTABLE, * PRELOCATIONTABLE;
编译器(或自己手工)先计算程序中需要重定位的地方在文件中的偏移量,将此偏移量减去文件头的大小,
然后换算为 Segment:Offset 的形式,填入EXE文件头中的重定位表,就完成了一个重定位项的建立。
还是上面的例子,这个重定项在重定位表中应该是这个样子:
.
.
.
---------
| 0003 |
---------
| 0000 |
---------
五、exe文件结构
Dos exe file structure
offset size description
00 word "mz" - link file .exe signature (mark zbikowski?)
02 word length of image mod 512
04 word size of file in 512 byte pages
06 word number of relocation items following header
08 word size of header in 16 byte paragraphs, used to locate
the beginning of the load module
0a word min # of paragraphs needed to run program
0c word max # of paragraphs the program would like
0e word offset in load module of stack segment (in paras)
10 word initial sp value to be loaded
12 word negative checksum of pgm used while by exec loads pgm
14 word program entry point, (initial ip value)
16 word offset in load module of the code segment (in paras)
18 word offset in .exe file of first relocation item
1a word overlay number (0 for root program)
- relocation table and the program load module follow the header
- relocation entries are 32 bit values representing the offset
into the load module needing patched
- once the relocatable item is found, the cs register is added to
the value found at the calculated offset
registers at load time of the exe file are as follows:
ax: contains number of characters in command tail, or 0
bx:cx 32 bit value indicating the load module memory size
dx zero
ss:sp set to stack segment if defined else, ss = cs and
sp=ffffh or top of memory.
ds set to segment address of exe header
es set to segment address of exe header
cs:ip far address of program entry point, (label on "end"
statement of program).
EXE文件包含一个文件头和一个可重定位程序映象.文件头包含MS-DOS
用于加载程序的信息,例如程序的大小和寄存器的初始值.文件头还指向一个
重定位表,该表包含指向程序映象中可重定位段地址的指针链表.文件头的形
式与EXEHEADER结构对应:
EXEHEADER STRUC
exSignature dw 5A4Dh ;.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 ? ;覆盖号
EXEHEADER ENDS
程序映象,包含处理器代码和程序的初始数据,紧接在文件头之后.它的
大小,以字节为单位,等于.EXE文件的大小减去文件头的大小,也等于exHeaderSize
的域的值乘以16.MS-DOS通过把该映象直接从文件拷贝到内存加载.EXE程序
然后调整定位表中说明的可重定位段地址.
定位表是一个重定位指针数组,每个指向程序映象中的可重定位段地址.
文件头中的exRelocItems域说明了数组中指针的个数,exRelocTable域说明了
分配表的起始文件偏移量.每个重定位指针由两个16位值组成:偏移量和段值.
为加载.EXE程序,MS-DOS首先读文件头以确定.EXE标志并计算程序映象的
大小,然后它试图申请内存.首先,它计算程序映象文件的大小加上PSP的大小
再加上EXEHEADER结构中的exMinAlloc域说明的内存大小这三者之和,如果总 和超过最大可
用内存块的大小,则MS-DOS停止加载程序并返回一个出错值.否
则,它计算程序映象的大小加上PSP的大小再加上EXEHEADER结构中exMaxAlloc
域说明的内存大小之和,如果第二个总和小于最大可用内存块的大小,则MS-DOS 分配计算得到的内存量.否则,它分配最大可用内存块.
分配完内存后,MS-DOS确定段地址;也称为起始段地址,MS-DOS从此处加载
程序映象.如果exMinAlloc域和exMaxAlloc域中的值都为零,则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之 上,把控制转移到位于调整后地址处的程序.