PE檔案結構複習

啥也不是哦發表於2020-11-11

突然想到好久沒有複習PE檔案結構了,正好又到了寫部落格的日子,話不多說,開淦!

#include "Entry.h"


int main()
{
	//檔案讀取
	FILE * pFile = NULL;
	char * buffer;
	int nFileLength = 0;
	pFile = fopen("C:\\Users\\86188\\Desktop\\DragonNest.exe", "rb");
	//pFile = fopen("C:\\Users\\86188\\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啊哈哈哈哈哈

相關文章