Exe文件裝載過程

 

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)整后地址處的程序.