LordPE下载

MS-DOS头:

兼容性,在DOS系统下提示无法运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 直接在VS里面输_IMAGE_DOS_HEADER然后CTRL点击即可看到结构定义,19个字段,重点关注第一个和最后一个
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // MS-DOS头的标志MZ,固定为4D 5A,实际值为0x5A4D
WORD e_cblp; // 文件末页的字节数
WORD e_cp; // 文件页数
WORD e_crlc; // 重定位
WORD e_cparhdr; // 区段中头部的大小
WORD e_minalloc; // 最小附加内存段的需求
WORD e_maxalloc; // 最大附加内存段的需求
WORD e_ss; // 初始SS值,用于初始化堆栈
WORD e_sp; // 初始SP值,用于初始化堆栈指针的值
WORD e_csum; // 校验和
WORD e_ip; // 初始化IP寄存器的值,即程序的入口点
WORD e_cs; // 初始化CS的值,CS:IP
WORD e_lfarlc; // 重定位表的偏移
WORD e_ovno; // Overlay number
WORD e_res[4]; // Reserved words
WORD e_oemid; // OEM厂商的标识
WORD e_oeminfo; // OEM厂商的信息
WORD e_res2[10]; // Reserved words
LONG e_lfanew; // 4个字节,偏移为37,指向新的PE头,因为这是DOS头,是为了兼容DOS系统,真正执行需要到PE头
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

编辑器:低地址存放低位数据,小端存储

注意编辑器行号的进制要变为16进制而不是十进制,偏移是十六进制的数据呀!

PE头

看清程序是32/64位

标准PE头(每个PE文件都有)和扩展PE头(可有可无)

1
2
3
4
5
6
// 32位
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; // PE标识:PE->50 45 00 00,实际值为0x00004550
IMAGE_FILE_HEADER FileHeader; // 标准NT头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 扩展头,可选
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
1
2
3
4
5
6
// 64位
typedef struct _IMAGE_NT_HEADERS64 {
DWORD Signature; // PE标识:PE->50 45 00 00,实际值为0x00004550
IMAGE_FILE_HEADER FileHeader; // 标准NT头
IMAGE_OPTIONAL_HEADER64 OptionalHeader; // 可选
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
1
2
3
4
5
6
7
8
9
10
// 标准NT头
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 运行平台,指明可运行的处理器平台
WORD NumberOfSections; // 区段数量,.text、.rdata、.data...可用于遍历区段
DWORD TimeDateStamp; // 文件创建时间戳
DWORD PointerToSymbolTable; // 指向符号表的指针,没什么用了
DWORD NumberOfSymbols; // 符号表中符号的数量
WORD SizeOfOptionalHeader; // 扩展头的大小
WORD Characteristics; // PE文件的属性,exe:0x010F,dll:0x0210
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//
// Optional header format.
//

typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//

WORD Magic; // 文件类型的标识,普通可执行文件:0x10B
BYTE MajorLinkerVersion; // 链接器的主版本号
BYTE MinorLinkerVersion; // 链接器的子版本号
DWORD SizeOfCode; // 按磁盘扇区的整倍数计算
DWORD SizeOfInitializedData; // 已初始化的数据块的大小
DWORD SizeOfUninitializedData; // 未初始化的数据块的大小
DWORD AddressOfEntryPoint; // 程序入口的RVA(相对虚拟地址)
DWORD BaseOfCode; // 代码段的起始RVA,一般为0x1000
DWORD BaseOfData; // 数据段的起始RVA,一般位于代码段之后

//
// NT additional fields.
//

DWORD ImageBase; // 程序的首选装载地址,Windows优先将文件装入到由ImageBase字段指定的地址中
DWORD SectionAlignment; // 内存中的区块的对齐大小
DWORD FileAlignment; // 文件中的区块的对齐大小
WORD MajorOperatingSystemVersion; // 要求操作系统最低版本号的主版本号
WORD MinorOperatingSystemVersion; // 要求操作系统最低版本号的子版本号
WORD MajorImageVersion; // 可运行于操作系统的主版本号
WORD MinorImageVersion; // 可运行于操作系统的子版本号
WORD MajorSubsystemVersion; // 要求最低子系统版本的主版本号
WORD MinorSubsystemVersion; // 要求最低子系统版本子主版本号
DWORD Win32VersionValue; // 保留值,必须为0
DWORD SizeOfImage; // 映像装入内存后的总尺寸
DWORD SizeOfHeaders; // 所有头(DOS头、PE头) + 区块表的尺寸大小
DWORD CheckSum; // 映像文件的校验和
WORD Subsystem; // 可执行文件期望的子系统
WORD DllCharacteristics; // DllMain()函数何时被调用,默认为 0
DWORD SizeOfStackReserve; // 初始化时的栈大小
DWORD SizeOfStackCommit; // 初始化时实际提交的栈大小
DWORD SizeOfHeapReserve; // 初始化时保留的堆大小
DWORD SizeOfHeapCommit; // 初始化时实际提交的堆大小
DWORD LoaderFlags; // 与调试有关,默认为 0
DWORD NumberOfRvaAndSizes; // 下边数据目录的项数,这个字段自Windows NT 发布以来一直是16
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; // 数据目录表
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
1
2
3
4
常用的三种地址:
VA:虚拟内存地址,00000000~0FFFFFFFh,等于进程的基址+相对虚拟内存地址
RVA:相对虚拟内存地址,记录模块相对于基址的偏移
FOA:File Offset Address,文件偏移地址,某个位置距离文件头(MZ)的偏移
1
2
3
4
5
6
// 各种标志的值,重点关注MZ和PE00
#define IMAGE_DOS_SIGNATURE 0x5A4D // MZ
#define IMAGE_OS2_SIGNATURE 0x454E // NE
#define IMAGE_OS2_SIGNATURE_LE 0x454C // LE
#define IMAGE_VXD_SIGNATURE 0x454C // LE
#define IMAGE_NT_SIGNATURE 0x00004550 // PE00
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 运行平台的宏
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 常见PE属性
#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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 可执行文件期望的子系统的值
#define IMAGE_SUBSYSTEM_UNKNOWN 0 // Unknown subsystem.
#define IMAGE_SUBSYSTEM_NATIVE 1 // Image doesn't require a subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 // Image runs in the Windows GUI subsystem.
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 // Image runs in the Windows character subsystem.
#define IMAGE_SUBSYSTEM_OS2_CUI 5 // image runs in the OS/2 character subsystem.
#define IMAGE_SUBSYSTEM_POSIX_CUI 7 // image runs in the Posix character subsystem.
#define IMAGE_SUBSYSTEM_NATIVE_WINDOWS 8 // image is a native Win9x driver.
#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Image runs in the Windows CE subsystem.
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 //
#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 //
#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 //
#define IMAGE_SUBSYSTEM_EFI_ROM 13
#define IMAGE_SUBSYSTEM_XBOX 14
#define IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16
#define IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG 17

区段

区段表:

区段表

常用区段:

  • .text / .code:代码段
  • .data:可读写的数据段,存放全局变量和静态变量
  • .rdata:只读数据段
  • .idata:存放导入表信息
  • .edata:存放导出表信息
  • .rsrc:资源段
  • .bss:未初始化的数据
  • .crt:用于C++ 运行时(CRT)所添加的数据
  • .tls:TLS是线程局部存储器,用于支持通过_declspec(thread)声明的线程局部存储变量的数据,这包括数据的初始化值,也包括运行时所需要的额外变量
  • .reloc:可执行文件的基址重定位,基址重定位一般仅Dll需要的
  • .sdata:相对于全局指针的可被定位的 短的读写数据
  • .pdata:异常表,包含CPU特定的IAMGE_RUNTIME_FUNTION_ENTRY结构数组,DataDirectory中的IMAGE_DIRECTORY_ENTRY_EXCEPTION指向它.
  • .didata:延迟装入输入数据,在非Release模式下可以找到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 每一个区段都是这样一个结构
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;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//
// 区段属性
//
// 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.

数据目录表

1
2
3
4
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; // 虚拟地址
DWORD Size; // 尺寸
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 目录顺序
#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
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data,保留字段,必须为0
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // 即INT的RVA
} 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 forwarders
DWORD Name; // 动态链接库的名称(kernel32.dll)的RVA
DWORD FirstThunk; // 即IAT的RVA
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD 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;
1
2
3
4
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint; // 需要导入函数的函数序号
CHAR Name[1]; // 需要导入函数的函数名
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

导出表

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics; // 保留段,一直为0
DWORD TimeDateStamp; // 创建时间
WORD MajorVersion;
WORD MinorVersion;
DWORD Name; // 指向模块名的RVA
DWORD Base; // 输出API函数的索引值的基数(1)
DWORD NumberOfFunctions; // 导出函数个数
DWORD NumberOfNames; // 导出函数名个数,很多函数没有函数名
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

重定位表

主要用于动态链接库

1
2
3
4
5
6
typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress; // 需要重定位的RVA,一般为0x1000的倍数
DWORD SizeOfBlock; // 结构体和数组TypeOffset的体积总和
// WORD TypeOffset[1];
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;
1
2
3
4
5
typedef struct _TYPE
{
WORD Offset : 12;
WORD Type : 4; // 4bit
}TYPE, *PTYPE;

TLS表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IMAGE_TLS_DIRECTORY32 {
DWORD StartAddressOfRawData; // 内存起始地址
DWORD EndAddressOfRawData; // 内存结束地址
DWORD AddressOfIndex; // TLS的索引
DWORD AddressOfCallBacks; // PIMAGE_TLS_CALLBACK *,回调函数的一个数组
DWORD SizeOfZeroFill; // 零填充区域的长度
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;

延迟导入表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {
union {
DWORD AllAttributes;
struct {
DWORD RvaBased : 1; // Delay load version 2
DWORD ReservedAttributes : 31;
} DUMMYSTRUCTNAME;
} Attributes; // 属性

DWORD DllNameRVA; // 动态链接库名称的RVA
DWORD ModuleHandleRVA; // 模块句柄的RVA
DWORD ImportAddressTableRVA; // IAT的RVA
DWORD ImportNameTableRVA; // INT的RVA
DWORD BoundImportAddressTableRVA; // RVA to an optional bound IAT
DWORD UnloadInformationTableRVA; // RVA to an optional unload info table
DWORD TimeDateStamp; // 0 if not bound,
// Otherwise, date/time of the target DLL

} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;

typedef const IMAGE_DELAYLOAD_DESCRIPTOR *PCIMAGE_DELAYLOAD_DESCRIPTOR;

资源表

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics; // 00000000
DWORD TimeDateStamp; // 资源创建时间
WORD MajorVersion; // 4
WORD MinorVersion; // 0
WORD NumberOfNamedEntries; // 以字符串作为资源标识的条目的个数
WORD NumberOfIdEntries; // 以数字作为资源标识的条目的个数
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union {
struct {
DWORD NameOffset:31; // 资源表的偏移
DWORD NameIsString:1; // 资源为字符串,值为1表示资源为字符串->开发者的资源,不为1表示是系统资源
} DUMMYSTRUCTNAME;
DWORD Name; // 资源的类型即资源名
WORD Id; // 资源的数字ID
} DUMMYUNIONNAME;
union {
DWORD OffsetToData; // 数据偏移的地址
struct {
DWORD OffsetToDirectory:31; // 子目录偏移的地址
DWORD DataIsDirectory:1; // 数据是目录,值为1表示数据是目录
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
1
2
3
4
typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
WORD Length; // 字符串长度
WCHAR NameString[ 1 ]; // 字符串数组
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;
1
2
3
4
5
6
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData; // 资源数据的RVA
DWORD Size; // 资源数据的长度
DWORD CodePage; // 代码页
DWORD Reserved; // 保留字段
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
1
2
3
4
5
6
7
8
9
10
// 资源总数 = NumberOfNamedEntries + NumberOfIdEntries
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
WORD NumberOfNamedEntries;
WORD NumberOfIdEntries;
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

PE文件解析代码

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once
#include <stdio.h>
#include <Windows.h>

// 计算数据目录表起始位置到文件头(MZ)的偏移
DWORD RvaToOffset(DWORD dwRva, char *buffer);

// 解析导入表的函数
void ImportTbale(char *buffer);
// 解析导出表的函数
void ExportTable(char *buffer);
// 解析重定位表的函数
void RelocTable(char *buffer);

// 解析TLS的函数
void TLSTable(char *buffer);

// 解析延迟导入表的函数
void DelayImport(char *buffer);

主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
#include "Entry.h"

int main()
{
FILE *pFile = NULL;
errno_t err;
char *buffer;
int nFileLength = 0;
err = fopen_s(&pFile, "D:\\desktop\\PEiD.exe", "rb"); // 二进制形式读取PE文件,注意反斜杠前面需要再加一个反斜杠进行转义
fseek(pFile, 0, SEEK_END);
nFileLength = ftell(pFile);
rewind(pFile);
int imageLength = nFileLength * sizeof(char) + 1;
buffer = (char *)malloc(imageLength);
memset(buffer, 0, nFileLength * sizeof(char) + 1);
fread(buffer, 1, imageLength, pFile);

// DOS头解析
PIMAGE_DOS_HEADER Read_Dos_Header;
Read_Dos_Header = (PIMAGE_DOS_HEADER)buffer;
printf("--- MS DOS HEADER ---\n");
printf("DOS标志位:%x\n", Read_Dos_Header->e_magic);
printf("PE头偏移:%x\n\n", Read_Dos_Header->e_lfanew);

// PE头解析
printf("--- PE HEADER ---\n");
PIMAGE_NT_HEADERS Read_NT_Headers; // PE头,包含标准PT头和扩展PE头
Read_NT_Headers = (PIMAGE_NT_HEADERS)(buffer + Read_Dos_Header->e_lfanew);
printf("PE标志位:%x\n", Read_NT_Headers->Signature);
printf("运行平台:%x\n", Read_NT_Headers->FileHeader.Machine); // FileHeader: 标准头
printf("程序入口点:%x\n\n", Read_NT_Headers->OptionalHeader.ImageBase); // OptionalHeader:扩展头

// 区段解析遍历, 根据标准PE头中的NumberOfSections进行遍历
PIMAGE_SECTION_HEADER Read_Section_Header = IMAGE_FIRST_SECTION(Read_NT_Headers);
printf("--- Section HEADER ---\n");
PIMAGE_FILE_HEADER PFileHeder = &(Read_NT_Headers->FileHeader);
for (int i = 0; i < PFileHeder->NumberOfSections; ++i)
{
printf("Name(区段名):%s\n", Read_Section_Header[i].Name);
printf("VOffset(起始的相对虚拟地址):%08X\n", Read_Section_Header[i].VirtualAddress);
printf("VSize(内存区段大小):%08X\n", Read_Section_Header[i].SizeOfRawData);
printf("ROffset(文件偏移):%08X\n", Read_Section_Header[i].PointerToRawData);
printf("RSize(文件区段大小):%08X\n", Read_Section_Header[i].Misc.VirtualSize);
printf("标记(区段属性):%08X\n\n", Read_Section_Header[i].Characteristics);
}

printf("---导入表信息---\n");
ImportTbale(buffer);

printf("---导入表信息---\n");
ExportTable(buffer);

/*printf("---重定位表信息---\n");
RelocTable(buffer);*/

printf("---TLS表信息---\n");
TLSTable(buffer);

printf("---延迟导入表表信息---\n");
DelayImport(buffer);

free(buffer);
return 0;
}

// 地址转换函数
// dwRva是某个数据目录表项的VirutualAddress
// buffer是读取到的PE文件缓冲区
DWORD RvaToOffset(DWORD dwRva, char *buffer)
{
// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 区段表
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNT);

// 判断是否落在头部中
if (dwRva < pSection[0].VirtualAddress)
{
return dwRva;
}
for (int i = 0; i < pNT->FileHeader.NumberOfSections; ++i)
{
// VirtualAddress:起始地址
// Size:长度
// VirtualAddress+Size:结束地址
// 判断是否落在某个区段内
if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize)
{
// dwRva - pSection[i].VirtualAddress对应数据目录表起始地址到区段起始地址的偏移Offset
// pSection[i].PointerToRawData: 区段到文件头的偏移Offset
// 返回数据目录表起始地址到文件头的偏移Offset
return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
}
}
return 0;
}

void ImportTbale(char *buffer)
{
// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 定位导入表
PIMAGE_DATA_DIRECTORY pImoprtDir = (PIMAGE_DATA_DIRECTORY)(pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT);

// 填充结构
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(RvaToOffset(pImoprtDir->VirtualAddress, buffer) + buffer);
while (pImport->Name != NULL)
{
char *szDllName = (char *)(RvaToOffset(pImport->Name, buffer) + buffer);
printf("DLL名称: %s\n", szDllName);
printf("日期时间标志: %08x\n", pImport->TimeDateStamp);
printf("ForwarderChain: %08x\n", pImport->ForwarderChain);
printf("名称OFFSET: %08x\n", pImport->Name);
printf("FirstThunk: %08x\n", pImport->FirstThunk);
printf("OriginalFirstThunk: %08x\n\n", pImport->OriginalFirstThunk);

// 指向导入地址表的RVA
PIMAGE_THUNK_DATA pIat = (PIMAGE_THUNK_DATA)(RvaToOffset(pImport->OriginalFirstThunk, buffer) + buffer);
DWORD index = 0;
DWORD ImportOffset = 0;
// 被导入函数的序号
while (pIat->u1.Ordinal != 0)
{
printf("ThunkRva: %08x\n", pImport->OriginalFirstThunk + index);
ImportOffset = RvaToOffset(pImport->OriginalFirstThunk, buffer);
printf("ThunkOffset: %08x\n", ImportOffset + index);
index += 4;
if ((pIat->u1.Ordinal & 0x80000000) != 1)
{
PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToOffset(pIat->u1.AddressOfData, buffer) + buffer);
printf("API名称:%s\n", pName->Name);
printf("Hint(API序号):%04x\n", pName->Hint);
printf("ThunkValue:%08x\n\n", pIat->u1.Function); // 被导入函数的地址
}
pIat++;
}
pImport++;
}

}

void ExportTable(char *buffer)
{
// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 定位数据目录表中的导出表
PIMAGE_DATA_DIRECTORY pExportDir = pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;

// 填充导出表结构
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(RvaToOffset(pExportDir->VirtualAddress, buffer) + buffer);
char *szName = (char *)(RvaToOffset(pExport->Name, buffer) + buffer);
if (pExport->AddressOfFunctions == 0)
{
printf("当前没有导出表!\n\n");
return;
}
printf("导出表OFFSET:%08x\n", RvaToOffset(pExportDir->VirtualAddress, buffer));
printf("特征值:%08x\n", pExport->Characteristics);
printf("基数:%08x\n", pExport->Base);
printf("名称OFFSET:%08x\n", pExport->Name);
printf("名称:%s\n", szName);
printf("函数数量:%08x\n", pExport->NumberOfFunctions);
printf("函数名数量:%08x\n", pExport->NumberOfNames);
printf("函数地址:%08x\n", pExport->AddressOfFunctions);
printf("函数名地址:%08x\n", pExport->AddressOfNames);
printf("函数名称序号地址:%08x\n", pExport->AddressOfNameOrdinals);
// 函数数量
DWORD dwNumOfFun = pExport->NumberOfFunctions;
// 函数名数量
DWORD dwNumOfNames = pExport->AddressOfNames;
// 基数
DWORD dwBase = pExport->Base;
// 导出地址表
PDWORD pEat32 = (PDWORD)(RvaToOffset(pExport->AddressOfFunctions, buffer) + buffer);
// 导出名称表
PDWORD pEnt32 = (PDWORD)(RvaToOffset(pExport->AddressOfNames, buffer) + buffer);
// 导出序号表
PWORD pId = (PWORD)(RvaToOffset(pExport->AddressOfNameOrdinals, buffer) + buffer);
for (int i = 0; i < dwNumOfFun; ++i)
{
if (pEat32[i] == 0)
{
continue;
}
DWORD Id = 0;
for (; Id < dwNumOfNames; ++Id)
{
if (pId[Id] == i)
{
break;
}
}
if (Id == dwNumOfNames)
{
printf("Id: %x Address: 0x%08x Name[NULL]\n", i + dwBase, pEat32[i]);
}
else
{
char *szFunName = (char *)(RvaToOffset(pEnt32[Id], buffer) + buffer);
printf("Id: %x Address: 0x%08x Name[%s]\n", i + dwBase, pEat32[i], szFunName);
}
}
}

void RelocTable(char *buffer)
{
typedef struct _TYPE
{
WORD Offset : 12;
WORD Type : 4; // 4bit
}TYPE, *PTYPE;

// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 定位重定位表
PIMAGE_DATA_DIRECTORY pRelocDir = (pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC);

// 填充重定位表的结构
PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(RvaToOffset(pRelocDir->VirtualAddress, buffer) + buffer);

// 定位区段
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNT);

while (pReloc->SizeOfBlock != 0)
{
// 找到本0x1000字节的起始位置
DWORD dwCount = (pReloc->SizeOfBlock - 8) / 2; // 需要重定位的个数
DWORD dwRva = (pReloc->VirtualAddress);
PTYPE pRelocArr = (PTYPE)(pReloc + 1);
printf("区段: %s\n", pSection->Name);
printf("RVA: %08x\n", dwRva);
printf("项目: %X H / %d\n", pReloc->SizeOfBlock, pReloc->SizeOfBlock); // 进制不同

// 找到下一个0x1000字节的结构体
pReloc = (PIMAGE_BASE_RELOCATION)((char *)pReloc + pReloc->SizeOfBlock);

for (int i = 0; i < dwCount; ++i)
{
PDWORD pData = (PDWORD)(RvaToOffset(pRelocArr[i].Offset + dwRva, buffer) + buffer);
DWORD pDataOffset = RvaToOffset(pRelocArr[i].Offset + dwRva, buffer);
printf("RVA: %08x\n", pRelocArr[i].Offset + dwRva);
printf("区段: %08x\n", *pData);
printf("偏移: %08x\n\n", pDataOffset);
}
}
}

void TLSTable(char *buffer)
{
// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 定位数据目录表中的TLS表
PIMAGE_DATA_DIRECTORY pTLSDir = (pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS);

// 填充TLS结构
PIMAGE_TLS_DIRECTORY pTLS = (PIMAGE_TLS_DIRECTORY)(RvaToOffset(pTLSDir->VirtualAddress, buffer) + buffer);

printf("数据块开始VA:%08x\n", pTLS->StartAddressOfRawData);
printf("数据块结束VA:%08x\n", pTLS->EndAddressOfRawData);
printf("索引变量VA:%08x\n", pTLS->AddressOfIndex);
printf("回调表VA:%08x\n", pTLS->AddressOfCallBacks);
printf("填0大小:%08x\n", pTLS->SizeOfZeroFill);
printf("特征值:%08x\n\n", pTLS->Characteristics);
}

void DelayImport(char *buffer)
{
// DOS头
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;

// PE头
PIMAGE_NT_HEADERS pNT = (PIMAGE_NT_HEADERS)(buffer + pDos->e_lfanew);

// 定位数据目录表中的延迟导入表
PIMAGE_DATA_DIRECTORY pDelayLoadDir = (pNT->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);

// 填充延迟导入表的数据结构
PIMAGE_DELAYLOAD_DESCRIPTOR pDelayLoad = (PIMAGE_DELAYLOAD_DESCRIPTOR)(RvaToOffset(pDelayLoadDir->VirtualAddress, buffer) + buffer);

while (pDelayLoad->DllNameRVA != NULL)
{
char *szDllName = (char *)(RvaToOffset(pDelayLoad->DllNameRVA, buffer) + buffer);
printf("DllName: %s\n", szDllName);
printf("Attributes: %08X\n", pDelayLoad->Attributes);
printf("ModuleHandleRVA: %08X\n", pDelayLoad->ModuleHandleRVA);
printf("ImportAddressTableRVA: %08X\n", pDelayLoad->ImportAddressTableRVA);
printf("ImportNameTableRVA: %08X\n", pDelayLoad->ImportNameTableRVA);
printf("BoundImportAddressTableRVA: %08X\n", pDelayLoad->BoundImportAddressTableRVA);
printf("UnloadInformationTableRVA: %08X\n", pDelayLoad->UnloadInformationTableRVA);
printf("TimeDateStamp: %08X\n\n", pDelayLoad->TimeDateStamp);
pDelayLoad++;
}
}