一: 再读PE 文件结构启发式学习。
该系列帖子是我初入看雪论坛时写的。时隔几年,再读。概要如下:
a. pe 结构有 文件头,节表,节构成。
节表个数由 _IMAGE_FILE_HEADER.NumberOfSections 确定
二:几个数据结构
2.1 NT 文件头
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //这里的标记是 PE00
IMAGE_FILE_HEADER FileHeader; //NT header 包含FILE header 和Option header
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
2.1.1 文件头
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; 0x014c -- x86
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
2.1.2 可选文件头
// 很多不重要, 有的也与其它定义,例如节表内容有重复嫌疑
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic; // 010b -- NT HDR32
BYTE MajorLinkerVersion; //link version 不重要
BYTE MinorLinkerVersion;
DWORD SizeOfCode; //代码段大小
DWORD SizeOfInitializedData; //初始化数据大小
DWORD SizeOfUninitializedData; //未初始化数据大小
DWORD AddressOfEntryPoint; //程序入口点: 重要
DWORD BaseOfCode; //代码基址(RVA)
DWORD BaseOfData; //数据基址(RVA)
//
// NT additional fields.
//
DWORD ImageBase; //模块基址
DWORD SectionAlignment; //内存对齐调整
DWORD FileAlignment; //文件对齐调整
WORD MajorOperatingSystemVersion; //版本信息,不重要
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage; //模块的大小
DWORD SizeOfHeaders; //header的大小
DWORD CheckSum; //未使用
WORD Subsystem; //02 为gui, 03 是console
WORD DllCharacteristics; //dll 用
DWORD SizeOfStackReserve; //系统加载堆和栈初始化信息
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;z // 不重要
DWORD NumberOfRvaAndSizes; // 总是16
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //16个目录,重要,其中第一个是导出表地址,大小,第二个是导入表地址大小,第三个是资源表地址大小,第13个是导入地址表地址大小.
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
2.2 节表: 用来完成从文件到内存的映射.
section header:
2 行半, 40 个字节, 8个dword + 8bytes 名字
可以实现文件偏移到rva 的地址转换。节中也包含重定位信息
section header 的结构是这样定义的 ,
#define IMAGE_SIZEOF_SHORT_NAME 8
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc;
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations; // 重定位指针, 在exe 中为空
DWORD PointerToLinenumbers; // 行号指针
WORD NumberOfRelocations; // 重定位个数
WORD NumberOfLinenumbers; // 行号个数
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
2.3 导入表
名称和地址是重要的, 他们都是rva
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,
DWORD ForwarderChain;
DWORD Name; // RVA to name, 导入的dll 文件名称
DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses), 指向导入函数地址表.
} IMAGE_IMPORT_DESCRIPTOR;
2.4 导入地址表,
导入地址表中的每一项4个byte都是RVA 地址项, 需要由loader 改写, 指向导入函数名称
该地址项在文件中指向一个import_by_name 结构,用来说明它是导入dll中的第几项, 导入函数名是什么.
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
BYTE Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
导入表和导入地址表共同占据着.rdata 段. 对用户而言,它们都是只读数据段.