PE文件是Windows系统下可执行文件的总称,英文全称 Portable Executable 可移植的可执行文件,常见的有exe、dll、sys、com、ocx
对于学习反(木马、免杀、病毒、外挂、内核),了解PE文件结构是非常有必要且非常非常重要的!
PE结构包括:
- MS-DOS头
- 标准PE头
- 扩展PE头
- 数据目录
- 节表
代码段(.text 或 .code): 可读 可执行 不可写
数据段(.data 或 .rdata 或.bss): 可读 可写 不可执行
未初始化数据段(.bss):可读 可写 不可执行
资源段(.rsrc): 可读 不可执行 不可写
导入表(.idata) : 简称 IAT 表 Import Address Table
导出表(.edata): 简称 EAT 表 Export Address Table
IMAGE_DOS_HEADER (长度不定)
MZ标记: 4D5A EXE程序的标志 在DOS头里 4字节
DOS_STUB (长度不定)
DOS STUB : 在dos系统下,会执行 DOS Stub 中的内容,不会出现不可预料的错误
IMAGE_NT_HEADERS (f8h)
PE Signature: PE标记 MZ偏移3C的位置 是e_lfanew 指向PE标记 PE头相对于文件的偏移地址 4字节
IMAGE_FILE_HEADER PE文件头 (14h)
Machine : PE标记后2个字节是Machine 14C代表32位CPU 8664代表64位CPU 2字节
NumberOfSections: 代表区段数量 代码段 数据段 资源段 2字节
Timestamp: 时间戳,文件创建时间, 表示从1970年1月1日 00:00:00 到文件创建时间的秒数 4字节
PointerOfSymbolTable: 用于调试 4字节
NumberOfSymbols: 用于调试 4字节
SizeOfOptionalHeader: 可选头大小 e0h 2字节
Characteristics: 文件信息标志 如dll/exe 2字节
IMAGE_OPTIONAL_HEADER 可选PE头 (e0h)
Magic :代表文件的格式 010b是32位应用程序 020b是64位应用程序 107是系统的ROM文件 2字节
Major LinkerVersion : 连接器首版本号 1字节
Minor LinkerVersion : 连接器次版本号 1字节
SizeOfCode : 代码块大小 4字节 (512为一个磁盘扇区)
SizeOfInitializedData : 已初始化数据块大小 4字节 也就是.data
SizeOfUninitializedData : 未初始化数据块大小 4字节 也就是.data
AddressOfEntryPoint : 程序执行入口(OEP) 4字节 RVA(相对偏移)(相对虚拟内存地址)
BaseOfCode: 代码块起始RVA 4字节 表示代码段起始RVA
BaseOfData : 数据块起始RVA 4字节 表示数据段的起始RVA
ImageBase: 基址 4字节 入口点
EXE,DLL文件被装载到用户内存的0~7FFFFFFF中,SYS文件被加载到80000000~FFFFFFFF中
执行PE文件的时候。PE装载器先创建进程,再将文件载入内存,然后把EIP寄存器的值设置为ImageBase+AddressOfEntryPoint
BaseofCode + ImageBase可以得到代码段的起始地址
BaseOfData + ImageBase可以得到数据段段的起始地址
SectionAlignment: 块对齐值 4字节
FileAlignment : 文件对齐值 4字节
Major Operating System Version : 操作系统首版本号 2字节
Minor Operating SystemVersion: 操作系统次版本号 2字节
Major Image Version:用户程序首版本号 2字节
Minor Image Version:用户程序次版本号 2字节
MajorSub System Version :子系统首版本号 2字节
MinorSub System Version :子系统次版本号 2字节
Win32 Version Value : 32位系统版本号值 4字节
SizeOfImage : 整个程序在内存中占用的空间(PE映像尺寸)映像大小 4字节
SizeOfHeaders : 块前头部大小 4字节 所有头(头的结构体大小)+节表的大小
Checksum : 校验和 4字节 对于驱动程序,可能会使用
SubSystem : 程序运行所需子系统 2字节 重要
DICharacteristics:当文件为dll文件时使用 2字节
SizeOf StackReserve :保留栈大小 4字节
SizeOf StackCommit :使用栈大小 4字节
SizeOf HapReserve : 保留堆大小 4字节
SizeOf HeapCommit : 使用堆大小 4字节
LoaderFlags : 设置自动调用断点或调试器 4字节 与调试有关
NumberOfRvaAndSizes: Rva的大小的数量 4字节 下面的成员,数据目录结构的项目数量
DataDirectory : Image_Data Directorys 80字节 数据目录,默认16个,16是宏,这里方便直接写成16
SECTION TABLE (长度不定)
IMAGE_SECTION_HEADER (28h)
Name :块名 8字节
VirtualSize: 内存中快大小 4字节
VirtusalAddress: 内存中块RVA值 4字节
SizeOfRawData:文件中块大小 4字节
PointerToRawData:文件中块偏移 4字节
PointerToRelocations: OBJ文件使用 4字节
PointerToLineNumbers: OBJ文件使用 4字节
NumberOfRelocations : OBJ文件使用 2字节
NumberOfLineNumbers : OBJ文件使用 2字节
Characteristics : 块属性
- RVA 相对虚拟地址,映像文件在虚拟内存中相对于加载基址的偏移。
- VA 虚拟地址,映像文件在虚拟内存中的地址。
- FOA 文件偏移地址,映像文件在磁盘文件中相对于文件开头的偏移。
三个地址概念:
- VA:( Virtual Address )虚拟内存地址,范围00000000h - 0FFFFFFFh,就是进程的基地址 + RVA相对虚拟内存地址
- RVA:相对虚拟内存地址,RVA是相对某个模块而存在的
- FOA:文件偏移地址,文件头的偏移地址
VA = ImageBase + RVA
RVA = VA - ImageBase
PE头部分用于宏观上记录文件的一些信息,运行平台,大小,创建日期,属性等
节表部分用于对各中类型的数据进行定义分段
节数据就是文件的数据部分,实际上我们编写程序的过程中就是对该部分的数据进行编写。
而其他的部分则是由编译器依照我们编写的部分进行相应的填写而得到
DOS头结构
在winnt.h里已经定义了,在使用的时候包含windows.h头文件即可
#include<Windows.h>
DOS头的结构体
typedef struct _IMAGE_DOS_HEADER { // DOS-MZ文件头
WORD e_magic; // DOS 可执行文件的标识符 +0h
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; // 指向PE文件头 +3ch
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
其中e_magic 和 e_lfanew 比较重要, e_magic 为 "MZ" ,e_lfanew 字段是真正PE文件的相对偏移(RVA)
typedef struct _IMAGE_NT_HEADERS {DWORD Signature; //该结构体中的Signature就是PE签名,标识该文件是否是PE文件。该部分占4字节,即“50 45 0000”。IMAGE_FILE_HEADER FileHeader; //20字节IMAGE_OPTIONAL_HEADER32 OptionalHeader; //32位0xE0字节 64位0xF0字节
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32; //64位是PIMAGE_NT_HEADERS64
typedef struct _IMAGE_FILE_HEADER {WORD Machine; // 程序允许的cpu型号,为0代表所有 重要成员WORD NumberOfSections; // 节区数量 重要成员DWORD TimeDateStamp; // 自1970年1月1日00:00起的秒数的低 32 位 (C 运行时time_t值) ,该值指示文件创建时间。DWORD PointerToSymbolTable; // COFF符号表的文件偏移量,如果没有COFF符号表,则为零。映像的此值应为零,因为 COFF 调试信息已弃用。DWORD NumberOfSymbols; // 符号表中的条目数。此数据可用于查找紧跟在符号表后面的字符串表。映像的此值应为零,因为 COFF 调试信息已弃用。WORD SizeOfOptionalHeader; // 可选标头的大小,这是可执行文件所必需的 32位默认E0 64位默认F0 重要成员WORD Characteristics; // 指示文件属性的标志。 文件属性,每个位有不同含义 可判断是否存在重定位信息,很重要 重要成员
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
IMAGE_OPTIONAL_HEADER {WORD Magic; // +0018h - 用于标识32/64位文件 PE32: 10B PE64: 20BBYTE MajorLinkerVersion; // +001ah - 链接器版本号BYTE MinorLinkerVersion; // +001bh - DWORD SizeOfCode; // +001ch - 所有代码段的总大小 按照FileAlignment对齐后的大小 编译器填入 无用DWORD SizeOfInitializedData; // +0020h - 所有已初始化数据区块的大小 按文件对齐 编译器填入 没用(可改)DWORD SizeOfUninitializedData; // +0024h - 所有未初始化数据区块的大小 按文件对齐 编译器填入 没用(可改)DWORD AddressOfEntryPoint; 重要成员 // +0028h - 程序执行入口OEP RVADWORD BaseOfCode; // +002ch - 代码节的起始RVADWORD BaseOfData; // +0030h - 数据节的起始RVADWORD ImageBase; 重要成员 // +0034h - 程序的建议装载地址 内存镜像基址(程序默认载入基地址)DWORD SectionAlignment; 重要成员 // +0038h - 内存中的节的对齐粒度DWORD FileAlignment; 重要成员 // +003ch - 文件中的节的对齐粒度WORD MajorOperatingSystemVersion; // +0040h - 操作系统版本号WORD MinorOperatingSystemVersion; // +0042h - WORD MajorImageVersion; // +0044h - 该PE的版本号WORD MinorImageVersion; // +0046h - WORD MajorSubsystemVersion; // +0048h - 所需子系统的版本号WORD MinorSubsystemVersion; // +004ah - DWORD Win32VersionValue; // +004ch - 未用DWORD SizeOfImage; 重要成员 // +0050h - 内存中的整个PE映像尺寸,可比实际值大,必须是SectionAlignment的整数倍DWORD SizeOfHeaders; 重要成员 // +0054h - 所有头+节表的大小 对齐之后的值DWORD CheckSum; // +0058h - 校验和 映像校验和,一些系统.dll文件有要求,判断是否被修改WORD Subsystem; // +005ch - 文件的子系统WORD DllCharacteristics; // +005eh - DLL文件特性,,不是针对DLL文件的,16进制转换2进制可以根据属性对应的表格得到相应的属性DWORD SizeOfStackReserve; // +0060h - 初始化时的栈大小DWORD SizeOfStackCommit; // +0064h - 初始化时实际提交的栈大小DWORD SizeOfHeapReserve; // +0068h - 初始化时保留的堆大小DWORD SizeOfHeapCommit; // +006ch - 初始化时实际提交的堆大小DWORD LoaderFlags; // +0070h - 与调试有关DWORD NumberOfRvaAndSizes; 重要成员 // +0074h - 下面的数据目录结构的项目数量IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 0078h - 数据目录
}
IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; // +0000h - 数据的起始RVADWORD Size; // +0004h - 数据块的长度
}
IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // +0000h - 8个字节节名 重要成员union {DWORD PhysicalAddress;DWORD VirtualSize; 重要成员 // 区段大小,没做对齐之前的大小} Misc; // +0008h - 节区的尺寸DWORD VirtualAddress; 重要成员 // +000ch - 节区的RVA地址DWORD SizeOfRawData; // +0010h - 在文件中对齐后的尺寸DWORD PointerToRawData; 重要成员 // +0014h - 在文件中的偏移DWORD PointerToRelocations; // +0018h - 在OBJ文件中使用DWORD PointerToLinenumbers; // +001ch - 行号表的位置(供调试用)WORD NumberOfRelocations; // +0020h - 在OBJ文件中使用WORD NumberOfLinenumbers; // +0022h - 行号表中行号的数量DWORD Characteristics; 重要成员 // +0024h - 节的属性
}
typedef struct _IMAGE_IMPORT_DESCRIPTOR {union {DWORD Characteristics; // 0 for terminating null import descriptorDWORD OriginalFirstThunk; // RVA 指向 INT (PIMAGE_THUNK_DATA结构数组)} DUMMYUNIONNAME;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 forwardersDWORD Name; //RVA指向dll名字,以0结尾DWORD FirstThunk; // RVA 指向 IAT (PIMAGE_THUNK_DATA结构数组)
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
typedef struct _IMAGE_IMPORT_BY_NAME {WORD Hint; //可能为空,编译器决定,如果不为空,是函数在导出表的索引BYTE Name[1]; //函数名称,以0结尾
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;#include "pshpack8.h" // Use align 8 for the 64-bit IAT.typedef struct _IMAGE_THUNK_DATA64 {union {ULONGLONG ForwarderString; // 指向一个转向者字符串的RVAULONGLONG Function; // 被输入的函数的内存地址ULONGLONG Ordinal; // 被输入API的序数值ULONGLONG AddressOfData; // 指针指向 IMAGE_IMPORT_BY_NAME} u1;
} IMAGE_THUNK_DATA64;
typedef IMAGE_THUNK_DATA64 * PIMAGE_THUNK_DATA64;#include "poppack.h" // Back to 4 byte packingtypedef struct _IMAGE_THUNK_DATA32 {union {DWORD ForwarderString; // PBYTE DWORD Function; // PDWORDDWORD Ordinal;DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;
typedef struct _IMAGE_EXPORT_DIRECTORY {DWORD Characteristics; //保留段,值为00,特征值DWORD TimeDateStamp; //导出表创建时间WORD MajorVersion; //导出表主版本号WORD MinorVersion; //导出表子版本号DWORD Name; // 指针指向该导出表文件名字符串DWORD Base; // 导出函数起始序号DWORD NumberOfFunctions; // 所有导出函数的个数DWORD NumberOfNames; // 以函数名字导出的函数个数DWORD AddressOfFunctions; // 指针指向导出函数地址表RVADWORD AddressOfNames; // 指针指向导出函数名称表RVADWORD AddressOfNameOrdinals; // 指针指向导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
正常情况下,dll才有导出表
typedef struct _IMAGE_BASE_RELOCATION {DWORD VirtualAddress; // 重定位数据的开始 RVA 地址DWORD SizeOfBlock; // 重定位块的长度
// WORD TypeOffset[1]; // 重定位项数组
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
typedef struct _IMAGE_TLS_DIRECTORY32 {DWORD StartAddressOfRawData; // 内存起始地址DWORD EndAddressOfRawData; //内存结束地址DWORD AddressOfIndex; // PDWORDDWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *DWORD SizeOfZeroFill; // 0填充区域长度union {DWORD Characteristics; //保留字段struct {DWORD Reserved0 : 20;DWORD Alignment : 4;DWORD Reserved1 : 8;} DUMMYSTRUCTNAME;} DUMMYUNIONNAME;} IMAGE_TLS_DIRECTORY32;
typedef IMAGE_TLS_DIRECTORY32 * PIMAGE_TLS_DIRECTORY32;
TLS表 存在TSL区段里:线程局部存储,会在程序运行之前执行完毕,用于反调试
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {union {DWORD AllAttributes;struct {DWORD RvaBased : 1; // Delay load version 2DWORD ReservedAttributes : 31;} DUMMYSTRUCTNAME;} Attributes;DWORD DllNameRVA; // RVA to the name of the target library (NULL-terminate ASCII string)DWORD ModuleHandleRVA; // RVA to the HMODULE caching location (PHMODULE)DWORD ImportAddressTableRVA; // RVA to the start of the IAT (PIMAGE_THUNK_DATA)DWORD ImportNameTableRVA; // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData)DWORD BoundImportAddressTableRVA; // RVA to an optional bound IATDWORD UnloadInformationTableRVA; // RVA to an optional unload info tableDWORD TimeDateStamp; // 0 if not bound,// Otherwise, date/time of the target DLL} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;
导入表(Import Table):
- 导入表包含了程序对外部DLL函数的引用,包括函数的名字和所在的DLL名称。
- 在编译阶段,链接器会生成导入表,其中的条目用于告诉加载器程序需要哪些外部函数。
- 导入表中通常包含IMAGE_IMPORT_DESCRIPTOR结构,它描述了导入的DLL和函数列表。
IAT(Import Address Table):
- IAT是在程序加载到内存时由操作系统动态填充的,它存储了从导入表中列出的DLL函数的实际内存地址。
- 当程序加载时,加载器会解析导入表,加载所需的DLL,并将DLL中函数的实际地址填入IAT。
- 程序执行时,它通过IAT中的地址来调用外部DLL的函数。
----------------------------------------------------------------------------------------------------------------------------
IMAGE_DOS_HEADER DOS头 0x40(64)字节
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE headerWORD e_magic; // MZ标记 勿改WORD e_cblp; // Bytes on last page of fileWORD e_cp; // Pages in fileWORD e_crlc; // RelocationsWORD e_cparhdr; // Size of header in paragraphsWORD e_minalloc; // Minimum extra paragraphs neededWORD e_maxalloc; // Maximum extra paragraphs neededWORD e_ss; // Initial (relative) SS valueWORD e_sp; // Initial SP valueWORD e_csum; // ChecksumWORD e_ip; // Initial IP valueWORD e_cs; // Initial (relative) CS valueWORD e_lfarlc; // File address of relocation tableWORD e_ovno; // Overlay numberWORD e_res[4]; // Reserved wordsWORD e_oemid; // OEM identifier (for e_oeminfo)WORD e_oeminfo; // OEM information; e_oemid specificWORD e_res2[10]; // Reserved wordsLONG e_lfanew; // PE头偏移 勿改} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
仅使用了两个成员 , 其他成员都可以修改
e_magic 0x5A4D
0 + e_lfanew = PE标记
e_lfanew 到 PE标记 之间的数据为 DOS_STUB , 可随意修改
IMAGE_NT_HEADER NT头 32位: 0xF8(248)字节 64位: 0x108(264)字节
typedef struct _IMAGE_NT_HEADERS {DWORD Signature; //PE标记 0x00004550 IMAGE_FILE_HEADER FileHeader; //标准PE头 0x14(20)字节IMAGE_OPTIONAL_HEADER32 OptionalHeader; //可选PE头 32位默认大小为0xE0
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;typedef struct _IMAGE_NT_HEADERS64 {DWORD Signature; //PE标记 0x00004550 IMAGE_FILE_HEADER FileHeader; //标准PE头 0x14(20)字节IMAGE_OPTIONAL_HEADER64 OptionalHeader; //可选PE头 64位默认大小为0xF0
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
PE标记必须为0x00004550 , 否则程序无法启动
IMAGE_FILE_HEADER 标准PE头 0x14(20)字节
typedef struct _IMAGE_FILE_HEADER {WORD Machine; //运行平台 0x014c 或 0x8664 WORD NumberOfSections; //节区数量 DWORD TimeDateStamp; //文件创建时间戳 可抹零DWORD PointerToSymbolTable; //指向符号表(用于调试)DWORD NumberOfSymbols; //符号表中符号个数(用于调试)WORD SizeOfOptionalHeader; //可选头大小 32位:0xE0 64位:0xF0 WORD Characteristics; //文件属性 由位组成
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
#define IMAGE_SIZEOF_FILE_HEADER 20
IMAGE_OPTIONAL_HEADER 可选PE头 32位默认大小为0xE0(224)字节 64位大小为0xF0(240)字节
typedef struct _IMAGE_OPTIONAL_HEADER {WORD Magic; //镜像文件的状态,用于判断程序是32位还是64位BYTE MajorLinkerVersion; //链接器版本号BYTE MinorLinkerVersion; //链接器次要版本号DWORD SizeOfCode; //代码段的大小(以字节为单位),如果有多个代码段,则为所有这些代码段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)DWORD SizeOfInitializedData; //已初始化数据段的大小(以字节为单位),如果有多个初始化数据段,则为所有这些数据段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)DWORD SizeOfUninitializedData; //未初始化数据段的大小(以字节为单位),如果有多个未初始化数据段,则为所有这些数据段的总和。是文件对齐后的大小 编译器填的 没用(不一定准确)DWORD AddressOfEntryPoint; //RVA 一个指向入口点函数的指针,相对于ImageBase, 1.对于可执行文件,这是起始地址 2.对于设备驱动程序,这是初始化函数的地址3.入口点函数对于dll是可选的。当没有入口点存在时,该成员为零 DWORD BaseOfCode; //代码段RVA 指向代码段开头的指针,相对于ImageBase。编译器填的 没用(不一定准确)DWORD BaseOfData; //数据段RVA 指向数据段开头的指针,相对于ImageBase。编译器填的 没用(不一定准确)DWORD ImageBase; //内存镜像基址 exe默认0x400000,dll默认0x10000000DWORD SectionAlignment; //加载到内存中的节的对齐方式,以字节为单位。该值必须大于或等于FileAlignment(文件对齐)成员。默认值是系统的页面大小DWORD FileAlignment; //Image(PE文件)中各节的原始数据(以字节为单位)的对齐方式。该值应该是512到64K(包括)之间2的幂。缺省值是512。如果SectionAlignment成员小于系统页面大小,则该成员必须与SectionAlignment相同WORD MajorOperatingSystemVersion; //所需操作系统的主要版本号WORD MinorOperatingSystemVersion; //所需操作系统的次要版本号WORD MajorImageVersion; //镜像(PE文件)的主版本号WORD MinorImageVersion; //镜像(PE文件)的次要版本号WORD MajorSubsystemVersion; //子系统的主要版本号WORD MinorSubsystemVersion; //子系统的次要版本号DWORD Win32VersionValue; //保留,并且必须为0DWORD SizeOfImage; //映像装入内存后的总大小DWORD SizeOfHeaders; //DOS头、PE头、区块表总大小 文件对齐后大小DWORD CheckSum; //Image(PE文件)校验和WORD Subsystem; //运行此映像所需的子系统WORD DllCharacteristics; //Image的DLL特性DWORD SizeOfStackReserve; //为堆栈保留的字节数DWORD SizeOfStackCommit; //要提交给堆栈的字节数DWORD SizeOfHeapReserve; //为本地堆保留的字节数DWORD SizeOfHeapCommit; //要为本地堆提交的字节数DWORD LoaderFlags; //该成员已过时,与调试有关,默认为 0 DWORD NumberOfRvaAndSizes; //数据目录表的项数 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
ImageBase + AddressOfEntryPoint (RVA) = 程序真正执行地址 (OEP)
ImageBase + 相对虚拟地址(RVA) = 虚拟内存地址(VA)
VA - ImageBase = 相对虚拟地址(RVA)
文件偏移地址(FOA)
IMAGE_DATA_DIRECTORY 数据目录表
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16typedef struct _IMAGE_DATA_DIRECTORY {DWORD VirtualAddress; //数据块的起始RVADWORD Size; //目录项大小
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
导出表 / 导入表 / 重定位表 / IAT表
IMAGE_SECTION_HEADER 节表 每个40字节
#define IMAGE_SIZEOF_SECTION_HEADER 40
#define IMAGE_SIZEOF_SHORT_NAME 8typedef struct _IMAGE_SECTION_HEADER {BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //节名称 8字节 \0结尾的ASCII码字符串 可自定义union //8字节{DWORD PhysicalAddress; //节的文件地址DWORD VirtualSize; //节内存中大小} Misc;DWORD VirtualAddress; //(RVA)节在内存中的偏移地址DWORD SizeOfRawData; //节在文件中的大小 对齐后DWORD PointerToRawData; //(FOA)节在文件中的偏移地址DWORD PointerToRelocations; //.obj文件有效,重定位的偏移 DWORD PointerToLinenumbers; //调试相关WORD NumberOfRelocations; //.obj文件有效,重定位项数目 WORD NumberOfLinenumbers; //行号表中行号的数量DWORD Characteristics; //节属性 可读 可写 可执行 60000020
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
.data 初始化的数据
.idata 导入表
.rsrc 资源数据
.reloc 基地址重定位表
.edata 导出表
.tls thread local storage 线程局部存储器
.rdata 存放调试目录和说明字符串
最后一个节区之后的40个字节 必须为0
IMAGE_EXPORT_DIRECTORY 导出表
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directorytypedef struct _IMAGE_EXPORT_DIRECTORY {DWORD Characteristics; // 未使用 0DWORD TimeDateStamp; // 创建导出表的时间戳WORD MajorVersion; // 未使用 0WORD MinorVersion; // 未使用 0DWORD Name; // 指向该导出表的文件名字符串RVADWORD Base; // 导出函数的起始序号DWORD NumberOfFunctions; // 所有导出函数的个数 不准确DWORD NumberOfNames; // 以函数名字导出的函数个数DWORD AddressOfFunctions; // 导出函数地址表RVADWORD AddressOfNames; // 导出函数名称表RVADWORD AddressOfNameOrdinals; // 导出函数序号表RVA
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
IMAGE_IMPORT_DESCRIPTOR 导入表
typedef struct _IMAGE_IMPORT_DESCRIPTOR {union {DWORD Characteristics; //导入表结束标志DWORD OriginalFirstThunk; //导入名称表(INT)的地址RVA } DUMMYUNIONNAME;DWORD TimeDateStamp; //时间戳 如果是0xFFFFFFFF为绑定导入DWORD ForwarderChain; //链表的前一个结构DWORD Name; //导入DLL文件名的地址RVADWORD FirstThunk; //导入地址表(IAT)的地址RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;//成员OriginalFirstThunk 和 FirstThunk 都指向此结构
typedef struct _IMAGE_THUNK_DATA32 {union {DWORD ForwarderString; // PBYTE 指向一个转向者字符串的RVADWORD Function; // PDWORD 被输入的函数的内存地址DWORD Ordinal; // 序号DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME 指向IMAGE_IMPORT_BY_NAME} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;//如果结构_IMAGE_THUNK_DATA32成员最高有效位为1时 低31位为导出序号,否则指向此结构
typedef struct _IMAGE_IMPORT_BY_NAME {WORD Hint; //导出序号(有些编译器不会填充此值)CHAR Name[1]; //该值长度不准确,以0结尾的字符串,导出函数名
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
_IMAGE_BASE_RELOCATION 重定位表
typedef struct _IMAGE_BASE_RELOCATION {DWORD VirtualAddress; //重定位数据所在页的RVADWORD SizeOfBlock; //当前页中重定位数据块的大小
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
PE笔记
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <stdio.h>using namespace std;int main()
{FILE* pFile = fopen("C:\\Users\\Administrator\\Desktop\\PETool.exe", "rb");fseek(pFile, NULL, SEEK_END);int nSize = ftell(pFile);fseek(pFile, NULL, SEEK_SET);char* pFileBuffer = (char*)malloc(nSize);memset(pFileBuffer, 0, nSize);fread(pFileBuffer, nSize, 1, pFile);char* a = pFileBuffer + 0x3c;int b = *((int*)a);cout << pFileBuffer << endl;cout << pFileBuffer + b << endl;FILE* pFile1 = fopen("D:\\PETool.exe", "wb");fwrite(pFileBuffer, nSize, 1, pFile1);free(pFileBuffer);fclose(pFile);return 0;
}
cout<<hex<<i<<endl; //输出十六进制数 cout<<dec<<i<<endl; //输出十进制数
cout << hex << uppercase << showbase << pDos->e_magic << endl;
抹除PE标记作业
#include "tools.h"
int main()
{int FileSize = 0;char* pFileBuffer = FileToMemory(FILE_PATH_IN, &FileSize);PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pFileBuffer;//输出 PIMAGE_DOS_HEADER 所有信息cerr << hex << uppercase << showbase << pDos->e_magic << endl;cerr << hex << uppercase << showbase << pDos->e_cblp << endl;cerr << hex << uppercase << showbase << pDos->e_cp << endl;cerr << hex << uppercase << showbase << pDos->e_cparhdr << endl;cerr << hex << uppercase << showbase << pDos->e_crlc << endl;cerr << hex << uppercase << showbase << pDos->e_cs << endl;cerr << hex << uppercase << showbase << pDos->e_csum << endl;cerr << hex << uppercase << showbase << pDos->e_ip << endl;cerr << hex << uppercase << showbase << pDos->e_lfarlc << endl;cerr << hex << uppercase << showbase << pDos->e_maxalloc << endl;cerr << hex << uppercase << showbase << pDos->e_oemid << endl;cerr << hex << uppercase << showbase << pDos->e_oeminfo << endl;cerr << hex << uppercase << showbase << pDos->e_ovno << endl;for (size_t i = 0; i < 4; i++){cerr << hex << uppercase << showbase << pDos->e_res[i] << endl;}for (size_t i = 0; i < 10; i++){cerr << hex << uppercase << showbase << pDos->e_res2[i] << endl;}cerr << hex << uppercase << showbase << pDos->e_sp << endl;cerr << hex << uppercase << showbase << pDos->e_ss << endl;cerr << hex << uppercase << showbase << pDos->e_lfanew << endl;memset(pFileBuffer, 0x00, 0x2); //抹除MZ标记memset(pFileBuffer + 2, 0x00, 0x3A); //抹除e_magic - e_lfanew 之间数据memset((&(pDos->e_lfanew))+ 1 , 0x00, 0xC0); //抹除DOS_STUBmemset((pFileBuffer + *(&(pDos->e_lfanew))), 0x00, 0x2); //抹除PE标记MemoryToFile(FILE_PATH_OUT, pFileBuffer, FileSize);return 0;
}
时间戳转时间
string StampToTime(time_t timeStamp)
{struct tm* timeinfo = nullptr;char buffer[80];timeinfo = localtime(&timeStamp);strftime(buffer, 80, "%Y年%m月%d日 %H:%M:%S", timeinfo);printf("文件创建时间: %s\n", buffer);return string(buffer);
}
PE结构体
Machine
#define IMAGE_FILE_MACHINE_UNKNOWN 0 // 适用于任何机器
#define IMAGE_FILE_MACHINE_TARGET_HOST 0x0001 // Useful for indicating we want to interact with the host and not a WoW guest.
#define IMAGE_FILE_MACHINE_I386 0x014c // Intel 386 以及后续处理器
#define IMAGE_FILE_MACHINE_R3000 0x0162 // MIPS little-endian, 0x160 big-endian
#define IMAGE_FILE_MACHINE_R4000 0x0166 // MIPS little-endian
#define IMAGE_FILE_MACHINE_R10000 0x0168 // MIPS little-endian
#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 // MIPS little-endian WCE v2
#define IMAGE_FILE_MACHINE_ALPHA 0x0184 // Alpha_AXP
#define IMAGE_FILE_MACHINE_SH3 0x01a2 // SH3 little-endian
#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3
#define IMAGE_FILE_MACHINE_SH3E 0x01a4 // SH3E little-endian
#define IMAGE_FILE_MACHINE_SH4 0x01a6 // SH4 little-endian
#define IMAGE_FILE_MACHINE_SH5 0x01a8 // SH5
#define IMAGE_FILE_MACHINE_ARM 0x01c0 // ARM Little-Endian
#define IMAGE_FILE_MACHINE_THUMB 0x01c2 // ARM Thumb/Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 Little-Endian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC Little-Endian
#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1
#define IMAGE_FILE_MACHINE_IA64 0x0200 // Intel 64
#define IMAGE_FILE_MACHINE_MIPS16 0x0266 // MIPS
#define IMAGE_FILE_MACHINE_ALPHA64 0x0284 // ALPHA64
#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 // MIPS
#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 // MIPS
#define IMAGE_FILE_MACHINE_AXP64 IMAGE_FILE_MACHINE_ALPHA64
#define IMAGE_FILE_MACHINE_TRICORE 0x0520 // Infineon
#define IMAGE_FILE_MACHINE_CEF 0x0CEF
#define IMAGE_FILE_MACHINE_EBC 0x0EBC // EFI Byte Code
#define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8)
#define IMAGE_FILE_MACHINE_M32R 0x9041 // M32R little-endian
#define IMAGE_FILE_MACHINE_ARM64 0xAA64 // ARM64 Little-Endian
#define IMAGE_FILE_MACHINE_CEE 0xC0EE
Characteristics
0102
0000 0001 0000 0010
Magic
#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b //32位PE文件
#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b //64位PE文件
#define IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 //该文件是ROM镜像
Subsystem
DllCharacteristics
DataDirectory
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#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
#define 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 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
Characteristics 节属性常用位含义