Exe文件装载过程

 

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之   上,把控制转移到位于调整后地址处的程序.