“稀饭”大佬给我发的一份资料,我觉得在学习汇编前还是有必要了解一下PE文件结构。

 

这张图很清楚:

+----------------------+------------------ | Dos Header |64字节(0x40) | | +----------------------+------------------ | Dos Stub | | | +----------------------+------------------ | |--------------->+----------------------+ | | | Signature | | | | | | Pe Header |248字节(0xF8) +----------------------+ | | | File Header | | | | | | | +----------------------+ | | | Optional Header |--------------->+----------------------+ +----------------------+----------------| | | Magic | | section headers 1 |40字节(0x28) +----------------------+ | | | | +----------------------+ +----------------------+---------------- | . . . | | section headers 2 | | | | | +----------------------+ +----------------------+ | DataDirectory | | .................. | +----------------------| | | | | +----------------------+ +----------------------+ | | section 1 | | | | +----------------------+ +----------------------+ | Export Directory |8字节(0x08) | section 2 | +----------------------+ | | | Import Directory | +----------------------+ +----------------------+ | .................. | | . . . | | | +----------------------+ +----------------------+ | . . . | +----------------------+    

 //sdk winnt.h //dos header/// #define IMAGE_DOS_SIGNATURE 0x5A4D // MZ typedef struct _IMAGE_DOS_HEADER // DOS .EXE header { WORD e_magic; // +0x00 Magic number 魔法数字 "MZ" * WORD e_cblp; // +0x02 Bytes on last page of file 文件最后一页的字节数 WORD e_cp; // +0x04 Pages in file 文件的页数 WORD e_crlc; // +0x06 Relocations 重定位 WORD e_cparhdr; // +0x08 Size of header in paragraphs 段中头的大小 WORD e_minalloc; // +0x0a Minimum extra paragraphs needed 需要的最少额外段 WORD e_maxalloc; // +0x0c Maximum extra paragraphs needed 需要的最多额外段 WORD e_ss; // +0x0e Initial (relative) SS value 初始的(相对的)SS寄存器值 WORD e_sp; // +0x10 Initial SP value 初始的SP寄存器值 WORD e_csum; // +0x12 Checksum 校验和 WORD e_ip; // +0x14 Initial IP value 初始的IP寄存器值 WORD e_cs; // +0x16 Initial (relative) CS value 初始的(相对的)CS寄存器值 WORD e_lfarlc; // +0x18 File address of relocation table 重定位表在文件中的地址 WORD e_ovno; // +0x1a Overlay number 交叠数 WORD e_res[4]; // +0x1c Reserved words 保留 WORD e_oemid; // +0x24 OEM identifier (for e_oeminfo) WORD e_oeminfo; // +0x26 OEM information; e_oemid specific WORD e_res2[10]; // +0x28 Reserved words 保留 LONG e_lfanew; // +0x3c File address of new exe header 新exe头在文件中的地址 * // +0x40 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; // size 64字节(0x40) /// ///

 

///
//dos stub
sizeof(dos stub) = (大小不固定,因便宜器而改变)
DOS stub实际上是个有效的 EXE,在不支持 PE文件格式的操作系统中,它将简单显示一个错误提示,
类似于字符串 "This program requires Windows" 或者程序员可根据自己的意图实现完整的 DOS代码。
通常我们也不对 DOS stub 太感兴趣: 因为大多数情况下它是由汇编器/编译器自动生成。
通常,它简单调用中断21h服务9来显示字符串"This program cannot run in DOS mode"。
//

// //sdk winnt.h //pe header typedef struct _IMAGE_NT_HEADERS { DWORD Signature; // +0x00 pe文件签(4Byte)名 ASCII="pe/0/0" * IMAGE_FILE_HEADER FileHeader; // +0x04 PE文件头(IMAGE_FILE_HEADER) IMAGE_OPTIONAL_HEADER OptionalHeader; // +0x18 PE文件可选头(IMAGE_OPTIONAL_HEADER) } IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS; // size 248字节(0xF8) //file header typedef struct _IMAGE_FILE_HEADER { WORD Machine; // +0x00 机器类型对于intel 386=0x014c * WORD NumberOfSections; // +0x02 文件节数目 * DWORD TimeDateStamp; // +0x04 时间戳文件创建的时间 DWORD PointerToSymbolTable; // +0x08 用于调试 DWORD NumberOfSymbols; // +0x0c 用于调试 WORD SizeOfOptionalHeader; // +0x10 指明IMAGE_OPTIONAL_HEADER的大小 * WORD Characteristics; // +0x12 特征集合 * // +0x14 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; // size 20字节(0x14) //optional header typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; // +0x00 魔法数字 * 0x010B BYTE MajorLinkerVersion; // +0x02 连接器主版本号 BYTE MinorLinkerVersion; // +0x03 连接器副版本号 DWORD SizeOfCode; // +0x04 可执行代码的大小 DWORD SizeOfInitializedData; // +0x08 已初始化数据的大小 DWORD SizeOfUninitializedData; // +0x0c 未初始化数据的大小 DWORD AddressOfEntryPoint; // +0x10 入口点地址 * DWORD BaseOfCode; // +0x14 可执行代码的偏移值(“代码基址”) DWORD BaseOfData; // +0x18 已初始化数据的偏移值(“数据基址”) DWORD ImageBase; // +0x1c 映象文件基址 * DWORD SectionAlignment; // +0x20 内存对其 * 2^n DWORD FileAlignment; // +0x24 文件对其 * 2^n WORD MajorOperatingSystemVersion; // +0x28 OS主版本号 WORD MinorOperatingSystemVersion; // +0x2a OS副版本号 WORD MajorImageVersion; // +0x2c 映象文件主版本号 WORD MinorImageVersion; // +0x2e 映象文件副版本号 WORD MajorSubsystemVersion; // +0x30 子系统主版本号 * WORD MinorSubsystemVersion; // +0x32 子系统副版本号 DWORD Win32VersionValue; // +0x34 Win32版本值 DWORD SizeOfImage; // +0x38 映像文件大小 * DWORD SizeOfHeaders; // +0x3c 所有类型的头的大小的总和 * (指向第一个节) DWORD CheckSum; // +0x40 校验和 WORD Subsystem; // +0x44 子系统 * WORD DllCharacteristics; // +0x46 DLL标记(0非dll文件,1 dll文件) * DWORD SizeOfStackReserve; // +0x48 保留栈的大小 DWORD SizeOfStackCommit; // +0x4c 指定栈的大小 DWORD SizeOfHeapReserve; // +0x50 保留堆的大小 DWORD SizeOfHeapCommit; // +0x54 指定堆的大小 DWORD LoaderFlags; // +0x58 加载器标志 DWORD NumberOfRvaAndSizes; // +0x5c RVA大小的总和 * IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // +0x60 // +0xe0 } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32; //size 224字节(0xE0) typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; // +0x00 RVA DWORD Size; // +0x04 The size in bytes // +0x08 } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 0 [ 0] RVA [size] of Export Directory 208 [ 28] RVA [size] of Import Directory 0 [ 0] RVA [size] of Resource Directory 0 [ 0] RVA [size] of Exception Directory 0 [ 0] RVA [size] of Certificates Directory 0 [ 0] RVA [size] of Base Relocation Directory 0 [ 0] RVA [size] of Debug Directory 0 [ 0] RVA [size] of Architecture Directory 0 [ 0] RVA [size] of Special Directory 0 [ 0] RVA [size] of Thread Storage Directory 0 [ 0] RVA [size] of Load Configuration Directory 0 [ 0] RVA [size] of Bound Import Directory 1E0 [ 8] RVA [size] of Import Address Table Directory 0 [ 0] RVA [size] of Delay Import Directory 0 [ 0] RVA [size] of Reserved Directory 0 [ 0] RVA [size] of Reserved Directory // // //sdk winnt.h //Section header #define IMAGE_SIZEOF_SHORT_NAME 8 //定义一个常量 typedef struct _IMAGE_SECTION_HEADER { UCHAR Name[IMAGE_SIZEOF_SHORT_NAME]; // +0x00 名字数组 union { // 共用体标志 ULONG PhysicalAddress; // 节的物理地址 ULONG VirtualSize; // +0x08 节的虚拟大小 <--(文件中的值) } Misc; ULONG VirtualAddress; // +0x0c 节被载入到的地址空间中的虚拟地址 ULONG SizeOfRawData; // +0x10 下个FileAlignment'倍数的大小 ULONG PointerToRawData; // +0x14 位于文件中的节身的位置的偏移量,如果是0,节数据不包含在文件内,在加载时被确定 ULONG PointerToRelocations; // +0x18 重定位指针 ULONG PointerToLinenumbers; // +0x1c 行数指针 USHORT NumberOfRelocations; // +0x20 重定位数目 USHORT NumberOfLinenumbers; // +0x22 行数数目 ULONG Characteristics; // +0x24 定义节的特征 // +0x28 } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; // size 40字节(0x28) 值 定义 0x00000020 代码节 0x00000040 已初始化数据节 0x00000080 未初始化数据节 0x04000000 不可缓存节 0x08000000 不可分页节 0x10000000 共享节 0x20000000 可执行节 0x40000000 可读节 0x80000000 可写节

 


注意:
如果一个PE文件里,对应了N个节,那么IMAGE_SECTION_HEADER结构也是对应N个
另外为了标记IMAGE_SECTION_HEADER结构已经结束了,都会在最后一个IMAGE_SECTION_HEADER附加一个
成员全部为零的IMAGE_SECTION_HEADER结构。
WINXP下运行PE的程序最小要求IMAGE_SECTION_HEADER为>=2,WIN2k则没有这个要求

//IMAGE_IMPORT_DESCRIPTOR typedef struct _IMAGE_IMPORT_DESCRIPTOR { union { DWORD Characteristics; // 0 for terminating null import descriptor DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) }; DWORD TimeDateStamp; // 0 if not bound, // -1 if bound, and real date/time stamp // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) // O.W. date/time stamp of DLL bound to (Old BIND) DWORD ForwarderChain; // -1 if no forwarders DWORD Name; DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) } IMAGE_IMPORT_DESCRIPTOR; typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR; DWORD OriginalFirstThunk //它是一个RVA(32位),指向一个以0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数组,其每个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组永不改变。 DWORD TimeDateStamp //它是一个具有好几个目的的32位的时间戳。让我们先假设时间戳为0,一些高级的情况将在以后处理。 DWORD ForwarderChain //它是输入函数列表中第一个中转的、32位的索引。中转也是高级的东东。对初学者先将所有位设为-1。 DWORD Name //它是一个DLL文件的名称(0结尾的ASCII码字符串)的、32位的RVA。 DWORD FirstThunk //它也是一个RVA(32位),指向一个0结尾的、由IMAGE_THUNK_DATA(换长数据)的RVA构成的数组,其每个IMAGE_THUNK_DATA(换长数据)元素都描述一个函数。此数组是输入地址表的一部分,并且可以改变。

 

注意:IMAGE_IMPORT_DESCRIPTOR结构个数是和需要应入的DLL文件个数对应的,比如应入了N个DLL文件,那么就有N个IMAGE_IMPORT_DESCRIPTOR结构
最后用一个成员全为零的IMAGE_IMPORT_DESCRIPTOR结构来标记结束。

 

 typedef struct _IMAGE_THUNK_DATA32 { union { DWORD ForwarderString; // PBYTE DWORD Function; // PDWORD DWORD Ordinal; DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME } u1; } IMAGE_THUNK_DATA32; typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

 


对应一个32位的值

DWORD ForwarderString;     标记位对应一个比特位,当为1时候,表示函数用序号引入,为0时,表示用名字引入
注意:IMAGE_THUNK_DATA32结构个数是和需要引入函数个数对应的,比如引入了N个函数,那么就有N个IMAGE_THUNK_DATA32结构
最后用一个成员全为零的IMAGE_THUNK_DATA32结构来标记结束。

typedef struct _IMAGE_IMPORT_BY_NAME { WORD Hint; //Hint字段的内容是可选的,如果它不是0,则它也表示函数的序号 BYTE Name[1]; //变长数组,保存的是一个以NULL结尾的字符串,也就是函数名。 } IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;