(内容未全部完成============)
简介:
PE( Portable Execute)文件是Windows下可执行文件的总称,常见的有 DLL,EXE,OCX,SYS 等。它是微软在 UNIX 平台的 COFF(通用对象文件格式)基础上制作而成。最初设计用来提高程序在不同操作系统上的移植性,但实际上这种文件格式仅用在 Windows 系列操作系统下。PE文件分为32位和64位,分别叫PE32,PE64.
事实上,一个文件是否是 PE 文件与其扩展名无关,PE文件可以是任何扩展名。那 Windows 是怎么区分可执行文件和非可执行文件的呢?我们调用 LoadLibrary 传递了一个文件名,系统是如何判断这个文件是一个合法的动态库呢?这就涉及到PE文件结构了。
"黑色图片"来自010edit的pe模板,大家可以试试这个hex解析软件(jpg,webp,mp3等许多文件都支持模板解析)
PE缩略图
**PE内存布局
PE的文件与内存映射关系
PE结构总览图
PE所有结构体指针
HANDLE hFile = CreateFile(
_T("目标文件"),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//获取文件大小
DWORD dwFileSize = GetFileSize(hFile, NULL);
CHAR *pFileBuf = new CHAR[dwFileSize];
//将文件读取到内存
DWORD ReadSize = 0;
ReadFile(hFile, pFileBuf, dwFileSize, &ReadSize, NULL);
/*指针顺序是PE在内存中的布局,64位pe直接把32换成64就可以使用*/
//Dos头
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
//NT头指针
PIMAGE_NT_HEADERS32 pNtHeader32 = (PIMAGE_NT_HEADERS32)(pDosHeader + pDosHeader->e_lfanew);
//文件头
PIMAGE_FILE_HEADER pFileHeader32 = &(pNtHeader32->FileHeader);
//可选头
PIMAGE_OPTIONAL_HEADER32 pOptionHeader32 = &(pNtHeader32->OptionalHeader);
//数据目录指针
PIMAGE_DATA_DIRECTORY pDataDirArray = pOptionHeader32->DataDirectory;
//节区头
PIMAGE_SECTION_HEADER pSectionHeader32 = (PIMAGE_SECTION_HEADER)(pOptionHeader32 + pFileHeader32->SizeOfOptionalHeader);
//数据目录数组遍历
for (size_t i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++)
{
pDataDirArray[i].VirtualAddress;
pDataDirArray[i].Size;
}
//区段遍历
for (size_t i = 0; i < pFileHeader32->NumberOfSections; i++)
{
pSectionHeader32->Name;
}
DosHeader(Dos头)
sizeof(IMAGE_DOS_HEADER)=64;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuf;
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD **e_magic**; //Magic number MZ(4D5Ah)
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;
DosHeader中的e_magic==“MZ”
e_lfanew=0x110 表示文件中0x110开始就是NTHeader
DosStub
基本上用不上
sizeof(IMAGE_DOS_HEADER)=64=0x40;
pDosStub=pDosHeader+0x40;
可以通过DosHeader和NTHeader确认位置(头尾都确定,中间就是DosStub)
NTHeader(NT头)
DosHeader中e_lfanew=0x110
pNTHeader=pDosHeader+pDosHeader->e_lfanew;
PE的32位和64在NtHeader中的OptionalHeader不一样:
IMAGE_OPTIONAL_HEADER32 (224字节)
IMAGE_OPTIONAL_HEADER64 (240字节)
32位程序到64位程序内存索引变大,所以IMAGE_OPTIONAL_HEADER中的成员由DWORD变成QWORD,增大了32位*4=16字节
32位NTHeader
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //PE(5045h)
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
64位NTHeader
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
FileHeader(文件头)
pFileHeader=pNTHeader+pNTHeader->FileHeader;
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections; //节区数量
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader; //可选头大小
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
WORD Characteristics 表示的含义:
#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 // Relocation info stripped from file.
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable (i.e. no unresolved external references).
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 // Line nunbers stripped from file.
#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 // Local symbols stripped from file.
#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 // Aggressively trim working set
#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 // App can handle >2gb addresses
#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 // Bytes of machine word are reversed.
#define IMAGE_FILE_32BIT_MACHINE 0x0100 // 32 bit word machine.
#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 // Debugging info stripped from file in .DBG file
#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 // If Image is on removable media, copy and run from the swap file.
#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 // If Image is on Net, copy and run from the swap file.
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 // File should only be run on a UP machine
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
OptionHeader(可选头)
32位OptionHeader
sizeof(IMAGE_OPTIONAL_HEADER32) =224
typedef struct _IMAGE_OPTIONAL_HEADER {
// Standard fields.
WORD Magic; //PE32(010Bh)
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //代码段大小
DWORD SizeOfInitializedData; //初始化数据大小
DWORD SizeOfUninitializedData;//未初始化数据大小
DWORD AddressOfEntryPoint; //程序入口
DWORD BaseOfCode; //代码段RVA
DWORD BaseOfData; //数据段RVA
// NT additional fields.
DWORD ImageBase; //程序映射基地址
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; //映射大小
DWORD SizeOfHeaders; //DosHeader开始到SectionHeader结束的大小
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;
可以在vs的项目属性->链接器->子系统中配置(影响入口函数,例如:main,wmain,WinMain,wWinMain)
WORD DllCharacteristics 表示属性
// DllCharacteristics Entries
//IMAGE_LIBRARY_PROCESS_INIT 0x0001 // Reserved.
//IMAGE_LIBRARY_PROCESS_TERM 0x0002 // Reserved.
//IMAGE_LIBRARY_THREAD_INIT 0x0004 // Reserved.
//IMAGE_LIBRARY_THREAD_TERM 0x0008 // Reserved.
#define IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA 0x0020 // Image can handle a high entropy 64-bit virtual address space.
#define **IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE** 0x0040 // DLL can move.重定位标识
#define IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY 0x0080 // Code Integrity Image
#define IMAGE_DLLCHARACTERISTICS_NX_COMPAT 0x0100 // Image is NX compatible
#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 // Image understands isolation and doesn't want it
#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 // Image does not use SEH. No SE handler may reside in this image
#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 // Do not bind this image.
#define IMAGE_DLLCHARACTERISTICS_APPCONTAINER 0x1000 // Image should execute in an AppContainer
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 // Driver uses WDM model
#define IMAGE_DLLCHARACTERISTICS_GUARD_CF 0x4000 // Image supports Control Flow Guard.
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
64位OptionHeader
sizeof(IMAGE_OPTIONAL_HEADER64))=240
typedef struct _IMAGE_OPTIONAL_HEADER64 {
WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
ULONGLONG ImageBase; //32位变成64位,增大了32位
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
ULONGLONG SizeOfStackReserve; //32位变成64位,增大了32位
ULONGLONG SizeOfStackCommit; //32位变成64位,增大了32位
ULONGLONG SizeOfHeapReserve; //32位变成64位,增大了32位
ULONGLONG SizeOfHeapCommit; //32位变成64位,增大了32位
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;
IMAGE_DATA_DIRECTORY (数据目录表)
SectionHeader(节区头)
pSectionHeader=pOptionHeader+pFileHeader->SizeOfOptionalHeader;
// Section header format.
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData; //节区大小
DWORD PointerToRawData; //节区在文件中的偏移
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //节区属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
#define IMAGE_SIZEOF_SECTION_HEADER 40
//节区属性
// IMAGE_SCN_TYPE_REG 0x00000000 // Reserved.
// IMAGE_SCN_TYPE_DSECT 0x00000001 // Reserved.
// IMAGE_SCN_TYPE_NOLOAD 0x00000002 // Reserved.
// IMAGE_SCN_TYPE_GROUP 0x00000004 // Reserved.
#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 // Reserved.
// IMAGE_SCN_TYPE_COPY 0x00000010 // Reserved.
#define IMAGE_SCN_CNT_CODE 0x00000020 // Section contains code.
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 // Section contains initialized data.
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 // Section contains uninitialized data.
#define IMAGE_SCN_LNK_OTHER 0x00000100 // Reserved.
#define IMAGE_SCN_LNK_INFO 0x00000200 // Section contains comments or some other type of information.
// IMAGE_SCN_TYPE_OVER 0x00000400 // Reserved.
#define IMAGE_SCN_LNK_REMOVE 0x00000800 // Section contents will not become part of image.
#define IMAGE_SCN_LNK_COMDAT 0x00001000 // Section contents comdat.
// 0x00002000 // Reserved.
// IMAGE_SCN_MEM_PROTECTED - Obsolete 0x00004000
#define IMAGE_SCN_NO_DEFER_SPEC_EXC 0x00004000 // Reset speculative exceptions handling bits in the TLB entries for this section.
#define IMAGE_SCN_GPREL 0x00008000 // Section content can be accessed relative to GP
#define IMAGE_SCN_MEM_FARDATA 0x00008000
// IMAGE_SCN_MEM_SYSHEAP - Obsolete 0x00010000
#define IMAGE_SCN_MEM_PURGEABLE 0x00020000
#define IMAGE_SCN_MEM_16BIT 0x00020000
#define IMAGE_SCN_MEM_LOCKED 0x00040000
#define IMAGE_SCN_MEM_PRELOAD 0x00080000
#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 //
#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 //
#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 //
#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 //
#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 // Default alignment if no others are specified.
#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 //
#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 //
#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 //
#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 //
#define IMAGE_SCN_ALIGN_512BYTES 0x00A00000 //
#define IMAGE_SCN_ALIGN_1024BYTES 0x00B00000 //
#define IMAGE_SCN_ALIGN_2048BYTES 0x00C00000 //
#define IMAGE_SCN_ALIGN_4096BYTES 0x00D00000 //
#define IMAGE_SCN_ALIGN_8192BYTES 0x00E00000 //
// Unused 0x00F00000
#define IMAGE_SCN_ALIGN_MASK 0x00F00000
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 // Section contains extended relocations.
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 // Section can be discarded.
#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 // Section is not cachable.
#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 // Section is not pageable.
#define IMAGE_SCN_MEM_SHARED 0x10000000 // Section is shareable(可共享).
#define IMAGE_SCN_MEM_EXECUTE 0x20000000 // Section is executable(可执行).
#define IMAGE_SCN_MEM_READ 0x40000000 // Section is readable(可读).
#define IMAGE_SCN_MEM_WRITE 0x80000000 // Section is writeable(可写).
位置计算
BaseAdderss(dll加载,exe固定基地址,重定位表)ImageBase
FOV
RVA
VA
Export Table
_ _declspec(export)时,编译器仅向.drectve部分发出等效的命令行(例如,“-export:MyFunction”)。
#pragma comment(linker, “/export:DoSomething=DllImpl.ActuallyDoSomething”)
对MessageBox和ExitProcess函数的调用变成了对0040101A和00401020地址的调用,但是这两个地址显然是位于程序自身模块而不是DLL模块中,实际上,这是由编译器在程序所有代码的后面自动加上的Jmp dword ptr[xxxxxxxx]类型的指令,这个指令时一个间接寻址的跳转指令,xxxxxxxx地址中存放的才是真正的导入函数的地址。在这个例子中,00402000地址处存放的就是ExitProcess函数的地址。
PE文件被装载的时候,Windows装载器会根据xxxxxxxx处的RVA得到函数名,再根据函数名在内存中找到函数地址,并且用函数地址将xxxxxxx处的内容替换成真正的函数地址,那么所有的疑惑就迎刃而解了。接下来看看如何获取导入表的位置。
Import Table