PE文件结构整理

时间:2022-11-26 11:52:48

  一直想做一个PE结构的总结,只是学的时候有很多东西就没搞懂,加上时间一长,很多知识也早忘了,也就一直没完成。这几天从头看了下,好不容易理清楚了,整理一下,以免又忘了

pe文件框架结构,图片贴过来太模糊了就画个表格代替一下

DOS文件头 PE文件头  区块表    各区块    调试信息  

  DOS文件头

DOS文件头包括DOS MZ头和DOS stub

MS-DOS头部是一个IMAGE_DOS_HEADER结构(代码来自windows.inc)

IMAGE_DOS_HEADER STRUCT
  +0h        e_magic         WORD      ?     ;DOS可执行文件标记"MZ"
  +2h        e_cblp          WORD      ?
  +4h        e_cp            WORD      ?
  +6h        e_crlc          WORD      ?
  +8h        e_cparhdr       WORD      ?
  +0a        e_minalloc      WORD      ?
  +0c        e_maxalloc      WORD      ?
  +0e        e_ss            WORD      ?
  +10        e_sp            WORD      ?
  +12        e_csum          WORD      ?
  +14        e_ip            WORD      ?
  +16        e_cs            WORD      ?
  +18        e_lfarlc        WORD      ?
  +1a        e_ovno          WORD      ?
  +1c        e_res           WORD      4 dup(?)
  +24        e_oemid         WORD      ?
  +26        e_oeminfo       WORD      ?
  +28        e_res2          WORD     10 dup(?)
  +3c        e_lfanew        DWORD     ?    ;指向PE文件头
IMAGE_DOS_HEADER ENDS

此结构共64字节

这里面只有第一个和最后一个字段较为重要,e_magic字段被设置为5A4Dh,其ASCII值为"MZ"(4D5Ah)<小端存储>

偏移为3ch的e_lfanew字段指出真正的PE头的文件偏移位置

接下来的DOS stub(DOS块)实际上是一个有效的EXE,在DOS下显示一个错误提示

  PE文件头

PE文件头是一个IMAGE_NT_HEADERS结构

IMAGE_NT_HEADERS STRUCT
  +0h      Signature         DWORD                   ?   ;PE文件表示
  +4h      FileHeader        IMAGE_FILE_HEADER       <>  ;映像文件头,包含了PE文件的一些基本信息
  +18h     OptionalHeader    IMAGE_OPTIONAL_HEADER32 <>  ;可选映像头,补充PE文件属性
IMAGE_NT_HEADERS ENDS

对于32位PE文件,此结构通常为248字节

对于Signature的定义为

IMAGE_NT_SIGNATURE equ 00004550h

其ASCII值为:"PE00"(50450000h)

后面的两个结构最重要的就是偏移78h的数据目录表

IMAGE_FILE_HEADER结构

IMAGE_FILE_HEADER STRUCT
  +04h  Machine               WORD    ?    ;可执行文件的目标CPU类型,14Ch为Intel i386
  +06h  NumberOfSections      WORD    ?    ;区块的数目
  +08h  TimeDateStamp         DWORD   ?  ;文件创建日期和时间
  +0ch  PointerToSymbolTable  DWORD   ?  ;COFF符号表的文件偏移位置
  +10h  NumberOfSymbols       DWORD   ?  ;符号个数
  +14h  SizeOfOptionalHeader  WORD    ?  ;IMAGE_OPTIONAL_HEADER32结构的大小,32位PE文件通常是E0h,64位PE32+是F0h
  +16h  Characteristics       WORD    ?  ;文件属性
IMAGE_FILE_HEADER ENDS

 IMAGE_OPTIONAL_HEADER32结构

IMAGE_OPTIONAL_HEADER32 STRUCT
  +18h  Magic                         WORD       ?  ;标志字,ROM映像(0107h)普通可执行映像(010Bh)PE32+(020Bh)
  +1Ah  MajorLinkerVersion            BYTE       ?  ;链接程序的主版本号
  +1Bh  MinorLinkerVersion            BYTE       ?  ;链接程序的次版本号
  +1Ch  SizeOfCode                    DWORD      ?  ;所有带有IMAGE_SCN_CNT_CODE属性区块的总大小
  +20h  SizeOfInitializedData         DWORD      ?  ;已初始化数据块的大小
  +24h  SizeOfUninitializedData       DWORD      ?  ;未初始化数据块的大小(通常在.bss块中)
  +28h  AddressOfEntryPoint           DWORD      ?  ;程序执行入口RVA(这个地址并不直接指向Main、WinMain、DllMain,而是指向运行库代码并由它来调用上述函数)
  +2Ch  BaseOfCode                    DWORD      ? ;代码段的起始RVA(Microsoft链接器生成的执行文件通常是1000h)<内存中代码段通常在PE文件头之后、数据块之前>
  +30h  BaseOfData                    DWORD      ?  ;数据段的起始RVA。这个域的值对于不同版本的微软链接器是不一致的,在64位可执行文件中是不出现的
  +34h  ImageBase                     DWORD      ?  ;文件在内存中的首选装入地址
  +38h  SectionAlignment              DWORD      ?  ;内存中的区块对齐值
  +3Ch  FileAlignment                 DWORD      ?  ;磁盘上区块的对齐值
  +40h  MajorOperatingSystemVersion   WORD       ?  ;操作系统最低主版本号
  +42h  MinorOperatingSystemVersion   WORD       ?  ;操作系统最低次版本号
  +44h  MajorImageVersion             WORD       ?  ;用户自定义主版本号
  +46h  MinorImageVersion             WORD       ?  ;用户自定义次版本号
  +48h  MajorSubsystemVersion         WORD       ?  ;要求最低子系统版本主版本号
  +4Ah  MinorSubsystemVersion         WORD       ?  ;要求最低子系统版本次版本号
  +4Ch  Win32VersionValue             DWORD      ?  ;不用字段,常设为0
  +50h  SizeOfImage                   DWORD      ?  ;映像装入内存后的总尺寸
  +54h  SizeOfHeaders                 DWORD      ?  ;MS-DOS头部、PE头部、区块表的组合尺寸。域值四舍五入至文件对齐的倍数
  +58h  CheckSum                      DWORD      ?  ;映像校验和,链接器的/RELEASE开关被使用时,校验和被置于文件中
  +5Ch  Subsystem                     WORD       ?  ;标明可执行文件所期望的子系统(用户界面类型)的枚举值
  +5Eh  DllCharacteristics            WORD       ?  ;DLL特性旗标,DllMain()函数何时被调用,默认为0
  +60h  SizeOfStackReserve            DWORD      ?  ;为线程保留的堆栈大小,它一开始只提交其中一部分,只有在必要时,才提交剩下的部分
  +64h  SizeOfStackCommit             DWORD      ?  ;一开始即被委派给堆栈的内存数量。默认值是4KB
  +68h  SizeOfHeapReserve             DWORD      ?  ;为进程的默认堆保留的内存。默认值是1MB,在当前Windows里,堆值在用户不干涉的情况下就能增长超过这个值
  +6Ch  SizeOfHeapCommit              DWORD      ?  ;EXE文件里,委派给堆的内存大小。默认值是4KB
  +70h  LoaderFlags                   DWORD      ?  ;与调试有关,默认为0
  +74h  NumberOfRvaAndSizes           DWORD      ?  ;数据目录的项数,从最早的Windows NT发布以来一直是16
  +78h  DataDirectory                 IMAGE_DATA_DIRECTORY IMAGE_NUMBEROF_DIRECTORY_ENTRIES dup(<>)
IMAGE_OPTIONAL_HEADER32 ENDS

这两个结构中有很多常用到的信息,如IMAGE_FILE_HEADER结构中的区块数目, IMAGE_OPTIONAL_HEADER中的ImageBase、SectionAlignment和FileAlignment等字段

  区块表

对于数据目录表,最后再来总结

区块表是一个IMAGE_SECTION_HEADER结构数组,区块的个数由IMAGE_NT_HEADERS.FileHeader.NumberOfSections指出

区块表的后面与区块之间一般是大量的填充位

IMAGE_SECTION_HEADER结构:40字节

IMAGE_SECTION_HEADER STRUCT
    Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)  ;区块名
    union Misc
        PhysicalAddress dd  ?
        VirtualSize dd      ?  ;区块实际大小
    ends
    VirtualAddress dd       ?  ;区块的RVA地址
    SizeOfRawData dd        ?  ;在文件中对齐后的尺寸
    PointerToRawData dd     ?  ;在文件中的偏移
    PointerToRelocations dd ?  ;在OBJ文件中使用,重定位的偏移,指向一个IMAGE_RELOCATION结构数组
    PointerToLinenumbers dd ?  ;行号表在文件中的偏移值,这是文件调试信息
    NumberOfRelocations dw  ?  ;重定位项数目,在EXE文件中无意义
    NumberOfLinenumbers dw  ?  ;该块在行号表中的行号数目
    Characteristics dd      ?  ;块属性
IMAGE_SECTION_HEADER ENDS

此结构常用VirtualAddress和PointerToRawData字段来定位区块,并实现文件偏移与虚拟地址的转换

  数据目录表

数据目录表位于IMAGE_NT_HEADER的78h偏移处,是一个IMAGE_DATA_DIRECTORY结构数组,从最早的Windows NT发布以来项数一直是16

IMAGE_DATA_DIRECTORY结构为:

IMAGE_DATA_DIRECTORY STRUCT
  VirtualAddress    DWORD      ?  ;数据块的起始RVA
  isize             DWORD      ?  ;数据块的大小
IMAGE_DATA_DIRECTORY ENDS

一、数组的第一项指向输出表

输出表是一个IMAGE_EXPORT_DIRECTORY结构

IMAGE_EXPORT_DIRECTORY STRUCT
  Characteristics           DWORD      ?  ;输出属性,目前未定义,总是为0
  TimeDateStamp             DWORD      ?  ;输出表创建时间(GMT时间)
  MajorVersion              WORD       ?  ;输出表主版本号,未使用
  MinorVersion              WORD       ?  ;次版本号,未使用
  nName                     DWORD      ?  ;指向一个ASCII字符串的RVA,这个字符串是与这些输出函数关联的DLL名字
  nBase                     DWORD      ?  ;基数,加上序数就是函数地址数组的索引值
  NumberOfFunctions         DWORD      ?  ;EAT中条目数(输出函数地址表)
  NumberOfNames             DWORD      ?  ;ENT中的条目数(输出函数名称表)
  AddressOfFunctions        DWORD      ?  ;EAT的RVA
  AddressOfNames            DWORD      ?  ;ENT的RVA
  AddressOfNameOrdinals     DWORD      ?  ;输出序数表的RVA
IMAGE_EXPORT_DIRECTORY ENDS

输出表结构

 PE文件结构整理

AddressOfNameOrdinals指向的输出序数表的作用是把EAT和ENT联系起来,当PE装载器在ENT中找到对应函数的位置,再从输出序数表读取相应位置的值,这个值便是在EAT中的偏移,这个值作为进入EAT的索引(这里有一点不明白,看雪加密解密第三版书上说此值作为输出函数的输出序数并且要考虑Base阈值,但是实例中输出序数表中的值是函数在EAT中的偏移)。当通过序数来查询一个输出函数是,减去nBase阈值得到进入EAT的索引

二、数据目录表第二项输入表

输入表以一个IMAGE_IMPORT_DESCRIPTOR(IID)结构数组开始,数组以一个全为0的IID结构结束

IMAGE_IMPORT_DESCRIPTOR STRUCT
    union
        Characteristics dd      ?
        OriginalFirstThunk dd   ?  ;指向输入名称表(INT)的RVA
     ends
    TimeDateStamp dd    ?  ;时间标志
    ForwarderChain dd   ?  ;第一个被转向的API的索引,一般为0
    Name1 dd            ?  ;指向DLL的名字
    FirstThunk dd       ?  ;指向输入地址表(IAT)的RVA
IMAGE_IMPORT_DESCRIPTOR ENDS

INT和IAT都是一个IMAGE_THUNK_DATA结构数组,同样以一个全为0的IMAGE_THUNK_DATA结构作为结束

IMAGE_THUNK_DATA32 STRUCT
    union u1
        ForwarderString dd  ?  ;指向一个转向者字符串的RVA
        Function dd         ?  ;被输入的函数的内存地址
        Ordinal dd          ?  ;被输入的API的序数值
        AddressOfData dd    ?  ;指向IMAGE_IMPORT_BY_NAME
    ends
IMAGE_THUNK_DATA32 ENDS

INT中当IMAGE_THUNK_DATA值得最高位为1是,表示函数以序号方式输入;最高位为0是以函数名的方式输入,此时指向一个IMAGE_IMPORT_BY_NAME结构

IMAGE_IMPORT_BY_NAME结构为:

IMAGE_IMPORT_BY_NAME STRUCT
    Hint dw     ?  ;指出此函数在所驻留的DLL的输出表中的序号,有些连接器将此值设为0
    Name1 db    ?  ;输入函数的函数名,这是一个可变尺寸域
IMAGE_IMPORT_BY_NAME ENDS

PE加载器先同过INT找到相应的函数,再将对应函数的地址重写入IAT,此时程序仅依靠IAT提供的函数地址就可以正常运行了

因为一开始IAT跟INT是一样的,有些链接器就只用一个IAT结构就完成了输入(不知道在哪儿看到过这句话,大概意思就这个)

三、数据目录表第三项资源表

数据目录表中的IMAGE_DIRECTORY_ENTRY_RESOURCE条目包含资源的RVA和大小,资源目录每一节点包含一个IMAGE_RESOURCE_DIRECTORY结构和数个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,IMAGE_RESOURCE_DIRECTORY指出紧跟在后面的IMAGE_RESOURCE_DIRECTORY_ENTRY结构的个数

 

从网上找了张图,这张图对资源目录结构的描述很清晰

PE文件结构整理

IMAGE_RESOURCE_DIRECTORY STRUCT
    Characteristics dd      ?  ;理论上是资源的属性标志,但通常为0
    TimeDateStamp dd        ?  ;资源建立的时间
    MajorVersion dw         ?  ;理论上放置资源的版本,但通常为0
    MinorVersion dw         ?  ;
    NumberOfNamedEntries dw ?  ;使用名字的资源条目的个数
    NumberOfIdEntries dw    ?  ;使用ID数字资源条目的个数
IMAGE_RESOURCE_DIRECTORY ENDS

NumberOfNamedEntries和NumberOfIdEntries加起来的和等于本目录IMAGE_RESOURCE_DIRECTORY_ENTRY的个数

IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT
    union
        rName    RECORD NameIsString:1,NameOffset:31
        Name1 dd ?
        Id dw ?
    ends
    union
        OffsetToData dd ?
          rDirectory    RECORD DataIsDirectory:1,OffsetToDirectory:31
    ends
IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS

  就先总结到这儿吧2015-07-02/17:11:08