1. 程式人生 > >解析PE資源表與重定位表

解析PE資源表與重定位表

PE

技術分享圖片

#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保存的就是這個字符串的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));
                    //因為是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();
}

解析PE資源表與重定位表