解析PE資源表與重定位表
阿新 • • 發佈:2018-04-18
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資源表與重定位表