这里三层结构一定要注意 nameOffset OffsetToDirctory 与 OffsetToData 联合体中的这三个偏移是相对资源表最开始的位置的偏移 但在第三层中的 offsetToData是相对虚拟地址(RVA)要先转换成 FOA再加上文件起始地址就是在文件中的地址

#include<Windows.h>
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<commdlg.h>
using namespace std;
DWORD dwFileSize;
BYTE* g_pFileImageBase = 0;
PIMAGE_NT_HEADERS g_pNt = 0;

DWORD RVAtoFOA(DWORD dwRVA);

//显示pe头
//参数为内存映射首地址
void myPE() {
		OPENFILENAME stOF;
		HANDLE hFile, hMapFile;
		DWORD totalSize;		//文件大小
		LPVOID lpMemory;		//内存映像文件在内存的起始位置
		char szFileName[MAX_PATH] = { 0 };	//要打开的文件路径及名称名
		char bufTemp1[10];					//每个字符的十六进制字节码
		char bufTemp2[20];					//第一列
		char lpServicesBuffer[100];		//一行的所有内容
		char bufDisplay[50];				//第三列ASCII码字符
		DWORD dwCount;						//计数,逢16则重新计
		DWORD dwCount1;						//地址顺号
		DWORD dwBlanks;						//最后一行空格数
		char szExtPe[] = TEXT("PE Files\0*.exe;*.dll;*.scr;*.fon;*.drv\0All Files(*.*)\0*.*\0\0");
		RtlZeroMemory(&stOF, sizeof(stOF));
		stOF.lStructSize = sizeof(stOF);
		//stOF.hwndOwner = (HWND)GetModuleHandle(NULL);
		stOF.lpstrFilter = szExtPe;
		stOF.lpstrFile = szFileName;
		stOF.nMaxFile = MAX_PATH;
		stOF.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
	if (GetOpenFileName(&stOF))		//让用户选择打开的文件
	{
		hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
					NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);
		//如果是无效的句柄
		if (hFile == INVALID_HANDLE_VALUE) {
			printf("文件打开失败\n");
			return;
		}
		//获取文件大小
		dwFileSize = GetFileSize(hFile, NULL);
		g_pFileImageBase = new BYTE[dwFileSize]{};
		DWORD dwRead;
		//将文件读取到内存中
		bool bRet =ReadFile(hFile, g_pFileImageBase, dwFileSize, &dwRead, NULL);
		//如果读取失败就返回
		if (!bRet)
		{
			delete[] g_pFileImageBase;
		}
		//关闭句柄
		CloseHandle(hFile);
/////////////////////////////////dos头///////////////////////////////////////////////
		//typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
		//	WORD   e_magic;                     // Magic number
		//	WORD   e_cblp;                      // Bytes on last page of file
		//	WORD   e_cp;                        // Pages in file
		//	WORD   e_crlc;                      // Relocations
		//	WORD   e_cparhdr;                   // Size of header in paragraphs
		//	WORD   e_minalloc;                  // Minimum extra paragraphs needed
		//	WORD   e_maxalloc;                  // Maximum extra paragraphs needed
		//	WORD   e_ss;                        // Initial (relative) SS value
		//	WORD   e_sp;                        // Initial SP value
		//	WORD   e_csum;                      // Checksum
		//	WORD   e_ip;                        // Initial IP value
		//	WORD   e_cs;                        // Initial (relative) CS value
		//	WORD   e_lfarlc;                    // File address of relocation table
		//	WORD   e_ovno;                      // Overlay number
		//	WORD   e_res[4];                    // Reserved words
		//	WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
		//	WORD   e_oeminfo;                   // OEM information; e_oemid specific
		//	WORD   e_res2[10];                  // Reserved words
		//	LONG   e_lfanew;                    // File address of new exe header
		//} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
/////////////////////////////////dos头///////////////////////////////////////////////
		//使用PIMAGE_DOS_HEADER(占64字节)解释前64个字节
		PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)g_pFileImageBase;
		//判断PE文件的标识是否正确,有一个不对,那么它就不是PE文件
		if (pDos->e_magic != IMAGE_DOS_SIGNATURE)//0x5A4D('MZ')
		{
			return ;
		}
		cout << "---------------------------DOS头(_IMAGE_DOS_HEADER)------------------------------- "<< endl;
		cout << hex << "WORD   e_magic:     " << pDos->e_magic << endl;
		cout << hex << "WORD   e_cblp:      " << pDos->e_cblp << endl;
		cout << hex << "WORD   e_cp:        " << pDos->e_cp << endl;
		cout << hex << "WORD   e_cblp:      " << pDos->e_cblp << endl;
		cout << hex << "WORD   e_cparhdr:   " << pDos->e_cparhdr << endl;
		cout << hex << "WORD   e_minalloc:  " << pDos->e_minalloc << endl;
		cout << hex << "WORD   e_ss:        " << pDos->e_ss << endl;
		cout << hex << "WORD   e_sp:        " << pDos->e_sp << endl;
		cout << hex << "WORD   e_csum:      " << pDos->e_csum << endl;
		cout << hex << "WORD   e_ip:        " << pDos->e_ip << endl;
		cout << hex << "WORD   e_cs:        " << pDos->e_cs << endl;
		cout << hex << "WORD   e_lfarlc:    " << pDos->e_lfarlc << endl;
		cout << hex << "WORD   e_ovno:      " << pDos->e_ovno << endl;
		cout << hex << "WORD   e_res[4]:    " << *(pDos->e_res) << endl;
		cout << hex << "WORD   e_oemid:     " << pDos->e_oemid << endl;
		cout << hex << "WORD   e_oeminfo:   " << pDos->e_oeminfo << endl;
		cout << hex << "WORD   e_res2[10]:  " << *(pDos->e_res2) << endl;
		cout << hex << "WORD   e_lfanew:    " << pDos->e_lfanew << endl;

/////////////////////////////////NT头///////////////////////////////////////////////

		/*typedef struct _IMAGE_NT_HEADERS {
			DWORD Signature;
			IMAGE_FILE_HEADER FileHeader;
			IMAGE_OPTIONAL_HEADER32 OptionalHeader;
		} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;*/
		g_pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + g_pFileImageBase);
		if (g_pNt->Signature != IMAGE_NT_SIGNATURE)//0x00004550('PE')
		{
			return ;
		}
		cout << "---------------------------NT头(PIMAGE_NT_HEADERS)------------------------------- " << endl;
		cout << hex << "DWORD Signature;                              " << g_pNt->Signature << endl;
		cout << hex << "IMAGE_FILE_HEADER FileHeader;                 " << &g_pNt->OptionalHeader << endl;
		cout << hex << "IMAGE_OPTIONAL_HEADER32 OptionalHeader;       " << &g_pNt->OptionalHeader << endl;
/////////////////////////////////FILE头///////////////////////////////////////////////
		/*typedef struct _IMAGE_FILE_HEADER {
			WORD    Machine;
			WORD    NumberOfSections;
			DWORD   TimeDateStamp;
			DWORD   PointerToSymbolTable;
			DWORD   NumberOfSymbols;
			WORD    SizeOfOptionalHeader;
			WORD    Characteristics;
		} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;*/
		PIMAGE_FILE_HEADER mytmf = &(g_pNt->FileHeader);
		cout << "---------------------------FILE头(IMAGE_FILE_HEADER )----------------------------- " << endl;
		cout << hex << "WORD    Machine;                              " << mytmf->Machine << endl;
		cout << hex << "WORD    NumberOfSections;                     " << mytmf->NumberOfSections << endl;
		cout << hex << "DWORD   TimeDateStamp;                        " << mytmf->TimeDateStamp << endl;
		cout << hex << "DWORD   PointerToSymbolTable;                 " << mytmf->PointerToSymbolTable << endl;
		cout << hex << "DWORD   NumberOfSymbols;                      " << mytmf->NumberOfSymbols << endl;
		cout << hex << "WORD    SizeOfOptionalHeader;                 " << mytmf->SizeOfOptionalHeader << endl;
		cout << hex << "WORD    Characteristics;                      " << mytmf->Characteristics << endl;
		/////////////////////////////////Optional头///////////////////////////////////////////////

		//
		// Optional header format.
		//

		//typedef struct _IMAGE_OPTIONAL_HEADER {
		//	//
		//	// Standard fields.
		//	//

		//	WORD    Magic;
		//	BYTE    MajorLinkerVersion;
		//	BYTE    MinorLinkerVersion;
		//	DWORD   SizeOfCode;
		//	DWORD   SizeOfInitializedData;
		//	DWORD   SizeOfUninitializedData;
		//	DWORD   AddressOfEntryPoint;
		//	DWORD   BaseOfCode;
		//	DWORD   BaseOfData;

		//	//
		//	// 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;
		//	DWORD   CheckSum;
		//	WORD    Subsystem;
		//	WORD    DllCharacteristics;
		//	DWORD   SizeOfStackReserve;
		//	DWORD   SizeOfStackCommit;
		//	DWORD   SizeOfHeapReserve;
		//	DWORD   SizeOfHeapCommit;
		//	DWORD   LoaderFlags;
		//	DWORD   NumberOfRvaAndSizes;
		//	IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
		//} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
		PIMAGE_OPTIONAL_HEADER32 myoption = &(g_pNt->OptionalHeader);
		cout << "---------------------------OPTIONAL头(PIMAGE_OPTIONAL_HEADER32 )-------------------------------------------------- " << endl;
		
	    cout << "---------------------------------------Standard fields.------------------------------------------------------------- " << endl;
		cout << hex << "WORD    Magic;                                     " << myoption->Magic << endl;
		cout << hex << "BYTE    MajorLinkerVersion;                        " << myoption->MajorLinkerVersion << endl;
		cout << hex << "BYTE    MinorLinkerVersion;                        " << myoption->MinorLinkerVersion << endl;
		cout << hex << "DWORD   SizeOfCode;                                " << myoption->SizeOfCode << endl;
		cout << hex << "DWORD   SizeOfInitializedData;                     " << myoption->SizeOfInitializedData << endl;
		cout << hex << "DWORD   SizeOfUninitializedData;                   " << myoption->SizeOfUninitializedData << endl;
		cout << hex << "DWORD   AddressOfEntryPoint;                       " << myoption->AddressOfEntryPoint << endl;
		cout << hex << "DWORD   BaseOfCode;                                " << myoption->BaseOfCode << endl;
		cout << hex << "DWORD   BaseOfData;                                " << myoption->BaseOfData << endl;
		cout << "----------------------------------------- NT additional fields.----------------------------------------------------- " << endl;
		cout << hex << "DWORD   ImageBase;                                 " << myoption->ImageBase << endl;
		cout << hex << "DWORD   SectionAlignment;                          " << myoption->SectionAlignment << endl;
		cout << hex << "DWORD   FileAlignment;                             " << myoption->FileAlignment << endl;
		cout << hex << "WORD    MajorOperatingSystemVersion;               " << myoption->MajorOperatingSystemVersion << endl;
		cout << hex << "WORD    MinorOperatingSystemVersion;               " << myoption->MinorOperatingSystemVersion << endl;
		cout << hex << "WORD    MajorImageVersion;                         " << myoption->MajorImageVersion << endl;
		cout << hex << "DWORD   SectionAlignment;                          " << myoption->SectionAlignment << endl;
		cout << hex << "WORD    MinorImageVersion;                         " << myoption->MinorImageVersion << endl;
		cout << hex << "WORD    MajorSubsystemVersion;                     " << myoption->MajorSubsystemVersion << endl;
		cout << hex << "WORD    MinorSubsystemVersion;                     " << myoption->MinorSubsystemVersion << endl;
		cout << hex << "DWORD   Win32VersionValue;                         " << myoption->Win32VersionValue << endl;
		cout << hex << "DWORD   SizeOfImage;                               " << myoption->SizeOfImage << endl;
		cout << hex << "DWORD   SizeOfHeaders;                             " << myoption->SizeOfHeaders << endl;
		cout << hex << "DWORD   CheckSum;                                  " << myoption->CheckSum << endl;
		cout << hex << "WORD    Subsystem;                                 " << myoption->Subsystem << endl;
		cout << hex << "WORD    DllCharacteristics;                        " << myoption->DllCharacteristics << endl;
		cout << hex << "DWORD   SizeOfStackReserve;                        " << myoption->SizeOfStackReserve << endl;
		cout << hex << "DWORD   SizeOfStackCommit;                         " << myoption->SizeOfStackCommit << endl;
		cout << hex << "DWORD   SizeOfHeapReserve;                         " << myoption->SizeOfHeapReserve << endl;
		cout << hex << "DWORD   SizeOfHeapCommit;                          " << myoption->SizeOfHeapCommit << endl;
		cout << hex << "DWORD   LoaderFlags;                               " << myoption->LoaderFlags << endl;
		cout << hex << "DWORD   NumberOfRvaAndSizes;                       " << myoption->NumberOfRvaAndSizes << endl;
		cout << hex << "IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];" << &(myoption->DataDirectory) << endl;
		/////////////////////////////////////////////所有区段表头////////////////////////////////////////////////////
		/*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;
			DWORD   PointerToLinenumbers;
			WORD    NumberOfRelocations;
			WORD    NumberOfLinenumbers;
			DWORD   Characteristics;
		} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;*/
		//所有区表头都在一起
		//大文件头中找区段数
		int nCountOfSection = g_pNt->FileHeader.NumberOfSections;
		//取第一个区表头
		PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(g_pNt);
		//循环
		for (int i = 0; i < nCountOfSection; i++)
		{
        cout << "----------------------第"<<i<<"个-----PIMAGE_SECTION_HEADER头(PIMAGE_OPTIONAL_HEADER32 )----------------------------- " << endl;
		cout << "BYTE    Name[IMAGE_SIZEOF_SHORT_NAME];                    " << pSec->Name << endl;
		cout << hex << "DWORD   PhysicalAddress;                           " << pSec->Misc.PhysicalAddress << endl;
		cout << hex << "DWORD   VirtualSize;                               " << pSec->Misc.VirtualSize << endl;
		cout << hex << "DWORD   VirtualAddress;                            " << pSec->VirtualAddress << endl;
		cout << hex << "DWORD   SizeOfRawData;                             " << pSec->SizeOfRawData << endl;
		cout << hex << "DWORD   PointerToRawData;                          " << pSec->PointerToRawData << endl;
		cout << hex << "DWORD   PointerToRelocations;                      " << pSec->PointerToRelocations << endl;
		cout << hex << "WORD    PointerToLinenumbers;                      " << pSec->PointerToLinenumbers << endl;
		cout << hex << "WORD    NumberOfRelocations;                       " << pSec->NumberOfRelocations << endl;
		cout << hex << "DWORD   NumberOfLinenumbers;                       " << pSec->NumberOfLinenumbers << endl;
		cout << hex << "DWORD   Characteristics;                           " << pSec->Characteristics << endl;
		cout << hex << "在文件中相对文件偏移;						       " << RVAtoFOA(pSec->VirtualAddress) << endl;
		
			//下一个区表头首地址
			++pSec;
		}
		/////////////////////////////////////////////导出表///////////////////////////////////////////////////////////
		//typedef struct _IMAGE_EXPORT_DIRECTORY {
		//	DWORD   Characteristics;
		//	DWORD   TimeDateStamp;
		//	WORD    MajorVersion;
		//	WORD    MinorVersion;
		//	DWORD   Name;
		//	DWORD   Base;
		//	DWORD   NumberOfFunctions;
		//	DWORD   NumberOfNames;
		//	DWORD   AddressOfFunctions;     // RVA from base of image
		//	DWORD   AddressOfNames;         // RVA from base of image
		//	DWORD   AddressOfNameOrdinals;  // RVA from base of image
		//} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
		//找到导出表  也就是第一个表下标为0
		DWORD dwExportRVA =g_pNt->OptionalHeader.DataDirectory[0].VirtualAddress;
		//获取在文件中的位置
		PIMAGE_EXPORT_DIRECTORY pExport =(PIMAGE_EXPORT_DIRECTORY)(RVAtoFOA(dwExportRVA) + g_pFileImageBase);
		//模块名字
		char* pName = (char*)(RVAtoFOA(pExport->Name) + g_pFileImageBase);
		printf("%s\n", pName);
		//地址表中的个数
		DWORD dwCountOfFuntions = pExport->NumberOfFunctions;
		//名称表中的个数
		DWORD dwCountOfNames = pExport->NumberOfNames;
		//地址表地址
		PDWORD pAddrOfFuntion = (PDWORD)(RVAtoFOA(pExport->AddressOfFunctions) + g_pFileImageBase);
		//名称表地址
		PDWORD pAddrOfName = (PDWORD)(RVAtoFOA(pExport->AddressOfNames) + g_pFileImageBase);
		//序号表地址
		PWORD pAddrOfOrdial = (PWORD)(RVAtoFOA(pExport->AddressOfNameOrdinals) + g_pFileImageBase);
		//base值
		DWORD dwBase = pExport->Base;
		//遍历地址表中的元素
		cout << "-----------------------------------------导出表中的导出函数与导出序号-------------------------------------------------- " << endl;
		if (dwExportRVA == 0) {
			printf("没有导出表\n");
			//return;
		}
		else {
			for (int i = 0; i < dwCountOfFuntions;i++)
			{
				//地址表中可能存在无用的值(就是为0的值)
				if (pAddrOfFuntion[i] == 0)
				{
					continue;
				}
				//根据序号表中是否有值(地址表的下标值),
				//来判断是否是名称导出
				bool bRet = false;
				for (int j = 0; j < dwCountOfNames;j++)
				{
					//i为地址表下标j为序号表的下标(值为地址表下标)
					//判断是否在序号表中
					if (i == pAddrOfOrdial[j])
					{
						//因为序号表与名称表的位置一一对应
						//取出名称表中的名称地址RVA
						DWORD dwNameRVA = pAddrOfName[j];
						char* pFunName = (char*)(RVAtoFOA(dwNameRVA) + g_pFileImageBase);
						printf("%04d  %s  0x%08x\n", i + dwBase, pFunName, pAddrOfFuntion[i]);
						bRet = true;
						break;
					}
				}
				if (!bRet)
				{
					//序号表中没有,说明是以序号导出的
					printf("%04d           %08X\n", i + dwBase, pAddrOfFuntion[i]);
				}

			}
		}
		/////////////////////////////////////////////导出表///////////////////////////////////////////////////////////
		//typedef struct _IMAGE_IMPORT_DESCRIPTOR {
		//	union {
		//		DWORD   Characteristics;            // 0 for terminating null import descriptor
		//		DWORD   OriginalFirstThunk;         // RVA to original unbound IAT (PIMAGE_THUNK_DATA)
		//	} DUMMYUNIONNAME;
		//	DWORD   TimeDateStamp;                  // 0 if not bound,
		//											// -1 if bound, and real date\time stamp
		//											//     in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
		//											// O.W. date/time stamp of DLL bound to (Old BIND)

		//	DWORD   ForwarderChain;                 // -1 if no forwarders
		//	DWORD   Name;
		//	DWORD   FirstThunk;                     // RVA to IAT (if bound this IAT has actual addresses)
		//} IMAGE_IMPORT_DESCRIPTOR;
		//typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
		cout << "-----------------------------------------导入表中的导入函数与导入模块-------------------------------------------------- " << endl;

		//找到导入表  也就是第二个下标为1
		DWORD dwImpotRVA = g_pNt->OptionalHeader.DataDirectory[1].VirtualAddress;
		//在文件中的位置
		DWORD dwImportInFile = (DWORD)(RVAtoFOA(dwImpotRVA) + g_pFileImageBase);
		PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwImportInFile;
		//遍历每一个导入表  通过最后一个为0作为判断条件
		if (dwImpotRVA == 0) {
			printf("没有导入表\n");
			return;
		}
		else {
			while (pImport->Name)
			{
				//函数名称地址
				PIMAGE_THUNK_DATA pFirsThunk =
					(PIMAGE_THUNK_DATA)(RVAtoFOA(pImport->FirstThunk) + g_pFileImageBase);
				//模块名
				char* pName = (char*)(RVAtoFOA(pImport->Name) + g_pFileImageBase);
				printf("导入模块名字%s\n", pName);
				//也是通过最后一个为0作为判断条件
				while (pFirsThunk->u1.AddressOfData)
				{
					//判断导入方式
					if (IMAGE_SNAP_BY_ORDINAL32(pFirsThunk->u1.AddressOfData))
					{
						//说明是序号导入(低16位是其序号)
						printf("\t\t%04X \n", pFirsThunk->u1.Ordinal & 0xFFFF);
					}
					else
					{
						//名称导入
						PIMAGE_IMPORT_BY_NAME pImportName =
							(PIMAGE_IMPORT_BY_NAME)(RVAtoFOA(pFirsThunk->u1.AddressOfData) + g_pFileImageBase);
						printf("\t\t%04X %s \n", pImportName->Hint, pImportName->Name);
					}
					//
					pFirsThunk++;
				}
				pImport++;
			}
		}
		cout << "--------------------------------------------------资源表--------------------------------------------------------------- " << endl;
		//注意的是NameOffset偏移  OffsetToDirectory偏移  OffsetToData偏移都是资源表最开始的偏移
			//找到资源表
			DWORD dwResRVA =
				g_pNt->OptionalHeader.DataDirectory[2].VirtualAddress;
			DWORD dwResFOA = (DWORD)(RVAtoFOA(dwResRVA) + g_pFileImageBase);
			PIMAGE_RESOURCE_DIRECTORY pRes = (PIMAGE_RESOURCE_DIRECTORY)dwResFOA;
			//资源有三层 每一层都以一个PIMAGE_RESOURCE_DIRECTORY开头,之后跟数个
			//PIMAGE_RESOURCE_DIRECTORY_ENTRY结构,可以说第一层由一个PIMAGE_RESOURCE_DIRECTORY
			//与一个PIMAGE_RESOURCE_DIRECTORY_ENTRY结构体组成
			//第一层(种类)
			//种类个数
			DWORD dwCountOfResType =
				pRes->NumberOfIdEntries + pRes->NumberOfNamedEntries;

			for (int i = 0; i < dwCountOfResType;i++)
			{   //pRes代表PIMAGE_RESOURCE_DIRECTORY的首地址+1之后就是后面的PIMAGE_RESOURCE_DIRECTORY_ENTRY首地址
				PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry =
					(PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes + 1);
				//typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
				//这个联合体说明资源叫什么  如果这种资源是已知的也就是微软定义的那么
				//联合体最高位为0也就是NameIsString成员为0这个时候整个四字节(union)代表着已知资源的类型,也就是ID起
				//作用  如果这种资源是未知是那么NameIsString的最高位为1 低31位指向一个name的结构体(PIMAGE_RESOURCE_DIR_STRING_U)偏移,也就是DWORD   Name;起作用
				//	union {
				//		struct {
				//			DWORD NameOffset : 31;
				//			DWORD NameIsString : 1;
				//		} DUMMYSTRUCTNAME;
				//		DWORD   Name;
				//		WORD    Id;
				//	} DUMMYUNIONNAME;
				//这个联合体说明资源在哪里
				//当DataIsDirectory字段为1时(也就是这个四字节最高位为1)说明这个联合体表示的地方是一个目录,OffsetToDirectory(低31位)表示具体有
				//多少个地方,这个些地方就是第二层
				//	union {
				//		DWORD   OffsetToData;
				//		struct {
				//			DWORD   OffsetToDirectory : 31;
				//			DWORD   DataIsDirectory : 1;
				//		} DUMMYSTRUCTNAME2;  
				//	} DUMMYUNIONNAME2;
				//} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

				//判断这种资源是字符串还是ID
				if (pResEntry->NameIsString)
				{
					//如果是字符串,NameOffset保存的就是这个字符串相对资源起始位置的偏移  
					//得到名字字符串的FOA
					DWORD dwName = (DWORD)(pResEntry->NameOffset+ (DWORD)pRes);
					//NameOffset所指向的结构体是IMAGE_RESOURCE_DIR_STRING_U类型
					//这里保存了字符串的长度和起始位置
					PIMAGE_RESOURCE_DIR_STRING_U pName = (PIMAGE_RESOURCE_DIR_STRING_U)dwName;
					//这里的字符串不是以0结尾的,所以需要拷贝出来加上‘\0’结尾后再打印
					WCHAR *pResName = new WCHAR[pName->Length + 1]{};
					memcpy(pResName, pName, (pName->Length) * sizeof(WCHAR));
					//因为是WCHAR,所以用wprintf
					wprintf(L"%s\n", pResName);
					//释放内存
					delete[] pResName;
				}
				else   //id
				{
					char* arryResType[] = { "", "鼠标指针(Cursor)", "位图(Bitmap)", "图标(Icon)", "菜单(Menu)"
						, "对话框(Dialog)", "字符串列表(String Table)", "字体目录(Font Directory)", "字体(Font)", "快捷键(Accelerators)"
						, "非格式化资源(Unformatted)", "消息列表(Message Table)", "鼠标指针组(Croup Cursor)", "", "图标组(Group Icon)", ""
						, "版本信息(Version Information)" };
					if (pResEntry->Id < 17)
					{
						printf("arryResType[pResEntry->Id]  %s\n", arryResType[pResEntry->Id]);
					}
					else
					{
						printf("pResEntry->Id %04X\n", pResEntry->Id);
					}

					//判断是否有下一层(0个表示没有下一层)
					if (pResEntry->DataIsDirectory)
					{   //到了第二层相对结构体同样和上一层一样但是OffsetToDirectory就指向对三层了
						DWORD dwResSecond = (DWORD)pRes + pResEntry->OffsetToDirectory;
						PIMAGE_RESOURCE_DIRECTORY pResSecond = (PIMAGE_RESOURCE_DIRECTORY)dwResSecond;
						//第二层个数
						DWORD dwCountOfSecond =
							pResSecond->NumberOfIdEntries + pResSecond->NumberOfNamedEntries;
						//遍历每一个资源
						for (int iSecond = 0; iSecond < dwCountOfSecond;iSecond++)
						{
							PIMAGE_RESOURCE_DIRECTORY_ENTRY pResSecondEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResSecond + 1);

							//判断这种资源是字符串还是ID
							if (pResEntry->NameIsString)
							{
								//如果是字符串,NameOffset保存的就是这个字符串的RVA
								//得到名字字符串的FOA
								DWORD dwNameFOA = (DWORD)(RVAtoFOA(pResEntry->NameOffset) + g_pFileImageBase);
								//NameOffset所指向的结构体是IMAGE_RESOURCE_DIR_STRING_U类型
								//这里保存了字符串的长度和起始位置
								PIMAGE_RESOURCE_DIR_STRING_U pName = (PIMAGE_RESOURCE_DIR_STRING_U)dwNameFOA;
								//这里的字符串不是以0结尾的,所以需要拷贝出来加上‘\0’结尾后再打印
								WCHAR *pResName = new WCHAR[pName->Length + 1]{};
								memcpy(pResName, pName, (pName->Length) * sizeof(WCHAR));
								wprintf(L"pResName %s\n", pResName);
								delete[] pResName;
							}
							else   //id
							{
								printf("pResEntry->Id %04X\n", pResEntry->Id);
							}
							//判断有没有下一层
							//第三层  同样套路从第一个结构体开始找 到了OffsetToDirectory就是第三层了
							//这里要注意的是到了第三层这个IMAGE_RESOURCE_DIRECTORY_ENTRY结构体的第一个联合体就没用了
							//同时第二个联合体的DataIsDirectory为0没有下一层了 
							//通过OffsetToData字段找到资源结构体的偏移(指向_IMAGE_RESOURCE_DATA_ENTRY结构体)
							if (pResSecondEntry->DataIsDirectory)
							{
								//第三层的起始位置
								DWORD dwResThrid =
									(DWORD)pRes + pResSecondEntry->OffsetToDirectory;
								PIMAGE_RESOURCE_DIRECTORY pResThrid = (PIMAGE_RESOURCE_DIRECTORY)dwResThrid;

								PIMAGE_RESOURCE_DIRECTORY_ENTRY pResThridEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResThrid + 1);
								//第三层,已经是最后一层,使用PIMAGE_RESOURCE_DIRECTORY_ENTRY中的
								//OffsetToData成员,得到PIMAGE_RESOURCE_DATA_ENTRY结构的位置
								/*typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
								DWORD   OffsetToData;   //资源偏移
								DWORD   Size;
								DWORD   CodePage;
								DWORD   Reserved;
								} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
								*/
								PIMAGE_RESOURCE_DATA_ENTRY pResData =
									(PIMAGE_RESOURCE_DATA_ENTRY)(pResThridEntry->OffsetToData + (DWORD)pRes);
								//资源的RVA和Size
								DWORD dwResDataRVA = pResData->OffsetToData;
								DWORD dwResDataSize = pResData->Size;
								//PIMAGE_RESOURCE_DATA_ENTRY中的OffsetToData是个RVA
								DWORD dwResDataFOA = (DWORD)(RVAtoFOA(dwResDataRVA) + g_pFileImageBase);
								//资源的二进制数据
								//遍历打印资源的二进制数据  这里就只能是二进制了
								PBYTE pData = (PBYTE)dwResDataFOA;
								for (int iData = 0; iData < dwResDataSize; iData++)
								{
									if (iData % 16 == 0 && iData != 0)
									{
										printf("\n");
									}
									printf("%02X ", pData[iData]);
								}
								printf("\n");
							}
							//下一个资源
							pResSecondEntry++;
						}
					}
				}
				//下一种资源
				pResEntry++;
			}
		cout << "----------------------------------------------------重定位表------------------------------------------------------------ " << endl;
		//因为代码用的是绝对地址因此要有定位表
		//当重定位发生时,只需要现在pe文件的加载基址,用现在的加载基址减去默认加载基址得到一个数,再用这个数加上需要重定位的数据即可,所以重定位
		//表中保存的只是需要重定位信息
		typedef struct _OFFSET_TYPE
		{
			WORD offset : 12; //本页的偏移量
			WORD type : 4;    //重定位类型(3)
		}OFFSET_TYPE, *POFFSET_TYPE;
		//重定位表RVA
		DWORD dwRelocRVA = g_pNt->OptionalHeader.DataDirectory[5].VirtualAddress;
		//是否为空
		if (!dwRelocRVA)
		{
			printf("没有重定位表\n");
			return;
		}
		//重定位表在文件中的地址
		PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)(RVAtoFOA(dwRelocRVA) + g_pFileImageBase);

		//循环重定位表
		//如果SizeOfBlock为0,说明没有需要重定位的数据了
		while (pReloc->SizeOfBlock)
		{
			//当前重定位页RVA
			printf("%08X\n\n", pReloc->VirtualAddress);
			//这一页一共有多少个重定位块(即多少个需要重定位的数据)
			DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
			//指向重定位块地址
			POFFSET_TYPE pOffset = (POFFSET_TYPE)(pReloc + 1);
			//遍历每一个重定位块   
			for (int i = 0; i < dwCount;i++)
			{
				//在这一页中的位置地址RVA
				DWORD dwRelocDataRVA = pReloc->VirtualAddress + pOffset->offset;
				//转成FOA
				DWORD dwRelocDataFOA = (DWORD)(RVAtoFOA(dwRelocDataRVA) + g_pFileImageBase);
				//实际需要重定位的数据地址是个VA
				DWORD dwRealDataVA = *(DWORD*)dwRelocDataFOA;
				//转成RVA(这就是内存中的转换完成的相对地址了)
				DWORD dwRealDataRVA = dwRealDataVA - g_pNt->OptionalHeader.ImageBase;
				//再转FOA(RVA在文件中的地址)
				DWORD dwRealDataFOA = (DWORD)(RVAtoFOA(dwRealDataRVA) + g_pFileImageBase);
				//需要重定位的具体数据(字节数不确定)
				DWORD dwData = *(DWORD*)dwRealDataFOA;

				printf("需要重定位的第%d个数据 RVA:%08X  VA:%08X  DATA:%08X\n",
					i + 1, dwRelocDataRVA, dwRealDataVA, dwData);
				//下一个重定位数据位置
				pOffset++;
			}

			//下一页
			pReloc =
				(PIMAGE_BASE_RELOCATION)(pReloc->SizeOfBlock + (DWORD)pReloc);
		}

		return ;
	}

}
//void _openFile();
DWORD RVAtoFOA(DWORD dwRVA)
{
	//此RVA落在哪个区段中
	//找到所在区段后,
	//减去所在区段的起始位置,加上在文件中的起始位置
	//大文件头中找区段数
	int nCountOfSection = g_pNt->FileHeader.NumberOfSections;
	//区段表头
	PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(g_pNt);
	//在扩展头中找到块对齐数
	DWORD dwSecAligment = g_pNt->OptionalHeader.SectionAlignment;
	//循环
	for (int i = 0; i < nCountOfSection; i++)
	{
		//求在内存中的真实大小
		//Misc.VirtualSize % dwSecAligment如果是0代表刚好对齐否则就先对齐(非0就是真)
		//Misc.VirtualSize / dwSecAligment * dwSecAligment   + dwSecAligment     //最后加上余数的对齐
		DWORD dwRealVirSize = pSec->Misc.VirtualSize % dwSecAligment ?
			pSec->Misc.VirtualSize / dwSecAligment * dwSecAligment + dwSecAligment
			: pSec->Misc.VirtualSize;
		//区段中的相对虚拟地址转文件偏移  思路是 用要转换的地址与各个区
		//段起始地址做比较如果落在一个区段中(大于起始地址小于起始地址加区段最大偏移和),
		//就用要转换的相对虚拟地址减去区段的起始地址的相对虚拟地址,
		//得到了这个地址相对这个区段偏移,再用得到的这个偏移加上区段在文件中的偏移的起始位置
		//(pointerToRawData字段)就是他在文件中的文件偏移
		if (dwRVA >= pSec->VirtualAddress &&
			dwRVA < pSec->VirtualAddress + dwRealVirSize)
		{
			//FOA = RVA - 内存中区段的起始位置 + 在文件中区段的起始位置 
			return dwRVA - pSec->VirtualAddress + pSec->PointerToRawData;
		}
		//下一个区段地址
		pSec++;
	}
}
int main() {

	myPE();
	cin.get();
}