PE文件浅析

相关术语

文件偏移地址(File Offset,FOA)

数据在PE文件中的地址。这是文件在磁盘中存放是相对于文件开头的偏移。

装载基址(Image Base)

PE文件装入内存时的基地址。

虚拟内存地址(Virtual Address,VA)

PE文件中的指令被装入内存之后的地址。

相对虚拟地址(Relative Virtual Address,RVA)

内存地址相对于映射基址的偏移量。

三者关系

VA=Image Base+ RVA

PE解码器

image-20231203210957047

IMAGE_DATA_DIRECTORY 数据目录结构

1
2
3
4
typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress; /**指向某个数据的相对虚拟地址 RAV 偏移0x00**/
DWORD Size; /**某个数据块的大小 偏移0x04**/
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
1
2
3
4
5
6
7
8
9
10
//定位目录项的方法(以导出表为例):    所有操作都在FileBuffer状态下完成

//1、指向相关内容
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)(FileAddress);
PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));

//2、获取导出表的地址(目录项的第0个成员)
DWORD ExportDirectory_RAVAdd = pOptionalHeader->DataDirectory[0].VirtualAddress;
DWORD ExportDirectory_FOAAdd = 0;

IMAGE_IMPORT_DESCRIPTOR导入表

descriptor->描述符image-20231204171557646

image-20231204174520603

OriginalFirstThunk

 **这个值是一个4字节的RVA地址,这个地址指向了导入名称表(INT)**,INT是一个IMAGE_THUNK_DATA结构体数组,这个结构体的最后一个成员内容为0时数组结束。这个数组的每一个成员又指向了一个IMAGE_IMPORT_BY_NAME结构体,这个结构体包含了两个成员函数序号和函数名,不过这个序号一般没什么用,所以有的编译器会把函数序号置0。函数名可以当作一个以0结尾的字符串。

FirstThunk

  **这个值是一个4字节的RVA地址,这个地址指向了导入地址表(IAT),这个IAT和INT一样,也是一个IMAGE_THUNK_DATA结构体数组,不过它在程序载入前和载入后由两种状态,在程序载入前它的结构和内容和INT表完全一样,但却是两个不同的表,指向了IMAGE_IMPORT_BY_NAME结构体。在程序载入后,他的结构和INT表一样,但内容就不一样了,里面存放的都是导入函数的地址。

image-20231204172951439

image-20231204173247411

节表各成员意义详解。

image-20231128183309800

PE文件头保存着整个PE文件的索引信息,可以帮助PE装载器定位资源,而节则保存着整个PE文件的所有资源。正因为如此,所以存在着这样的说法:头是节的描述,节是头的具体化。

参考文章:

https://www.cnblogs.com/onetrainee/p/12938085.html

https://bbs.kanxue.com/thread-252795.htm#%E7%AC%AC%E4%B8%80%E8%8A%82%EF%BC%9Ape%E6%96%87%E4%BB%B6%E7%BB%93%E6%9E%84