_IMAGE_DOS_HEADER = packed record      { DOS .EXE header                  }
      e_magic: Word;                     { Magic number                     }
      e_cblp: Word;                      { Bytes on last page of file       }
      e_cp: Word;                        { Pages in file                    }
      e_crlc: Word;                      { Relocations                      }
      e_cparhdr: Word;                   { Size of header in paragraphs     }
      e_minalloc: Word;                  { Minimum extra paragraphs needed  }
      e_maxalloc: Word;                  { Maximum extra paragraphs needed  }
      e_ss: Word;                        { Initial (relative) SS value      }
      e_sp: Word;                        { Initial SP value                 }
      e_csum: Word;                      { Checksum                         }
      e_ip: Word;                        { Initial IP value                 }
      e_cs: Word;                        { Initial (relative) CS value      }
      e_lfarlc: Word;                    { File address of relocation table }
      e_ovno: Word;                      { Overlay number                   }
      e_res: array [0..3] of Word;       { Reserved words                   }
      e_oemid: Word;                     { OEM identifier (for e_oeminfo)   }
      e_oeminfo: Word;                   { OEM information; e_oemid specific}
      e_res2: array [0..9] of Word;      { Reserved words                   }
      _lfanew: LongInt;                  { File address of new exe header   }
  end;

Magic number :用于標識可執行文件的類型,如下常量表示不同的類型

$5A4D;      // magic number for a DOS executable
  cNEMagic = $454E;       // magic number for a NE executable (Win 16)
  cPEMagic = $4550;       // magic nunber for a PE executable (Win 32)
  cLEMagic = $454C;       // magic number for a Virtual Device Driver
Bytes on last page of file : 文件在最后一個頁面的字節數

Pages in file : 文件所占用的頁面數

Relocations : 需要重定位的segment數目

size of header in paragraphs : dos文件頭占用大小

Minimum extra paragraphs needed : 需要額外的最少段數目

Maximum extra paragraphs needed : 需要額外的最大段數目

Initial (relative) SS value : 堆棧段寄存器的初始值

Initial (relative) sp value :堆棧寄存器的初始值

Checksum : 校驗和

 

Initial IP value : 初始的指令寄存器值

File address of relocation table: 重定位表,在早期的16位系統中,為使得尋址能力達到20位,以滿足可訪問更多的空間,搭配使用了段(segment)。這里記錄了指向代碼段(code)、數據段(data)的地址,但這些地址并不是絕對、固定的。因為代碼段和數據段等,只有在被加載到內存時,才能他們的實際地址。當程序被加載到內存後,exe loader會通過重定位表,再修正segment中的相對值(偏移量),以正確定位到代碼段及數據段的真實地址。

   隨著32系統的到來,windows exe被設計為遵循PE的格式,這時的重定位表就不再被使用了。這是因為虛擬地址的出現——在虛擬空間里,Exe可以被加載到任意的地址,程序總是被以虛擬空間的基址加載,所以也就不要調整重定位表了。然而,在dll文件里,重定位表,仍是需要的,因為會被加載到已存在進程地址空間中,不再已既定的基址加載,所以需要矯正表中的偏移值。

Overlay number :交叠数

///以下收集

 


一、简介

    PE文件最前面是一个DOS可执行文件(STUB),这使PE文件成为一个合法的MS-DOS可执行
文件。
    DOS文件头后面是一个32位的PE文件标志0X00004550(IMAGE_NT_SIGNATURE)。
    接着就是PE的文件头了,包含的信息有该程序运行平台、有多少段(sections)、文件
链接的时间、它是一个可执行文件(EXE)还是一个动态链接库(DLL)或是其他。
    后面紧接着有一个“可选”头部(这个部分总是存在,但是因为COFF在库(Libraries)
中用了这个词,在一可执行模块中并没有用这个词,但是仍被叫做可选的)。这可部分包含程
序加载的更多的信息:开始地址、保留堆栈数量、数据段大小等等。
    可选头中还有一个重要的域是一叫做“数据目录表”(data directories)的数组;表
中的每一项是一个指向某一个段的指针。例如:如果某程序有一个输出目录表(export dire
ctory ),那你就会在数据目录表中找到一个为IMAGE_DIRECTORY_ENTRY_EXPORT的指针,并且
它将指向某一个段。
    可选头的下面就是“段”(sections)了,通过一个叫做“段头”(section headers)
的结构索引。实际上,段的内容才是你要真正执行的程序,上面介绍的所有的文件头及目录表
等信息就是为了能正确的找到它。
    每一个段都有一些有关的标志,例如它包含什么数据(“初始化数据”或其他),它能
否被共享等,及它数据本身的特征。大多数情况下(并不是全部),每个段会被一个或多个目
录表指向,目录表可通过可选头的“数据目录表”的入口找到,就象输出函数表或基址重定位
表。也有没有目录表指向的段,如可执行代码或初始化数据。
    整个文件结构如下:
    +-------------------+
    | DOS-stub          |
    +-------------------+
    | file-header       |
    +-------------------+
    | optional header   |
    |- - - - - - - - - -|
    |                   |
    | data directories  |
    |                   |
    +-------------------+
    |                   |
    | section headers   |
    |                   |
    +-------------------+
    |                   |
    | section 1         |
    |                   |
    +-------------------+
    |                   |
    | section 2         |
    |                   |
    +-------------------+
    |                   |
    | ...               |
    |                   |
    +-------------------+
    |                   |
    | section n         |
    |                   |
    +-------------------+

    下面介绍一下相关虚拟地址(Relative Virtual Addresses)
    PE格式文件中经常用到RVA,即相关虚拟地址,用在不知道基地址的情况下表示一个内存
地址。它需要加上基地址才能得到线性地址(Linear address)。
    例如:假设一个可执行程序调入内存0x400000处并且程序从RVA 0x1560处开始执行。那
么正确的开始地址是0x401560。如果可执行程序调入0x100000处,则开始地址为0x101560。
    因为PE文件的每一个段不必按同样的边界对齐方式调入,因此RVA地址的计算变得比较复
杂。例如,在文件中每一个段往往按512个字节的方式对齐,而在内存中可能以4096字节的方
式对齐。这方面的介绍可见下面的“SectionAlignment”、“FileAlignment”。举个例子,
假设你知道一个程序从RVA 0x1560开始执行,你想从那儿反汇编它。你发现内存中的段对齐方
式为4096并且.code段开始于内存RVA 0x1560并且有16384字节长;那么你可以知道RVA 0x156
0在这个段的0x560处。你又发现这个段在文件中以512字节方式对齐并且.code开始于文件0x8
00处,那现在你知道了可执行程序开始于0x800+0x560 = 0xd60处。

二、DOS头(DOS-stub )
    众所周知DOS头的概念是从16位的WINDOWS可执行程序(NE格式)中来的,这个部分主要
用在OS/2可执行程序、自解压文档及其他应用程序。在PE格式文件中,大多数程序的这个部分
中只有大约100个字节的代码,只输出一个诸如“this program needs windows NT ”之类的
信息。
    你可以通过一个叫做IMAGE_DOS_HEADER的结构来识别一个合法的DOS头。这个结构的头两
个字节一定是“MZ”(#define IMAGE_DOS_SIGNATURE "MZ")。怎么才能找到PE开始的标志呢
?你可以通过该结构的一个叫做“e_lfanew”(offset 60,32bits) 的成员来找到它。在O
S/2及16位WINDOWS程序中这个标志是一个16位的字;在PE程序中,它是一个32位的双字,值为
0x00004550(#define IMAGE_NT_SIGNATURE 0x00004550)。

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

三、文件头(File Header)
    通过DOS头,你可以找到一个叫做IMAGE_FILE_HEADER的结构,如下;下面我分别介绍一
下。

 typedef struct _IMAGE_FILE_HEADER {
     WORD    Machine;    //0x04
     WORD    NumberOfSections;  //0x06
     DWORD   TimeDateStamp;   //0x08
     DWORD   PointerToSymbolTable;  //0x0c
     DWORD   NumberOfSymbols;  //0x10
     WORD    SizeOfOptionalHeader;  //0x14
     WORD    Characteristics;  //0x16
 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

    Machine:表示该程序要执行的环境及平台,现在已知的值如下:
        IMAGE_FILE_MACHINE_I386(0x14c)
            Intel 80386  处理器以上
        0x014d
            Intel 80486 处理器以上
        0x014e
            Intel Pentium 处理器以上
        0x0160
            R3000(MIPS)处理器,高位在前
        IMAGE_FILE_MACHINE_R3000(0x162)
            R3000(MIPS)处理器,低位在前
        IMAGE_FILE_MACHINE_R4000(0x166)
            R4000(MIPS)处理器,低位在前
        IMAGE_FILE_MACHINE_R10000(0x168)
            R10000(MIPS)处理器,低位在前
        IMAGE_FILE_MACHINE_ALPHA(0x184)
            DEC Alpha AXP处理器
        IMAGE_FILE_MACHINE_POWERPC(0x1f0)
            IBM Power PC,低位在前
    NumberOfSections:段的个数,段的概念我们将在下面介绍。
    TimeDateStamp:文件建立的时间。你可用这个值来区分同一个文件的不同的版本,即使
它们的商业版本号相同。这个值的格式并没有明确的规定,但是很显然的大多数的C编译器都
把它定为从1970.1.1 00:00:00以来的秒数(time_t )。这个值有时也被用做绑定输入目录表
,这将在下面介绍。
        注意:一些编译器将忽略这个值。
    PointerToSymbolTable 及 NumberOfSymbols:用在调试信息中,我不太清楚它们的用途
,不过发现它们总为0。
    SizeOfOptionalHeader:可选头的长度(sizeof IMAGE_OPTIONAL_HEADER)你可以用它
来检验PE文件的正确性。
    Characteristics:是一个标志的集合,其中大部分的位用在目标文件(OBJ)或库文件
(LIB)中:
        Bit 0 (IMAGE_FILE_RELOCS_STRIPPED):置1表示文件中没有重定向信息。每个段都
有它们自己的重定向信息。这个标志在可执行文件中没有使用,在可执行文件中是用一个叫做
基址重定向目录表来表示重定向信息的,这将在下面介绍。
        Bit 1 (IMAGE_FILE_EXECUTABLE_IMAGE):置1表示该文件是可执行文件(也就是说
不是一个目标文件或库文件)。
        Bit 2 (IMAGE_FILE_LINE_NUMS_STRIPPED):置1表示没有行数信息;在可执行文件
中没有使用。
        Bit 3 (IMAGE_FILE_LOCAL_SYMS_STRIPPED):置1表示没有局部符号信息;在可执行
文件中没有使用。
        Bit 4 (IMAGE_FILE_AGGRESIVE_WS_TRIM):
        Bit 7 (IMAGE_FILE_BYTES_REVERSED_LO)
        Bit 15 (IMAGE_FILE_BYTES_REVERSED_HI):表示文件的字节顺序如果不是机器所期
望的,那么在读出之前要进行交换。在可执行文件中它们是不可信的(操作系统期望按正确的
字节顺序执行程序)。
        Bit 8 (IMAGE_FILE_32BIT_MACHINE):表示希望机器为32位机。这个值永远为1。
        Bit 9 (IMAGE_FILE_DEBUG_STRIPPED):表示没有调试信息,在可执行文件中没有使
用。
        Bit 10 (IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP):置1表示该程序不能运行于可移
动介质中(如软驱或CD-ROM)。在这种情况下,OS必须把文件拷贝到交换文件中执行。
        Bit 11 (IMAGE_FILE_NET_RUN_FROM_SWAP):置1表示程序不能在网上运行。在这种
情况下,OS必须把文件拷贝到交换文件中执行。
        Bit 12 (IMAGE_FILE_SYSTEM):置1表示文件是一个系统文件例如驱动程序。在可执
行文件中没有使用。
        Bit 13 (IMAGE_FILE_DLL):置1表示文件是一个动态链接库(DLL)。
        Bit 14 (IMAGE_FILE_UP_SYSTEM_ONLY):表示文件被设计成不能运行于多处理器系
统中。

四、可选头(Optional Header)
    文件头下面就是可选头,这是一个叫做IMAGE_OPTIONAL_HEADER的结构。它包含很多关于
PE文件定位的信息。下面分别介绍:
    typedef struct _IMAGE_OPTIONAL_HEADER {
        //
        // Standard fields.
        //
        WORD    Magic;    //0x18
        BYTE    MajorLinkerVersion;  //0x1a
        BYTE    MinorLinkerVersion;  //0x1b
        DWORD   SizeOfCode;    //0x1c
        DWORD   SizeOfInitializedData;  //0x20
        DWORD   SizeOfUninitializedData;  //0x24
        DWORD   AddressOfEntryPoint;  //0x28
        DWORD   BaseOfCode;    //0x2c
        DWORD   BaseOfData;    //0x30
        //
        // NT additional fields.
        //
        DWORD   ImageBase;    //0x34
        DWORD   SectionAlignment;   //0x38
        DWORD   FileAlignment;   //0x3c
        WORD    MajorOperatingSystemVersion; //0x3e
        WORD    MinorOperatingSystemVersion; //0x40
        WORD    MajorImageVersion;   //0x42
        WORD    MinorImageVersion;   //0x44
        WORD    MajorSubsystemVersion;  //0x46
        WORD    MinorSubsystemVersion;  //0x48
        DWORD   Win32VersionValue;   //0x4c
        DWORD   SizeOfImage;   //0x50
        DWORD   SizeOfHeaders;   //0x54
        DWORD   CheckSum;    //0x58
        WORD    Subsystem;    //0x5c
        WORD    DllCharacteristics;  //0x5e
        DWORD   SizeOfStackReserve;  //0x60
        DWORD   SizeOfStackCommit;   //0x64
        DWORD   SizeOfHeapReserve;   //0x68
        DWORD   SizeOfHeapCommit;   //0x6c
        DWORD   LoaderFlags;   //0x70
        DWORD   NumberOfRvaAndSizes;  //0x74
        IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
    } IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

    Magic:这个值好象总是0x010b。
    MajorLinkerVersion及MinorLinkerVersion:链接器的版本号,这个值不太可靠。
    SizeOfCode:可执行代码的长度。
    SizeOfInitializedData:初始化数据的长度(数据段)。
    SizeOfUninitializedData:未初始化数据的长度(bss段)。
    AddressOfEntryPoint:代码的入口RVA地址,程序从这儿开始执行。
    BaseOfCode:可执行代码起始位置,意义不大。
    BaseOfData:初始化数据起始位置,意义不大。
    ImageBase:载入程序首选的RVA地址。这个在址可被Loader改变。
    SectionAlignment:段加载后在内存中的对齐方式。
    FileAlignment:段在文件中的对齐方式。
    MajorOperatingSystemVersion及MinorOperatingSystemVersion:操作系统版本,Load
er并没有用它。
    MajorImageVersion及MinorImageVersion:程序版本。
    MajorSubsystemVersion及MinorSubsystemVersion:子系统版本号,这个域系统支持;
例如:如果程序运行于NT下,子系统版本号如果不是4.0的话,对话框不能显示3D风格。
    Win32VersionValue:这个值好象总是为0。
    SizeOfImage:程序调入后占用内存大小(字节),等于所有段的长度之和。
    SizeOfHeaders:所有文件头的长度之和,它等于从文件开始到第一个段的原始数据之间
的大小。
    CheckSum:校验和。它仅用在驱动程序中,在可执行文件中可能为0。它的计算方法Mic
rosoft不公开,在imagehelp.dll中的CheckSumMappedFile()函数可以计算它。
    Subsystem:NT子系统,可能是以下的值:
        IMAGE_SUBSYSTEM_NATIVE (1)
            不需要子系统。用在驱动程序中。
        IMAGE_SUBSYSTEM_WINDOWS_GUI(2)
            WIN32 graphical程序(它可用AllocConsole()来打开一个控制台,但是不能在
一开始自动得到)。
        IMAGE_SUBSYSTEM_WINDOWS_CUI(3)
            WIN32 console程序(它可以一开始自动建立)。
        IMAGE_SUBSYSTEM_OS2_CUI(5)
            OS/2 console程序(因为程序是OS/2格式,所以它很少用在PE)。
        IMAGE_SUBSYSTEM_POSIX_CUI(7)
            POSIX console程序。
        Windows95程序总是用WIN32子系统,所以只有2和3是合法的值。
    DllCharacteristics:Dll状态。
    SizeOfStackReserve:保留堆栈大小。
    SizeOfStackCommit:启动后实际申请的堆栈数,可随实际情况变大。
    SizeOfHeapReserve:保留堆大小。
    SizeOfHeapCommit:实际堆大小。
    LoaderFlags:好象没有用。
    NumberOfRvaAndSizes:下面的目录表入口个数,这个值也不可靠,你可用常数IMAGE_N
UMBEROF_DIRECTORY_ENTRIES来代替它,值好象总等于16。
    DataDirectory:是一个IMAGE_DATA_DIRECTORY数组,数组元素个数为IMAGE_NUMBEROF_
DIRECTORY_ENTRIES,结构如下:
        typedef struct _IMAGE_DATA_DIRECTORY {
            DWORD   VirtualAddress;
            DWORD   Size;
        } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
        VirtualAddress:起始RVA地址。
        Size:长度。
    每一个目录表代表以下的值:
    IMAGE_DIRECTORY_ENTRY_EXPORT (0)
    IMAGE_DIRECTORY_ENTRY_IMPORT (1)
    IMAGE_DIRECTORY_ENTRY_RESOURCE (2)
    IMAGE_DIRECTORY_ENTRY_EXCEPTION (3)
    IMAGE_DIRECTORY_ENTRY_SECURITY (4)
    IMAGE_DIRECTORY_ENTRY_BASERELOC (5)
    IMAGE_DIRECTORY_ENTRY_DEBUG (6)
    IMAGE_DIRECTORY_ENTRY_COPYRIGHT (7)
    IMAGE_DIRECTORY_ENTRY_GLOBALPTR (8)
    IMAGE_DIRECTORY_ENTRY_TLS (9)
    IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG (10)
    IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (11)
    IMAGE_DIRECTORY_ENTRY_IAT (12)