Exe文件裝載過程
一、程序段前綴 PSP. 1
二、 程序加載及實(shí)現(xiàn)... 4
三、重定位過程... 4
四、在DOS下,重定位指的是重新定位在你的程序中所引用的段值。... 4
五、exe文件結(jié)構(gòu)... 5
一、程序段前綴 PSP
PSP ( Program segment prefix )
稱為程序段前綴,是 DOS 在加載外部命令或應(yīng)用程序 ( .EXE 或 .COM 文件 ) 時(shí),在加載的程序段前面設(shè)置的一個(gè)固定長度的信息區(qū)
( 共 100H 字節(jié) ), PSP 包括以下四個(gè)組成部分:
l 供進(jìn)程調(diào)用的 DOS 入口 PSP+0 , +2 , +5, +50H 和 +2CH 字段
l 供進(jìn)程使用的傳遞參數(shù) PSP+5CH, +6CH 和 +80H 字段
l 為 DOS 保存的中斷向量 PSP+0AH, +0EH , 和 +12H 字段
l 由 DOS 專用的保留區(qū)域 PSP+16H ~ 2BH 和 2EH ~ 37H 字段
PSP 的某些關(guān)鍵字段涉及到系統(tǒng)內(nèi)部管理,所以使用者不得更改
偏移
|
字節(jié)數(shù)
|
說 明
|
0000
|
02
|
中斷20H
|
0002
|
02
|
以節(jié)計(jì)算的內(nèi)存大小(利用這個(gè)可看出是否感染引導(dǎo)型病毒)
|
0004
|
01
|
保留
|
0005
|
05
|
至DOS的長調(diào)用
|
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
|
父進(jìn)程的PSP段值(可測知是否被跟蹤)
|
0018
|
14
|
存放20個(gè)SOFT號(hào)
|
002C
|
02
|
環(huán)境塊段地址(從中可獲知執(zhí)行的程序名)
|
002E
|
04
|
存放用戶棧地址指針
|
0032
|
1E
|
保留
|
0050
|
03
|
DOS調(diào)用 ( INT 21H / RETF )
|
0053
|
02
|
保留
|
0055
|
07
|
擴(kuò)展的FCB頭
|
005C
|
10
|
格式化的FCB1
|
006C
|
10
|
格式化的FCB2
|
007C
|
04
|
保留
|
0080
|
80
|
命令行參數(shù)長度(不包含總為最后的0D)及參數(shù)
也是程序運(yùn)行期間缺省的DTA
|
一個(gè)操作示例:
D:\Masm615>dir jch ;顯示計(jì)算階乘的程序
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 ;計(jì)算7的階乘
5040
D:\Masm615>debug jch.com 7 ;用DEBUG查看命令行參數(shù)情況
-d80 L10 ;可看出命令行參數(shù)長度為02,參數(shù)是: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數(shù)據(jù)
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 ;父進(jìn)程的PSP
128D:0020 FF FF FF FF FF FF FF FF-FF FF FF FF 7E 12 4C 01 ;環(huán)境塊段地址
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 ;顯示環(huán)境塊的內(nèi)容(在DOS提示符下可用SET命令查看環(huán)境信息)
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....
(環(huán)境塊中可找到當(dāng)前執(zhí)行的程序名)
-d be1:0 ;顯示父進(jìn)程的PSP(利用父進(jìn)程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.....
;父進(jìn)程中的命令行參數(shù)
另外:保存INT 22/INT 23/INT 24H的值使得用戶在程序中可修改這些中斷的值,病毒就曾利用這種技術(shù)防止不能傳染時(shí)引起出錯(cuò)信息。在程序退出時(shí)根據(jù)PSP中保存的值恢復(fù)各中斷的值。
當(dāng)可執(zhí)行文件加載后,根據(jù)其不同的類型,系統(tǒng)會(huì)采用相應(yīng)的方式進(jìn)行處理。若用戶程序是 .COM 文件,則加載后把程序裝入程序段偏移量為 100H
處,并把所有的段寄存器指向 PSP,實(shí)際上,由于 .COM 文件是以長度小于 64K 為要求設(shè)計(jì)的,所有的程序,數(shù)據(jù)包括 PSP
都在同一段內(nèi)。對(duì)于 .EXE 文件,加載后把程序的 DS 和 ES 指向 PSP 段,若要使用 PSP
,例如,要利用輸入?yún)?shù),可在程序的開始利用 DS或 ES 實(shí)現(xiàn)獲取。當(dāng)然,獲取 PSP 的段址也可以利用以下介紹的系統(tǒng)調(diào)用實(shí)現(xiàn)。
對(duì)編程者而言,在 PSP
中較為有用的是參數(shù)區(qū),參數(shù)是通過在程序執(zhí)行開始時(shí),利用空格與之分隔的字符串,用回車表示結(jié)束,具體的說,就是程序和參數(shù)在同一行輸入。當(dāng)程序加載時(shí),
系統(tǒng)將參數(shù)及參數(shù)長度填入 PSP+80H 開始的參數(shù)區(qū),其中 80H 處是參數(shù)長度,參數(shù)由 81H 開始存放。
二、 程序加載及實(shí)現(xiàn)
所謂 "加載" 就是在 DOS 環(huán)境下,PC 機(jī)從磁盤上將可執(zhí)行文件(即
DOS 下的 .EXE 文件和 .COM 文件)裝入到內(nèi)存的過程。加載的過程包括程序裝入內(nèi)存的分配方案,加載是否成功,裝入后 CPU
各寄存器內(nèi)容及包括的信息等,對(duì)編程者而言,掌握一般的加載后信息分配,在某些應(yīng)用場合中,了解和掌握如何在自己的程序中加載新的程序并使之運(yùn)行的方法是
非常有用的。
我們知道, DOS 下允許用戶鍵入的命令有 DOS 內(nèi)部命令,DOS
外部命令,可執(zhí)行的應(yīng)用程序及批處理文件,對(duì)同名的可執(zhí)行文件按 .COM 到 .EXE 的優(yōu)先順序處理,具體的說,DOS 加載可執(zhí)行文件是通過
EXEC 子功能實(shí)現(xiàn)的 ( 即 INT 21H 的 AH=4BH )。由于 .COM 和 .EXE
文件的結(jié)構(gòu)不同,它們?cè)诩虞d的實(shí)際過程及裝入內(nèi)存方式是不同的,所以,對(duì)待不同的文件應(yīng)有不同的處理方式。
加載 .EXE 文件后,各寄存器的初值設(shè)置
1) DS 和 ES 指向 PSP 的段地址
2) CS 指向代碼段的絕對(duì)段址
3) SS 指向堆棧段的絕對(duì)段址
4) IP 指向代碼段入口時(shí)第一條指令的偏移地址
5) SP 指向堆棧段如口時(shí)深度,此值由文件頭位移 10H 的字域決定
6) BX, CX 是加載程序的字節(jié)長度
加載 .COM 文件后,各寄存器的初值設(shè)置
1) CS,DS,ES 和 SS 指向 PSP 的段地址
2) IP 固定為 100H
3) SP 位 FFFEH ,并在棧頂出壓入一全 0 字
4) BX, CX 是 COM 文件的字節(jié)長度
A DOS 下 EXEC 子功能的應(yīng)用
B 加載程序的設(shè)計(jì)和實(shí)現(xiàn)
三、重定位過程
假如說在生成的exe文件頭里的重定位表中有一項(xiàng)是0000:0030,0000H是高字
段,0030H是低字段。用高字段加上起始段值,也就是exe文件加載到內(nèi)存里的起始地址段(假如是1000H),則結(jié)果為1000H,再加上低字段作為
偏移量,則結(jié)果為10030H,這個(gè)20位的地址指向內(nèi)存地址為10030H的單元。把指向的這個(gè)地址里的字取出來(假設(shè)是0010H),加上起始段值,
再放回去。這就完成了一次重定位。即10030H里存放的是1010H。
四、在DOS下,重定位指的是重新定位在你的程序中所引用的段值。
例如,在你的程序(Large Mode)中,有一處需要調(diào)用C的庫函數(shù),你的程序編譯后可能是下面的樣子:
0000:0000 9A78563412 call far 1234:5678
~~~~ ~~~~
其中,下劃線的段值部分,就是需要在程序加載時(shí)重新定位的。
程序加載器所作的重定位工作,就是將程序中需要重定位的地方,都加上程序的加載地址。
還是上面的例子,假如這個(gè)程序被加載到了內(nèi)存中的1111段處。那么完成重定位后,代碼應(yīng)該是這樣:
1111:0000 9A78564523 call far 2345:5678
~~~~ ~~~~
OK,DOS下的重定位就是這么簡單。那么,程序加載器如何找到重定位項(xiàng)呢?這就需要EXE文件頭中的
重定位表的幫助。
重定位表是一個(gè)非常簡單的4字節(jié)結(jié)構(gòu)數(shù)組:
typedef struct tagRTABLE
{
WORD wOffset
WORD wSegment
}RELOCATIONTABLE, * PRELOCATIONTABLE;
編譯器(或自己手工)先計(jì)算程序中需要重定位的地方在文件中的偏移量,將此偏移量減去文件頭的大小,
然后換算為 Segment:Offset 的形式,填入EXE文件頭中的重定位表,就完成了一個(gè)重定位項(xiàng)的建立。
還是上面的例子,這個(gè)重定項(xiàng)在重定位表中應(yīng)該是這個(gè)樣子:
.
.
.
---------
| 0003 |
---------
| 0000 |
---------
五、exe文件結(jié)構(gòu)
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文件包含一個(gè)文件頭和一個(gè)可重定位程序映象.文件頭包含MS-DOS
用于加載程序的信息,例如程序的大小和寄存器的初始值.文件頭還指向一個(gè)
重定位表,該表包含指向程序映象中可重定位段地址的指針鏈表.文件頭的形
式與EXEHEADER結(jié)構(gòu)對(duì)應(yīng):
EXEHEADER STRUC
exSignature dw 5A4Dh ;.EXE標(biāo)志
exExraBytes dw ? ;最后(部分)頁中的字節(jié)數(shù)
exPages dw ? ;文件中的全部和部分頁數(shù)
exRelocItems dw ? ;重定位表中的指針數(shù)
exHeaderSize dw ? ;以字節(jié)為單位的文件頭大小
exMinAlloc dw ? ;最小分配大小
exMaxAlloc dw ? ;最大分配大小
exInitSS dw ? ;初始SS值
exInitSP dw ? ;初始SP值
exChechSum dw ? ;補(bǔ)碼校驗(yàn)值
exInitIP dw ? ;初始IP值
exInitCS dw ? ;初始CS值
exRelocTable dw ? ;重定位表的字節(jié)偏移量
exOverlay dw ? ;覆蓋號(hào)
EXEHEADER ENDS
程序映象,包含處理器代碼和程序的初始數(shù)據(jù),緊接在文件頭之后.它的
大小,以字節(jié)為單位,等于.EXE文件的大小減去文件頭的大小,也等于exHeaderSize
的域的值乘以16.MS-DOS通過把該映象直接從文件拷貝到內(nèi)存加載.EXE程序
然后調(diào)整定位表中說明的可重定位段地址.
定位表是一個(gè)重定位指針數(shù)組,每個(gè)指向程序映象中的可重定位段地址.
文件頭中的exRelocItems域說明了數(shù)組中指針的個(gè)數(shù),exRelocTable域說明了
分配表的起始文件偏移量.每個(gè)重定位指針由兩個(gè)16位值組成:偏移量和段值.
為加載.EXE程序,MS-DOS首先讀文件頭以確定.EXE標(biāo)志并計(jì)算程序映象的
大小,然后它試圖申請(qǐng)內(nèi)存.首先,它計(jì)算程序映象文件的大小加上PSP的大小
再加上EXEHEADER結(jié)構(gòu)中的exMinAlloc域說明的內(nèi)存大小這三者之和,如果總 和超過最大可
用內(nèi)存塊的大小,則MS-DOS停止加載程序并返回一個(gè)出錯(cuò)值.否
則,它計(jì)算程序映象的大小加上PSP的大小再加上EXEHEADER結(jié)構(gòu)中exMaxAlloc
域說明的內(nèi)存大小之和,如果第二個(gè)總和小于最大可用內(nèi)存塊的大小,則MS-DOS 分配計(jì)算得到的內(nèi)存量.否則,它分配最大可用內(nèi)存塊.
分配完內(nèi)存后,MS-DOS確定段地址;也稱為起始段地址,MS-DOS從此處加載
程序映象.如果exMinAlloc域和exMaxAlloc域中的值都為零,則MS-DOS把映象
盡可能地加載到內(nèi)存最高端.否則,它把映象加載到緊挨著PSP域之上. 接下來,MS-DOS讀取重定位表中的項(xiàng)目調(diào)
整所有由可重定位指針說明的段 地址.對(duì)于重定位表中的每個(gè)指針,MS-DOS尋找程序映象中相應(yīng)的可重定位段
地址,并把起始段地址加到它之上.一旦調(diào)整完畢,段地址便指向了內(nèi)存中被加 載程序的代碼和數(shù)據(jù)段.
MS-DOS在所分配內(nèi)存的最低部分建造256字節(jié)的PSP,把AL和AH設(shè)置為加載
.COM程序時(shí)所設(shè)置的值.MS-DOS使用文件頭中的值設(shè)置SP與SS,調(diào)整SS初始值,
把起始地址加到它之上.MS-DOS還把ES和DS設(shè)置為PSP的段地址.
最后,MS-DOS從程序文件頭讀取CS和IP的初始值,把起始段地址加到CS之 上,把控制轉(zhuǎn)移到位于調(diào)整后地址處的程序.