PE格式分析一

Scroll Down

PE文件格式

PEStruct

PE的一些基本概念

名称描述
地址是“虚拟地址”而不是“物理地址”。为什么不是“物理地址”呢?因为数据在内存的位置经常在变,这样可以节省内存开支、避开错误的内存位置等的优势。同时用户并不需要知道具体的“真实地址”,因为系统自己会为程序准备好内存空间的(只要内存足够大)
镜像文件包含以EXE文件为代表的“可执行文件”、以DLL文件为代表的“动态链接库”。为什么用“镜像”?这是因为他们常常被直接“复制”到内存,有“镜像”的某种意思。看来西方人挺有想象力的哦0
RVA英文全称Relatively Virtual Address。偏移(又称“相对虚拟地址”)。相对镜像基址的偏移。
节是PE文件中代码或数据的基本单元。原则上讲,节只分为“代码节”和“数据节”。
VA英文全称Virtual Address。基址

PE地址概念

虚拟地址VA = 基地址(ImageBase)+ 相对虚拟地址(RVA)

IMAGE_DOS_HEADER

一共有64个字节,一个WORD两个字节,一个LONG四个字节。

typedef struct _IMAGE_DOS_HEADER {
    WORD   e_magic;                                     // [0x00] Magic number(重要,是否为PE文件的第一个标志)
    WORD   e_cblp;                                       // [0x02]Bytes on last page of file
    WORD   e_cp;                                         // [0x04]Pages in file
    WORD   e_crlc;                                       // [0x06]Relocations
    WORD   e_cparhdr;                                   // [0x08]Size of header in paragraphs
    WORD   e_minalloc;                                  //[0x0A] Minimum extra paragraphs needed
    WORD   e_maxalloc;                                  //[0x0C] Maximum extra paragraphs needed
    WORD   e_ss;                                          // [0x0E]Initial (relative) SS value
    WORD   e_sp;                                          // [0x10]Initial SP value
    WORD   e_csum;                                       // [0x12]Checksum
    WORD   e_ip;                                          //[0x14] Initial IP value
    WORD   e_cs;                                          //[0x16] Initial (relative) CS value
    WORD   e_lfarlc;                                      // [0x18]File address of relocation table
    WORD   e_ovno;                                       // [0x1A]Overlay number
    WORD   e_res[4];                                     // [0x1C]Reserved words
    WORD   e_oemid;                                      // [0x24]OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                                    //[0x26] OEM information; e_oemid specific
    WORD   e_res2[10];                                  // [0x28]Reserved words
    LONG   e_lfanew;                                     // [0x3C]File address of new exe header(有用,PE解析时用它找到PE头的位置)
} IMAGE_DOS_HEADER, * PIMAGE_DOS_HEADER;

重要数据的是第一个和最后一个,也就是e_magic与e_lfanew这两个数据成员,e_magic翻译为魔数,其实它就是一个标记,DOS头标志位,其值恒为4D5A,在系统中用宏定义为:IMAGE_DOS_SIGNATURE,e_lfanew,它表示NT头部在文件中的偏移。

IMAGE_NT_HEADERS32

typedef struct _IMAGE_NT_HEADERS
{
    DWORD Signature;                                   //标记(重要,判断是否为PE文件的第二个标志)
    IMAGE_FILE_HEADER FileHeader;                      //文件头(重要,存储着PE文件的基本信息)
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;  //扩展头(重要,存储着关于PE文件时加载的信息)
} IMAGE_NT_HEADERS32, * PIMAGE_NT_HEADERS32;

IMAGE_FILE_HEADER

typedef struct _IMAGE_FILE_HEADER {

    WORD      Machine;                         1  (文件的运行平台)
  
    WORD      NumberOfSections;               2  (区段的数量)

    DWORD   TimeDateStamp;                     3  (文件创建时间)

    DWORD   PointerToSymbolTable;             4  (符号表偏移,用于调试)

    DWORD   NumberOfSymbols;                 5   (符号个数,用于调试)

    WORD    SizeOfOptionalHeader;            6   (扩展头的大小)

    WORD    Characteristics;                    7   (PE文件的一些属性)

} IMAGE_FILE_HEADER, * PIMAGE_FILE_HEADER;
Machine :01 4C(注意是小端)
值        描述
0x0       适用于任何类型处理器
0x1d3      Matsushita AM33处理器
0x8664    x64处理器
0x1c0    ARM小尾处理器
0xebc    EFI字节码处理器
0x14c    Intel 386或后继处理器及其兼容处理器
0x200    Intel Itanium处理器
0x9041    Mitsubishi M32R小尾处理器
0x266    MIPS16处理器
0x366    带FPU的MIPS处理器
0x466    带FPU的MIPS16处理器
0x1f0    PowerPC小尾处理器
0x1f1    带符点运算支持的PowerPC处理器
0x166    MIPS小尾处理器
0x1a2    Hitachi SH3处理器
0x1a3    Hitachi SH3 DSP处理器
0x1a6    Hitachi SH4处理器
0x1a6    Hitachi SH5处理器
0x1c2    Thumb处理器
0x169    MIPS小尾WCE v2处理器

IMAGE_OPTIONAL_HEADER32

typedef struct _IMAGE_OPTIONAL_HEADER {

    //

    // Standard fields.

    //

    WORD    Magic;                                 1    文件类型标识,32位一般是0x010B,64位的PE文件一般是0x020B,还有0x0170,代表ROM镜像。

    BYTE    MajorLinkerVersion;                    2    连接器主版本

    BYTE    MinorLinkerVersion;                    3    连接器次版本

    DWORD   SizeOfCode;                            4   (重要)指所有代码区段(节)的总大小

    DWORD   SizeOfInitializedData;                 5    已初始化数据的总大小

    DWORD   SizeOfUninitializedData;               6    未初始化数据的总大小,在磁盘中不占用空间,在加载进内存之后,会预留这么大的空间。一般存储在.bss区段中。

    DWORD   AddressOfEntryPoint;                   7   (重要)程序开始执行的相对虚拟地址(RVA),也叫OEP,Orginal Entry Point ,源入口点。

    DWORD   BaseOfCode;                            8   (重要)起始代码的相对虚拟地址(RVA),一般这个值为0x00001000.

    DWORD   BaseOfData;                            9    起始数据的相对虚拟地址(RVA)

    //

    // NT additional fields.

    //

    DWORD   ImageBase;                             10  (重要)默认加载基址(如果没有加载到这个地址,会发生重定位.)

    DWORD   SectionAlignment;                      11  (重要)块对齐数,就是在映射到内存中的区段(节)对齐,这个数必须大于文件对齐数,一般是0x1000

    DWORD   FileAlignment;                         12  (重要)文件对齐数,就是在硬盘中的文件的区段(节)对齐,一般是0x200

    WORD    MajorOperatingSystemVersion;           13   主操作系统版本号

    WORD    MinorOperatingSystemVersion;           14   次操作系统版本号

    WORD    MajorImageVersion;                     15   主映像版本

    WORD    MinorImageVersion;                     16   次映像版本

    WORD    MajorSubsystemVersion;                 17   主子系统版本

    WORD    MinorSubsystemVersion;                 18   次子系统版本

    DWORD   Win32VersionValue;                     19   保留值,一般是0

    DWORD   SizeOfImage;                           20  (重要)要把文件加载进内存,所需要的内存大小,注意是进行了块对齐之后

    DWORD   SizeOfHeaders;                         21   所有头部大小,Dos头、PE头、区段表的尺寸之和

    DWORD   CheckSum;                              22   校验和(一般无用)对于驱动和一些系统dll来说需要校验(使用IMAGEHLP.DLL中的CheckSumMappedFile API)

    WORD    Subsystem;                             23  (重要)子系统值

    WORD    DllCharacteristics ;                   24  (重要)指示Dll特征的标志,DllMain()函数何时被调用,默认为0.

    DWORD   SizeOfStackReserve;                    25   初始化时栈的大小

    DWORD   SizeOfStackCommit;                     26   初始化时实际提交的栈的大小

    DWORD   SizeOfHeapReserve;                     27   初始化时保留的堆的大小

    DWORD   SizeOfHeapCommit;                      28   初始化时实际提交的堆的大小

    DWORD   LoaderFlags;                           29   与调试相关

    DWORD   NumberOfRvaAndSizes;                   30   数据目录的个数,也就是下面那个数组中元素的个数。

    IMAGE_DATA_DIRECTORY DataDirectory[ IMAGE_NUMBEROF_DIRECTORY_ENTRIES];   31  (非常重要)数据目录表

} IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32;

IMAGE_DATA_DIRECTORY

typedef struct _IMAGE_DATA_DIRECTORY
{

    DWORD   VirtualAddress;     // 数据的相对虚拟地址(RVA)

    DWORD   Size;               // 数据的大小

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

IMAGE_SECTION_HEADER

typedef struct _IMAGE_SECTION_HEADER
{
  +0 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节表名称,如“.text”
  //IMAGE_SIZEOF_SHORT_NAME=8
  +8 union
  {
    DWORD PhysicalAddress; // 物理地址
    DWORD VirtualSize; // 真实长度,这两个值是一个联合结构,可以使用其中的任何一个,一
    // 般是取后一个
  } Misc;
  +ch  DWORD VirtualAddress; // 节区的 RVA 地址
  +10h DWORD SizeOfRawData; // 在文件中对齐后的尺寸
  +14h DWORD PointerToRawData; // 在文件中的偏移量
  +18h DWORD PointerToRelocations; // 在OBJ文件中使用,重定位的偏移
  +1ch DWORD PointerToLinenumbers; // 行号表的偏移(供调试使用地)
  +1eh WORD NumberOfRelocations; // 在OBJ文件中使用,重定位项数目
  +20h WORD NumberOfLinenumbers; // 行号表中行号的数目
  +24h DWORD Characteristics; // 节属性如可读,可写,可执行等
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;