PE文件结构和调试

参考博客

  • https://www.cnblogs.com/night-ride-depart/p/5776107.html
  • https://www.cnblogs.com/lanuage/p/7725699.html

1.PE文件结构

PE文件的结构一般来说如下图所示:从起始位置开始依次是DOS头,NT头,节表以及具体的节。DOS头是用来兼容MS-DOS操作系统的,目的是当这个文件在MS-DOS上运行时提示一段文字,大部分情况下是:This program cannot be run in DOS mode.还有一个目的,就是指明NT头在文件中的位置。 NT头包含windows PE文件的主要信息,其中包括一个‘PE’字样的签名,PE文件头(IMAGE_FILE_HEADER)和PE可选头(IMAGE_OPTIONAL_HEADER32),头部的详细结构以及其具体意义在PE文件头文章中详细描述。

文件结构定义有两种地址偏移,一种是FOA(文件偏移地址),另外一种是RVA(相对虚拟地址)。
RVA = 虚拟地址-ImageBase
FOA: 文件偏移. 就是文件中所在的地址.

1通过虚拟地址偏移获取导入描述表
hMod = GetModuleHandle(NULL);
pAddr = (PBYTE)hMod;  //这里的pAddr就是一个ImageBase的虚拟地址
// pAddr = VA to PE signature (IMAGE_NT_HEADERS) 
pAddr += *((long long*)&pAddr[0x3C]);  //根据结构体大小计算位0x3c,相对偏移量

// dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table  
dwRVA = *((long long*)&pAddr[0x80]);
2通过文件地址偏移获取导入描述表

在这里插入图片描述
其中332表达的是0x014c 对应IMAGE_FILE_MACHINE_I386 // Intel 386
NumberOfSections:该PE文件中有多少个节,也就是节表中的项数。
TimeDateStamp:PE文件的创建时间,一般由连接器填写。
PointerToSymbolTable:COFF文件符号表在文件中的偏移。
NumberOfSymbols:符号表的数量。
SizeOfOptionalHeader:紧随其后的可选头的大小。
Characteristics:可执行文件的属性,可以是下面这些值按位相或。

	unsigned int RVAToFOA(LPVOID buf, long long rva) {
		if (buf == NULL) {
			return 0;
		}
		PIMAGE_DOS_HEADER pdosHeader = (PIMAGE_DOS_HEADER)buf;
		PIMAGE_NT_HEADERS pNtheader = (PIMAGE_NT_HEADERS)((long long)buf + (long long)pdosHeader->e_lfanew);
		PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((long long)buf + 4 + (long long)pdosHeader->e_lfanew);
		PIMAGE_OPTIONAL_HEADER pOptHeader = (PIMAGE_OPTIONAL_HEADER)((long long)pFileHeader + sizeof(IMAGE_FILE_HEADER));
		PIMAGE_SECTION_HEADER pSectionheader = (PIMAGE_SECTION_HEADER)((long long)pFileHeader + sizeof(IMAGE_FILE_HEADER) + pFileHeader->SizeOfOptionalHeader);
		for (int i = 0; i <= pFileHeader->NumberOfSections; i++) {
			if (rva >= (pSectionheader->VirtualAddress) && rva < (pSectionheader->VirtualAddress + pSectionheader->Misc.VirtualSize)) {
				return (rva - pSectionheader->VirtualAddress) + pSectionheader->PointerToRawData;
			}
			pSectionheader++;
		}
		return 0;
	}

OriginalFirstThunk 指向的数组通常叫做 hint-name table,即 HNT ,他在 PE 加载到内存中时被保留了下来且永远不会被修改。但是在 Windows 加载过 PE 到内存之后,Windows 会重写 FirstThunk 所指向的数组元素中的内容,使得数组中每个 IMAGE_THUNK_DATA 不再表示指向带有函数描述的 IMAGE_THUNK_DATA 元素,而是直接指向了函数地址。此时,FirstThunk 所指向的数组就称之为输入地址表(Import Address Table ,即经常说的 IAT)。
在这里插入图片描述
在这里插入图片描述

typedef struct _IMAGE_THUNK_DATA32 {
    union {
        DWORD ForwarderString;      // PBYTE 
        DWORD Function;             // PDWORD
        DWORD Ordinal;
        DWORD AddressOfData;        // PIMAGE_IMPORT_BY_NAME
    } u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

我们可以看出由于是union结构,所以IMAGE_THUNK_DATA 事实上是4个字节大小。
这个共用体是怎么使用的呢:
当 IMAGE_THUNK_DATA 值的最高位为 1时,表示函数以序号方式输入,这时候低 31位被看作一个函数序号。
当 IMAGE_THUNK_DATA 值的最高位为 0时,表示函数以字符串类型的函数名方式输入,这时双字的值是一个 RVA,指向一个 IMAGE_IMPORT_BY_NAME 结构。
接下来说明IMAGE_IMPORT_BY_NAME 结构:

	PDWORD OriginalFirstThunk = (PDWORD)((long long)FileBuffer + RVAToFOA(FileBuffer, ImageImportDescriptor->OriginalFirstThunk));
			PDWORD FirstThunk = (PDWORD)((long long)FileBuffer + RVAToFOA(FileBuffer, ImageImportDescriptor->FirstThunk));
			while (*OriginalFirstThunk != 0)
			{

				long long OriginalFirstThunkBestHighBit = (*OriginalFirstThunk & 0x80000000) >> 31;
				if (OriginalFirstThunkBestHighBit == 1)
				{
					long long OriginalFirstThunkSerialNumber = (*OriginalFirstThunk & 0x6FFFFFFF);
					printf("序号为->%x\n", OriginalFirstThunkSerialNumber);
				}
				else
				{
					PIMAGE_IMPORT_BY_NAME OriginalFirstThunkFunctionName = (PIMAGE_IMPORT_BY_NAME)((long long)FileBuffer + RVAToFOA(FileBuffer, *OriginalFirstThunk));
					printf("HIT为->%x\n", OriginalFirstThunkFunctionName->Hint);
					printf("函数名为->%s\n", OriginalFirstThunkFunctionName->Name);
				}
				OriginalFirstThunk++;
			}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值