PE结构笔记

(内容未全部完成============)

简介:

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的文件与内存映射关系
文件到内存映射关系

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;
}

PE指针

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;

Dos头
Dos内容
DosHeader中的e_magic==“MZ”
e_lfanew=0x110 表示文件中0x110开始就是NTHeader

DosStub

基本上用不上
sizeof(IMAGE_DOS_HEADER)=64=0x40;
pDosStub=pDosHeader+0x40;
可以通过DosHeader和NTHeader确认位置(头尾都确定,中间就是DosStub)
DosStub
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;

NTHeader
0x16进制窗口

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;

FileHeader
0x16进制窗口

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)
子系统Subsystem

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

DllCharacteristics

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

文件中的导出表
内存中的导出表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值