首先:pe文件结构
1、DOS头,对应结构体IMAGE_DOS_HEADER。
2、DOS存根,大小不定,由编译器决定,没有对应的结构体。
3、NT头,对应结构体IMAGE_NT_HEADER,包括3部分定义如下:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
4、节区头,对应结构体IMAGE_SECTION_HEADER,有多少个节区就有多少个连续的IMAGE_SECTION_HEADER结构体。
5、各个节区。
3处Characteristics分别位于IMAGE_FILE_HEADER、IMAGE_OPTIONAL_HEADER、IMAGE_SECTION_HEADER结构体中
1、IMAGE_FILE_HEADER结构体定义:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; // 定义该pe文件运行在那种cpu上
WORD NumberOfSections; // 定义节区头有多少个连续IMAGE_SECTION_HEADER结构体
DWORD TimeDateStamp; // 编译器生成该pe文件的时间
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32或者IMAGE_OPTIONAL_HEADER64大小,常用来定位节区头位置
WORD Characteristics; // 用于表示该pe文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
在WinNT.h中有定义(只列举部分):
#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 // File is executable
#define IMAGE_FILE_SYSTEM 0x1000 // System File.
#define IMAGE_FILE_DLL 0x2000 // File is a DLL.
#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 // Bytes of machine word are reversed.
// 处理机的高位字节是相反的
有样本中专门检测0x8000,如果没有就申请可读写内存空间,有就申请可执行读写的内存空间,如图:
2、IMAGE_OPTIONAL_HEADER结构体定义
typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; // 32位pe文件为0x010B,64位pe文件为0x020B
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint; // 该pe文件入口点RVA
DWORD BaseOfCode;
DWORD BaseOfData;
DWORD ImageBase; // 编译器生成的初始镜像加载地址
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; // 该pe文件对应镜像在内存中总大小,pe加载器根据此处值决定申请连续内存空间大小
DWORD SizeOfHeaders; // 该pe文件所有头的总长度,dos头+dos存根+NT头+节区表头,用来把"头"从文件到拷贝到镜像长度
DWORD CheckSum; // 该pe文件校验和
WORD Subsystem;
WORD DllCharacteristics; // ?????????????
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes; // 指明DataDirectory数组元素个数,一直是0x00000010
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
这里DllCharacteristics,好多书上翻译成DLL何时被调用,这个理解感觉…………
找到宏定义:
#define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040 // DLL can move.
用od调试样本时发现,这里有0x0040标志时,每次重新调试,镜像基址都会变化(ASLR),把40修改成00,即使有重定位节区,也不会重定位行为,od调试就方便了许多。
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_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
// 最后一个保留,未使用
3、IMAGE_SECTION_HEADER结构体定义:
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 节区名,最大长度8个字节
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;
根据在文件和镜像中的大小和偏移完成从文件到镜像的加载,根据在镜像中位置和大小结合Characteristics值,使用VirtualProtect修改该节区在镜像中的内存属性。
注意:pe文件的节区是多个。