PE结构学习笔记(二)
Chinese:
1、每个PE文件是以一个DOS程序开始的,有了它,一旦程序在DOS下执行,DOS才能识别出这是有效的执行体。
2、PE文件的第一个字节起始于一个传统的MS-DOS头部,被称作IMAGE_DOS_HEADER(一个结构体)。这个结构体有两个重要的成员,一个是WORD类型的e_magic 表示DOS可执行文件的标记(4D 5A),另一个是DWORD类型的e_lfanew 是指向PE Header的指针。
3、PE文件头(PE Header)紧挨着DOS Header,PE Header是PE相关结构NT映像头(IMAGE_NT_HEADER)的简称,里面包含着许多PE装载器用到的重要字段。之前说过了 Dos Header的e_lfanew记录的是PE Header的起始偏移量,所以得出一个公式:PNTHeader = ImageBase + DosHeader -> e_lfanew
4、IMAGE_NT_HEADER结构详解:
IMAGE_NT_HEADERS STRUCT
{
+0h DWORD Signature
+4h IMAGE_FILE_HEADER FileHeader
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader
} IMAGE_NT_HEADERS ENDS
[Signature Attribute]:
在一个有效的PE文件里,Signature字段被设置为00004550h, ASCII码字符是"PE00",标志着PE文件头的开始。
[IMAGE_FILE_HEADER Struct]
typedef struct IMAGE_FILE_HEADER { WORD Machine; // 运行平台 WORD NumberOfSections; // 文件的区块数目 DWORD TimeDateStamp; // 文件创建日期和时间 DWORD PointerToSymbolTable; // 指向符号表(主要用于调试) DWORD NumberOfSymbols; // 符号表中符号个数(同上) WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32结构大小 WORD Characteristics; // 文件属性 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
(1) Machine: 可执行文件的目标CPU类型。
IMAGE_FILE_MACHINE_I386 Value: 0x014c Meaning: x86
IMAGE_FILE_MACHINE_IA64 Value: 0x0200 Meaning: Intel Itanium
IMAGE_FILE_MACHINE_AMD64 Value: 0x8664 Meaning: x64
(2) NumberOfSection: 区块数目。(区块表示紧跟在IMAGE_NT_HEADERS后边的)
(3) TimeDateStamp: 表明文件是何时被创建的。(这个值是自1970年1月1日以来用格林威治时间(GMT)计算的秒数,这个值是比文件系统的日期时间更加精确的指示器。VC中可以用_ctime函数或gmtime函数转变。
(4) PointerToSymbolTable: COFF符号表的文件偏移位置,现在基本没用了。
(5) NumberOfSymbols: 如果有COFF符号表,它代表其中的符号数目,COFF符号是一个大小固定的结构,如果想找到COFF符号表的结束位置,则需要这个变量。
(6) SizeOfOptionalHeader: 紧跟着IMAGE_FILE_HEADER后边的数据结构(IMAGE_OPTIONAL_HEADER)的大小。(对于32位PE文件,这个值通常是00E0h; 对于64位PE32+文件,这个值是00F0h)
(7) Characteristics: 文件属性,有选择的通过几个值可以运算得到。(这些标志的有效值是定义与winnt.h内的IMAGE_FILE_xxx的值,具体含义如下。普通的exe文件这个字段的值一般是0100h, dll文件这个字段的值一般是210Eh) 多种属性可以通过 位或运算 使得同时拥有。
The characteristics of the image. This member can be one or more of the following values.
表格挺大的。。我直接附上msdn的地址:http://msdn.microsoft.com/en-us/library/windows/desktop/ms680313(v=vs.85).aspx
[IMAGE_OPTIONAL_HEADER32]
这个结构体很大 为了偷懒 我直接把msdn贴过来:http://msdn.microsoft.com/en-us/library/windows/desktop/ms680339(v=vs.85).aspx
这里只介绍几个关键的常用的:
(1) +28h DWORD AddressOfEntryPoint; // 程序执行入口RVA
指出文件的入口地址,这是一个RVA地址。如果在一个可执行文件上附加了一段代码并想让这段代码首先被执行,那么只需要将这个入口地址指向附加的代码就可以了。
(2) +34h DWORD ImageBase; // 程序的首选装载地址
指出文件的优先装入地址。也就是说当文件被执行时,如果可能的话,Windows优先将文件装入到由ImageBase字段指定的地址中,只有指定的地址已经被其他模块使用时,文件才被装入到其他地址中。
对于exe文件来说,由于每个文件总是使用独立的虚拟地址空间,优先装入地址不可能被其他模块占据,所以exe总是能够按照这个地址装入,这也意味着exe文件不再需要重定位信息。对于dll文件来说,由于多个dll文件全部使用宿主exe文件的地址空间,不能保证优先装入地址没有被其他的dll使用,所以dll文件中必须包含重定位信息以防万一。因此在IMAGE_FILE_HEADER结构中的Characteristics字段中,dll文件对应的IMAGE_FILE_RELOCS_STRIPPED位总是0,而exe文件的这个标志位总是1。
在Link的时候,可以通过指定/base:address选项来自定义优先装入地址,如果不指定,exe的默认优先装入地址是004000000h;dll的默认优先装入地址是10000000h。
(3) +38h DWORD SectionAlignment; // 内存中的区块的对齐大小
指定了节被装入内存后的对齐单位。每个节被装入的地址必定是本字段指定数值的整数倍。(一般为1000h 4KB/页)
(4) +3Ch DWORD FileAlignment; // 文件中的区块的对齐大小
指定了节存储在磁盘文件中时的对齐单位。
(5) +5Ch WORD Subsystem; // 可执行文件期望的子系统
指定使用界面的子系统,这个字段决定了系统如何为程序建立初始的界面。link时可以使用/subsystem:xxx指定这个字段的值。
Value: 0 IMAGE_SUBSYSTEM_UNKNOWN 未知的子系统
Value: 1 IMAGE_SUBSYSTEM_NATIVE 不需要子系统(如驱动程序)
Value: 2 IMAGE_SUBSYSTEM_WINDOWS_GUI GUI界面
Value: 3 IMAGE_SUBSYSTEM_WINDOWS_CUI CUI界面
Value: 5 IMAGE_SUBSYSTEM_OS2_CUI OS2控制台界面
Value: 7 IMAGE_SUBSYSTEM_POSIX_CUI POSIX控制台界面
Value: 8 IMAGE_SUBSYSTEM_NATIVE_WINDOWS 不需要子系统
Value: 9 IMAGE_SUBSYSTEM_WINDOWS_CE_GUI Windows CE图形界面
(6) +78h IMAGE_DATA_DIRECTORY DataDirectory [IMAGE_NUMBEROF_DIRECOTRY_ENTRIES]; // 数据目录表(灰常重要)
由16个相同的IMAGE_DATA_DIRECTORY结构组成,虽然PE文件中的数据是按照装入内存后的页属性归类而被放在不同的节中的,但是这些处于各个节中的数据按照用途可以被分为导出表、导入表、资源、重定位表等数据块,这16个IMAGE_DATA_DIRECTORY结构就是用来定义多种不同用途的数据块。IMAGE_DATA_DIRECTORY仅仅指出了数据块的位置和长度。
IMAGE_DATA_DIRECTORY STRUCT VirtualAddress DWORD ? ;数据的起始RVA isize DWORD ? ;数据块的长度 IMAGE_DATA_DIRECTORY ENDS
Index: 0 IMAGE_DIRECTORY_ENTRY_EXPORT 导出表
Index: 1 IMAGE_DIRECTORY_ENTRY_IMPORT 导入表
Index: 2 IMAGE_DIRECTORY_ENTRY_RESOURCE 资源
Index: 3 IMAGE_DIRECTORY_ENTRY_EXCEPTION 异常
Index: 4 IMAGE_DIRECTORY_ENTRY_SECURITY 安全
Index: 5 IMAGE_DIRECTORY_ENTRY_BASERELOC 重定位表
Index: 6 IMAGE_DIRECTORY_ENTRY_DEBUG 调试信息
Index: 7 IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 版权信息
Index: 9 IMAGE_DIRECTORY_ENTRY_TLS Thread Local Storage
Index: 12 IMAGE_DIRECTORY_ENTRY_IAT 导入函数地址表
从PE文件中寻找特定的数据时就是从这些IMAGE_DATA_DIRECTORY结构开始的,比如要存取资源,那么必须从第三个IMAGE_DATA_DIRECTORY结构中得到资源数据块的大小和未知;同理,如果要查看PE文件导入了哪些dll文件的哪些api函数,就必须从第二个IMAGE_DATA_DIRECTORY结构中得到导入表的位置和大小。