0%

PE结构学习

PE文件结构

1.可执行文件

在windows平台下的可执行文件的格式,成为PE文件结构(Portable Executable);

在Linux平台下的可执行文件格式成为ELF文件结构(Executable and Linking Format)


2.PE指纹

在初始位置为 4D 5A (十进制为MZ) 而在0x3c后 有一个0x0100h() 指向0x0100h(PE)处


3.PE整体结构

下图是在github上大佬滴水的学习笔记上截的


在visual studio中 可以调用 <windows.h>去查看PE的结构体

1
2
3
4
5
#include <windows.h>
_IMAGE_DOS_HEADER;
_IMAGE_NT_HEADERS;
_IMAGE_SECTION_HEADER

其中WORD类型 占2字节;DOWRD类型 占4个字节;BYTE类型 占1个字节

也可以调用python中的pefile库查看pe文件结构 (pip install pefile)

1
2
3
4
5
import pefile

pe = pefile.PE('1.exe')
print(pe)


4.PE文件整体结构解析

##### 4.1 DOS头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
WORD e_magic; // Magic number
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;

WORD e_magic; // Magic number 标志位(MZ 4D5A 占2个字节)

Long e_lfanew; // File address of new exe header 偏移位置(指向下一个文件前面)


4.2 PE(NT)文件头
1
2
3
4
5
6
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //PE标识
IMAGE_FILE_HEADER FileHeader; //标准PE头
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //拓展PE头
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

第一部分为PE文件头标志,DWORD Signature; (50450000 dowrd占4个字节)

4.2.1 标准PE头

第二部分为IMAGE_FILE_HEADER 占20字节 称之为标准PE头

1
2
3
4
5
6
7
8
9
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //识别CPU
WORD NumberOfSections; //表示节的数量
DWORD TimeDateStamp; //时间戳
DWORD PointerToSymbolTable; //调试相关
DWORD NumberOfSymbols; //调试相关
WORD SizeOfOptionalHeader; //拓展PE头的大小
WORD Characteristics; //文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

第一个成员 WORD Machine CPU识别

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
#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 WCEv2
#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-2Little-Endian
#define IMAGE_FILE_MACHINE_ARMNT 0x01c4 // ARM Thumb-2 LittleEndian
#define IMAGE_FILE_MACHINE_AM33 0x01d3
#define IMAGE_FILE_MACHINE_POWERPC 0x01F0 // IBM PowerPC LittleEndian
#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

第二个成员 WORD NumberOfSections 表示当前PE文件中节的数量

第三个成员DWORD TimeDateStamp 表示编译器编译时的时间戳

第四个成员DWORD PointerToSymbolTable 和第五个成员DWORD NumberOfSymbols为调试相关暂不去了解

第六个成员WORD SizeOfOptionalHeader 表示PE拓展头的大小

第七个成员WORD Characteristics; 表示当前PE文件属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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.


4.2.2 拓展PE头

第三部分为_IMAGE_OPTIONAL_HEADER PE可选头,在32位下是224字节,在64位下是240字节,下面位32位下的拓展PE头

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
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic; //文件类型标识 0x10B 32位镜像文件 0x20B;0x107 ROM镜像 ;64位镜像文件
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode; // .text总大小
DWORD SizeOfInitializedData; // .data总大小
DWORD SizeOfUninitializedData;// .bss总大小
DWORD AddressOfEntryPoint; // 程序虚拟入口地址
DWORD BaseOfCode; // 代码基址
DWORD BaseOfData; // 数据基址
//
// NT additional fields.
//
DWORD ImageBase; // 程序入口 dll默认是10000000h windwos ce的exe默认是00010000h windows系列exe默认是004000000h
DWORD SectionAlignment; // 内存对齐
DWORD FileAlignment; // 文件对齐
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; // PE总大小
DWORD SizeOfHeaders; // 头的总大小
DWORD CheckSum; // 校验和(用于检测文件是否被修改)
WORD Subsystem;
WORD DllCharacteristics; //dll文件特性
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;

WORD Magic;文件类型标识

1
2
3
0x10B表明这是一个32位镜像文件。
0x107表明这是一个ROM镜像。
0x20B表明这是一个64位镜像文件。

BYTE MajorLinkerVersion; 链接器主版本号(BYTE 2位16)

BYTE MinorLinkerVersion; 链接器副版本号

DWORD SizeOfCode; .text总大小

DWORD SizeOfInitializedData; .data总大小

DWORD SizeOfUninitializedData; .bss总大小

bss:BSS段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域。特点是可读写的,在程序执行之前BSS段会自动清0。


WORD DllCharacteristics; PE文件特性

1
2
3
4
5
6
7
8
9
10
11
12
#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
//seh的保护机制
#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


4.3 PE节表

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; //在内存中的偏移地址 加上ImageBase才是内存的真正地址
DWORD SizeOfRawData; //本节在磁盘中的大小
DWORD PointerToRawData; //本节在磁盘中的偏移量
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics; //节的属性
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;



....




练习

1.判断样本为dll还是exe

样本3的文件特征位 0x2102 根据文件特征表 0x2000(dll)+0x0100(32位)+0x0002(可执行文件)

说明该文件为一个可执行的32位dll文件

样本4的文件特征为0x0103 即(0x0100+ 0x0002+0x0001) 说明是一个32位可执行的exe

因此可以判断样本3为dll文件,样本4为exe文件(如果为XX 01就是一个exe文件,如果是XX 2X说明是dll文件)

2.计算样本的编译时间

时间戳在NT头后


可根据此找到样本3的时间戳为 0x169FCED5 后面 0x00E0 -> 224 说明是32位

计算方法如下



3.在OD中去对照查看地址

样本3中

AddressOfEntryPoint 程序虚拟入口地址为 0x0002AE57h

BaseOfCode 代码基址为 0x00001000h

BaseOfData 数据基址为 0x0003F000h

ImageBase0x10000000h

使用OD打开后 第一行为 AE57 即 程序的虚拟入口地址

第一行的地址为 1000h 即BaseOfCode (代码基址地址)

前面为1000 0000h 即 ImageBase(dll默认为 0x10000000h)

欢迎关注我的其它发布渠道

------------- 💖 🌞 本 文 结 束 😚 感 谢 您 的 阅 读 🌞 💖 -------------