PE概念:
PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,然而不管什么后缀都可以是可执行文件,只要满足正确的PE结构。
PE文件的结构:
1、DOS头:
DOS MZ 头:
偏移0位置为MZ标志
偏移位置3Ch(e_lfanew)为PE头地址偏移
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header WORD e_magic; // 幻数MZ 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; // 初始化堆栈段 WORD e_sp; // 初始化栈顶指针 WORD e_csum; // Checksum WORD e_ip; // DOS代码入口IP WORD e_cs; // DOS代码入口CS 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; // 指向PE文件头 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
DOS stub头,实质是有效的exe文件,用于在不支持PE的系统上显示错误
2、PE头:
PE文件头:
typedef struct _IMAGE_NT_HEADERS { DWORD Signature; //标志幻数P E 0x00 0x00 IMAGE_FILE_HEADER FileHeader; //映像文件头 IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选映像头 } IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
映像文件头:包含文件基本信息
typedef struct _IMAGE_FILE_HEADER { WORD Machine; //运行平台 WORD NumberOfSections; //文件节数 DWORD TimeDateStamp; //文件创建日期和时间 DWORD PointerToSymbolTable; //指向符号表 DWORD NumberOfSymbols; //符号表中符号数量 WORD SizeOfOptionalHeader; //可选映像头长度 WORD Characteristics; //文件属性(exe,dll等) } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
可选映像头
typedef struct _IMAGE_OPTIONAL_HEADER { //标准域 WORD Magic; //幻数0x010B BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; //文件执行的入口地址 DWORD BaseOfCode; DWORD BaseOfData; //NT附加域 DWORD ImageBase; //文件优先装入地址(初始VA) DWORD SectionAlignment; //内存中节的对齐粒度 DWORD FileAlignment; //文件中节的对齐粒度 WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; //内存中整个PE映像尺寸 DWORD SizeOfHeaders; //节数据到文件开始的偏移 DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //数据目录,可以查找需要的表 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
DataDirectory选项字段 Tips:DataDirectory结构包含VirtualAddress和Size
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // 导出表 #define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // 导入表 #define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // 资源表 #define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory #define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory #define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table #define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory // IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage) #define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data #define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP #define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory #define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory #define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 #define IMAGE_DIRECTORY_ENTRY_IAT 12 // 导入函数表 #define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors #define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
3、节表
包含节的信息,有几个节就有几个成员 Tips:类似目录
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节名 union { DWORD PhysicalAddress; DWORD VirtualSize; //节的大小 } Misc; DWORD VirtualAddress; //本节的RVA DWORD SizeOfRawData; //节在磁盘中的大小 DWORD PointerToRawData; //节在磁盘中相对于文件起始的偏移 DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
4、节
划分成块的PE文件真正内容,每一节是拥有共同属性的内容,载入到内存时会采用页对齐,即产生了节缝隙,可用于插入恶意代码。
一般有以下几种节:
代码节
已初始化的数据节
未初始化的数据节
资源节
引入函数节
引出函数节
5、RVA与VA
文件在磁盘上时,距离文件起始的距离为物理偏移(Offset)
文件在装入到虚拟地址中时,由于采用按页对齐,所以地址的对应不与Offset一致
在虚拟地址空间中,距离文件起始的距离为相对虚拟地址(RVA)
在虚拟地址空间中,虚拟地址(VA)能起到访问绝对地址的作用(VA-起始VA=RVA)
当程序指定的起始VA被其他程序占用时,操作系统会对VA进行重定向。