PE文件结构复习

突然想到好久没有复习PE文件结构了,正好又到了写博客的日子,话不多说,开淦!

#include "Entry.h"


int main()
{
	//文件读写
	FILE * pFile = NaULL;
	char * buffer;ep
	int nFileLength = 0;
	pFile =fopen("C:\\Users\\10129\\Desktop\\notepad++.e xe", "rb");
	//pFile = fopen("C:\\Users\\10129\\Desktop\\user32.dll", "rb");
	fseek(pFile, 0, SEEK_END);
	nFileLength = ftell(pFile);
	rewind(pFile);
	int imageLength = nFileLength * sizeof(char) + 1;
	buffer = (char *)malloc(imageLength);
	memset(buffer, 0, nFileLength * sizeof(char) + 1);
	fread(buffer, 1, imageLength, pFile);
	//MS-DOS头解析
	PIMAGE_DOS_HEADER ReadDosHeader;
	ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;
	printf("MS-DOS Info:\n");
	printf("MZ标志位:%x\n", ReadDosHeader->e_magic);
	printf("PE头偏移:%x\n", ReadDosHeader->e_lfanew);
	printf("==================================================================\n");
	printf("PE Header Info:\n");
	PIMAGE_NT_HEADERS32 ReadNTHeaders;
	//PE头解析
	ReadNTHeaders = (PIMAGE_NT_HEADERS32)(buffer + ReadDosHeader->e_lfanew);
	//PE头标志
	printf("PE标志位:%x\n", ReadNTHeaders->Signature);
	//标准PE头字段
	printf("运行平台:%x\n", ReadNTHeaders->FileHeader.Machine);
	//扩展PE头字段
	printf("ImageBase:%x\n", ReadNTHeaders->OptionalHeader.ImageBase);
	printf("==================================================================\n");
	printf("Section Header Info:\n");
	//区段解析遍历
	PIMAGE_SECTION_HEADER ReadSectionHeader = IMAGE_FIRST_SECTION(ReadNTHeaders);
	PIMAGE_FILE_HEADER pFileHeader = &ReadNTHeaders->FileHeader;
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		printf("Name(区段名称):%s\n", ReadSectionHeader[i].Name);
		printf("VOffset(起始的相对虚拟地址):%08X\n", ReadSectionHeader[i].VirtualAddress);
		printf("VSize(区段大小):%08X\n", ReadSectionHeader[i].SizeOfRawData);
		printf("ROffset(文件偏移):%08X\n", ReadSectionHeader[i].PointerToRawData);
		printf("RSize(文件中区段大小):%08X\n", ReadSectionHeader[i].Misc.VirtualSize);
		printf("标记(区段的属性):%08X\n\n", ReadSectionHeader[i].Characteristics);
	}
	printf("==================================================================\n");
	//ImportTable(buffer);
	//ExportTable(buffer);
	//TLSTable(buffer);
	//DelayImportTable(buffer);
	//RelocTable(buffer);
	ResourceTable(buffer);
	free(buffer);
	return 0;
}

//dwRva是某个数据目录表的VirtualAddress
//buffer是读取到的PE文件缓冲区
DWORD RvaToOffset(DWORD dwRva, char * buffer)
{
	//Dos头
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	//PE头
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	//区段表
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);
	//判断是否落在了头部当中
	if (dwRva < pSection[0].VirtualAddress)
	{
		return dwRva;
	}
	for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++)
	{
		//VirtualAddress 起始地址
		//Size 长度
		//VirtualAddress + Size 结束地址
		//判断是否落在某个区段内
		if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize)
		{
			//dwRva - pSection[i].VirtualAddress是数据目录表起始地址到区段起始地址的偏移(OFFSET)
			//pSection[i].PointerToRawData区段到文件头的偏移(OFFSET)
			//返回的是数据目录表起始地址到文件头的偏移(OFFSET)
			return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData;
		}
	}

}

void ImportTable(char * buffer)
{
	//Dos
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	//PE
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	//定位导入表
	PIMAGE_DATA_DIRECTORY pImportDir = (PIMAGE_DATA_DIRECTORY)(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT);
	//填充结构
	PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(RvaToOffset(pImportDir->VirtualAddress, buffer) + buffer);
	while (pImport->Name != NULL)
	{
		char *szDllName = (char *)(RvaToOffset(pImport->Name, buffer) + buffer);
		printf("DLL名称:%s\n", szDllName);
		printf("日期时间标志:%08X\n", pImport->TimeDateStamp);
		printf("ForwarderChain:%08X\n", pImport->ForwarderChain);
		printf("名称OFFSET:%08X\n", pImport->Name);
		printf("FirstThunk:%08X\n", pImport->FirstThunk);
		printf("OriginalFirstThunk:%08X\n\n", pImport->OriginalFirstThunk);
		//指向导入地址表的RVA
		PIMAGE_THUNK_DATA pIat = (PIMAGE_THUNK_DATA)(RvaToOffset(pImport->OriginalFirstThunk, buffer) + buffer);
		DWORD index = 0;
		DWORD ImprotOffset = 0;
		//被导入函数的序号
		while (pIat->u1.Ordinal != 0)
		{
			printf("ThunkRva:%08X\n", pImport->OriginalFirstThunk + index);
			ImprotOffset = RvaToOffset(pImport->OriginalFirstThunk, buffer);
			printf("ThunkOffset:%08X\n", ImprotOffset + index);
			index += 4;
			if ((pIat->u1.Ordinal & 0x80000000) != 1)
			{
				PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToOffset(pIat->u1.AddressOfData, buffer) + buffer);
				//名称
				printf("API名称:%s\n", pName->Name);
				//序号
				printf("Hint:%04X\n", pName->Hint);
				//被导入函数的地址
				printf("ThunkValue:%08X\n\n", pIat->u1.Function);
			}
			pIat++;
		}
		pImport++;
	}
}

void ExportTable(char * buffer)
{
	//Dos
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	//PE
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	//定位数据目录表中的导出表
	PIMAGE_DATA_DIRECTORY pExportDir = pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT;
	//填充导出表结构
	PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(RvaToOffset(pExportDir->VirtualAddress, buffer) + buffer);
	char * szName = (char *)(RvaToOffset(pExport->Name, buffer) + buffer);
	if (pExport->AddressOfFunctions == 0)
	{
		printf("当前没有导出表!\n");
		return;
	}
	printf("导出表OFFSET:%08X\n", RvaToOffset(pExportDir->VirtualAddress, buffer));
	printf("特征值:%08X\n", pExport->Characteristics);
	printf("基:%08X\n", pExport->Base);
	printf("名称OFFSET:%08X\n", pExport->Name);
	printf("名称:%s\n", szName);
	printf("函数数量:%08X\n", pExport->NumberOfFunctions);
	printf("函数名数量:%08X\n", pExport->NumberOfNames);
	printf("函数地址:%08X\n", pExport->AddressOfFunctions);
	printf("函数名称地址:%08X\n", pExport->AddressOfNames);
	printf("函数名称序号地址:%08X\n", pExport->AddressOfNameOrdinals);
	//函数数量
	DWORD dwNumOfFun = pExport->NumberOfFunctions;
	//函数名数量
	DWORD dwNumOfNames = pExport->NumberOfNames;
	//基
	DWORD dwBase = pExport->Base;
	//导出地址表
	PDWORD pEat32 = (PDWORD)(RvaToOffset(pExport->AddressOfFunctions, buffer) + buffer);
	//导出名称表
	PDWORD pEnt32 = (PDWORD)(RvaToOffset(pExport->AddressOfNames, buffer) + buffer);
	//导出序号表
	PWORD pId = (PWORD)(RvaToOffset(pExport->AddressOfNameOrdinals, buffer) + buffer);
	for (DWORD i = 0; i < dwNumOfFun; i++)
	{
		if (pEat32[i] == 0)
		{
			continue;
		}
		DWORD Id = 0;
		for (; Id < dwNumOfNames; Id++)
		{
			if (pId[Id] == i)
			{
				break;
			}
		}
		if (Id == dwNumOfNames)
		{
			printf("Id:%x Address:0x%08X Name[NULL]\n", i + dwBase, pEat32[i]);
		}
		else
		{

			char * szFunName = (char *)(RvaToOffset(pEnt32[Id], buffer) + buffer);
			printf("Id:%x Address:0x%08X Name[%s]\n", i + dwBase, pEat32[i], szFunName);
		}
	}
}

void TLSTable(char * buffer)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	PIMAGE_DATA_DIRECTORY pTLSDir = (pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS);
	PIMAGE_TLS_DIRECTORY pTLS32 = (PIMAGE_TLS_DIRECTORY)(RvaToOffset(pTLSDir->VirtualAddress, buffer) + buffer);
	printf("数据块开始VA:%08X\n", pTLS32->StartAddressOfRawData);
	printf("数据块结束VA:%08X\n", pTLS32->EndAddressOfRawData);
	printf("索引变量VA:%08X\n", pTLS32->AddressOfIndex);
	printf("回调表VA:%08X\n", pTLS32->AddressOfCallBacks);
	printf("填零大小:%08X\n", pTLS32->SizeOfZeroFill);
	printf("特征值:%08X\n", pTLS32->Characteristics);
}

void DelayImportTable(char * buffer)
{
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	PIMAGE_DATA_DIRECTORY pImportDir = (pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
	PIMAGE_DELAYLOAD_DESCRIPTOR pDelayLoad = (PIMAGE_DELAYLOAD_DESCRIPTOR)(RvaToOffset(pImportDir->VirtualAddress, buffer) + buffer);
	while (pDelayLoad->DllNameRVA != NULL)
	{
		char* szDllName = (char*)(RvaToOffset(pDelayLoad->DllNameRVA, buffer) + buffer);
		printf("DllName:%s\n", szDllName);
		printf("Attributes:%08X\n", pDelayLoad->Attributes);
		printf("ModuleHandleRVA:%08X\n", pDelayLoad->ModuleHandleRVA);
		printf("ImportAddressTableRVA:%08X\n", pDelayLoad->ImportAddressTableRVA);
		printf("ImportNameTableRVA:%08X\n", pDelayLoad->ImportNameTableRVA);
		printf("BoundImportAddressTableRVA:%08X\n", pDelayLoad->BoundImportAddressTableRVA);
		printf("UnloadInformationTableRVA:%08X\n", pDelayLoad->UnloadInformationTableRVA);
		printf("TimeDateStamp:%08X\n\n", pDelayLoad->TimeDateStamp);
		pDelayLoad++;
	}
}

void RelocTable(char * buffer)
{
	typedef struct  _TYPE {
		WORD Offset : 12;  // (1) 大小为12Bit的重定位偏移
		WORD Type : 4;      // (2) 大小为4Bit的重定位信息类型值
	}TYPE, *PTYPE;

	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	PIMAGE_DATA_DIRECTORY pRelocDir =
		(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_BASERELOC);
	//2 找到基址重定位表
	PIMAGE_BASE_RELOCATION pReloc =
		(PIMAGE_BASE_RELOCATION)
		(RvaToOffset(pRelocDir->VirtualAddress, buffer) + buffer);
	PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);//区段
	PIMAGE_FILE_HEADER pFileHeader = &pNt->FileHeader;
	while (pReloc->SizeOfBlock != 0)
	{
		//找到本1000字节的起始位置
		DWORD dwCount = (pReloc->SizeOfBlock - 8) / 2; //本块内需要重定位的个数。
		DWORD dwRva = pReloc->VirtualAddress;
		PTYPE pRelocArr = (PTYPE)(pReloc + 1); // 数组:存储需要重定位的每一个点的偏移;
		printf("RVA:%08X\n", dwRva);
		printf("项目:%X H/%d D\n", pReloc->SizeOfBlock, pReloc->SizeOfBlock);
		printf("区段:%s\n\n", pSection->Name);								   
		//找到下一个1000字节的结构体
		pReloc = (PIMAGE_BASE_RELOCATION)
			((char*)pReloc + pReloc->SizeOfBlock);
		for (int i = 0; i < dwCount; i++)
		{
			PDWORD pData =
				(PDWORD)(RvaToOffset(pRelocArr[i].Offset + dwRva, buffer) + buffer);
			DWORD DAD = RvaToOffset(dwRva + pRelocArr[i].Offset,buffer);
			printf("RVA:%08X\n", dwRva + pRelocArr[i].Offset);
			printf("区段:%08X\n", *pData);
			printf("偏移:%08X\n\n", DAD);
		}
	}
}

void ResourceTable(char * buffer)
{
	char * g_ResType[0x11] = {
		"NULL",
		"鼠标指针",
		"位图",
		"图标",
		"菜单",
		"对话框",
		"字符串列表",
		"字体目录",
		"字体",
		"快捷键",
		"非格式化资源",
		"消息列表",
		"鼠标指针组",
		"NULL",
		"图标组",
		"NULL",
		"版本信息",
	};

	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buffer;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buffer);
	PIMAGE_OPTIONAL_HEADER pOPtionHeader = (PIMAGE_OPTIONAL_HEADER)&pNt->OptionalHeader;

	PIMAGE_DATA_DIRECTORY pResDir = pOPtionHeader->DataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE;
	PIMAGE_RESOURCE_DIRECTORY pFirst = (PIMAGE_RESOURCE_DIRECTORY)(RvaToOffset(pResDir->VirtualAddress, buffer) + buffer);
	DWORD dwResNum = pFirst->NumberOfIdEntries + pFirst->NumberOfNamedEntries;
	PIMAGE_RESOURCE_DIRECTORY_ENTRY pFirstEntry =
		(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pFirst + 1);
	printf("根目录名称入口:%04X\n", pFirst->NumberOfNamedEntries);
	printf("根目录ID入口:%04X\n\n", pFirst->NumberOfIdEntries);

	for (int i = 0; i < dwResNum; i++)
	{
			//ID不是字符串,代表该资源是系统内置资源
		if (pFirstEntry->NameIsString != 1)
		{
				//
			if (pFirstEntry->Id < 0x10)
			{
				printf("ResType:%s\n", g_ResType[pFirstEntry->Id]);
			}
			else
			{
				printf("ResType:%d\n", g_ResType[pFirstEntry->Id]);
			}
		}
			// ID是字符串,代表该资源是开发者的资源
		else
		{
				PIMAGE_RESOURCE_DIR_STRING_U  pResName =
					(PIMAGE_RESOURCE_DIR_STRING_U)
					(pFirstEntry->NameOffset + (DWORD)pFirst);

			wchar_t * EpName = new wchar_t[pResName->Length + 1];
			memset(EpName, 0, sizeof(wchar_t)*(pResName->Length + 1));
			wcsncpy_s(EpName, pResName->Length + 1, pResName->NameString, pResName->Length);

		}
		if (pFirstEntry->DataIsDirectory == 1)
		{
			PIMAGE_RESOURCE_DIRECTORY pSecond = (PIMAGE_RESOURCE_DIRECTORY)(pFirstEntry->OffsetToDirectory + (DWORD)pFirst);
			DWORD dwSecondCount = pSecond->NumberOfIdEntries + pSecond->NumberOfNamedEntries;
			PIMAGE_RESOURCE_DIRECTORY_ENTRY pSecondEntry =
				(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pSecond + 1);
			//解析资源类型下的每个资源
			printf("名称入口:%04X\n",pSecond->NumberOfNamedEntries);
			printf("ID入口:%04X\n", pSecond->NumberOfIdEntries);
				for (int i = 0; i < dwSecondCount; i++)
				{
					//解析资源ID
					if (pSecondEntry->NameIsString != 1)
					{
						printf("Id:%d\n", pSecondEntry->Id);
					}
					else
					{

						PIMAGE_RESOURCE_DIR_STRING_U  pResName =
						(PIMAGE_RESOURCE_DIR_STRING_U)
						(pSecondEntry->NameOffset + (DWORD)pFirst);
						wchar_t * pName = new wchar_t[pResName->Length + 1];
						memset(pName, 0, sizeof(wchar_t)*(pResName->Length + 1));
						wcsncpy_s(pName, pResName->Length + 1, pResName->NameString, pResName->Length);
						printf("Name:%s\n\n", pName);
						delete[]pName;
					}
						if (pSecondEntry->DataIsDirectory == 1)
						{
							PIMAGE_RESOURCE_DIRECTORY pThird =
								(PIMAGE_RESOURCE_DIRECTORY)
								(pSecondEntry->OffsetToDirectory + (DWORD)pFirst);

							PIMAGE_RESOURCE_DIRECTORY_ENTRY pThirdEnty =
								(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pThird + 1);
							pThirdEnty->Name;
							//解析每个资源的信息
							if (pThirdEnty->DataIsDirectory != 1)
							{
								PIMAGE_RESOURCE_DATA_ENTRY  pStcData =
									(PIMAGE_RESOURCE_DATA_ENTRY)
									(pThirdEnty->OffsetToData + (DWORD)pFirst);

								char* pResbuf = (char *)
									(RvaToOffset(pStcData->OffsetToData, buffer) + buffer);
								DWORD StcDataOffset = RvaToOffset(pStcData->OffsetToData,buffer);
								printf("RVA:%08X\n", pStcData->OffsetToData);
								printf("Offset:%08X\n", StcDataOffset);
								printf("Size:%08X\n\n", pStcData->Size);

							}
						}
					pSecondEntry++;
				}
				printf("============================================\n");
		}
		pFirstEntry++;
	}
}

头文件

#pragma once
#include <stdio.h>
#include <Windows.h>

//计算数据目录表起始位置到文件头的偏移
DWORD RvaToOffset(DWORD dwRva, char *buffer);
//解析导入表的函数
void ImportTable(char * buffer);
//解析导出表的函数
void ExportTable(char * buffer);
//解析TLS表的函数
void TLSTable(char* buffer);
//解析延迟导入表的函数
void DelayImportTable(char *buffer);
//解析重定位表的函数
void RelocTable(char *buffer);
//解析资源表的函数
void ResourceTable(char *buffer);

爽,不过我到底是在复习还是在改bug啊哈哈哈哈哈

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PE结构(Portable Executable Structure)是一种Windows操作系统下的可执行文件格式,常用于存储和加载应用程序、动态链接库和驱动程序等可执行文件。而PDF(Portable Document Format)是一种跨平台的文档格式,用于显示和打印独立于软件、硬件和操作系统的文件PE结构和PDF文件在某些方面具有相似之处。首先,它们都是可移植的,即可以在不同的操作系统和计算机上使用。无论是PE结构的可执行文件,还是PDF文件的文档,都可以在多个平台上进行读取和执行。 其次,它们都使用了特定的文件结构PE结构通过头部信息、代码段、数据段、导出表、导入表等部分来组织和存储可执行代码和数据。而PDF文件通过文件头、文档信息、页面内容、字体信息等部分来保存和展示文档的内容。 最后,它们都支持一定程度的可编辑性。PE结构可以通过调试器、反汇编等工具进行调试和修改,以实现特定的功能需求。PDF文件可以使用编辑工具对文本、图片等元素进行修改和更新。 然而,PE结构和PDF文件也有一些明显的差异。首先,PE结构主要用于存储可执行代码和数据,而PDF文件主要用于存储文档内容。其次,PE结构具有更强的可执行性,可以在系统级别上执行代码,而PDF文件主要用于显示和打印文档,不具备直接执行代码的功能。 总之,PE结构和PDF文件是两种不同的文件格式,分别用于存储和加载可执行代码和数据,以及显示和打印文档内容。它们在某些方面具有相似之处,但也有一些明显的差异。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值