PE 资源表解析(x64)

admin 2024年2月22日15:54:50评论17 views字数 6549阅读21分49秒阅读模式

不知为何,网上关于 x64 平台下的 PE 解析基本没有... ...

(如果有人见过请留言 ^-^,通过学 x32 改 x64 多多少少有些烦躁)

为了方便测试,创建了一个简单的 WinForm 桌面程序(内容少,便于观察
PE 资源表解析(x64)

其中 Win32 API 是个菜单栏,点击里面的 GetComputerName 弹出模态窗口然后在 label 里显示计算机名。

资源目录头|根目录(IMAGE_RESOURCE_DIRECTORY
typedef struct _IMAGE_RESOURCE_DIRECTORY {    DWORD   Characteristics; // 资源属性,未使用    DWORD   TimeDateStamp; // 资源的创建时间    WORD    MajorVersion; // 主版本号    WORD    MinorVersion; // 次版本号    WORD    NumberOfNamedEntries; // 用户自定义资源类型的数量*    WORD    NumberOfIdEntries; // 预定义资源 ID 类型的数量*} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
资源目录项目|子目录(IMAGE_RESOURCE_DIRECTORY_ENTRY
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {  union {    struct {      DWORD NameOffset:31// 资源名偏移(FOA)      DWORD NameIsString:1// 高位为 1,则 NameOffset 指向 IMAGE_RESOURCE_DIR_STRING_U                            // 高位为 0,则是资源 ID 类型    DWORD   Name; // 用户定义的名称    WORD    Id; // 在一级目录指资源类型,二级目录指资源编号,三级目录指代码的页号  };  union {    DWORD   OffsetToData; // 数据偏移地址(FOA)    struct {      DWORD   OffsetToDirectory:31// 子目录偏移地址(FOA)      DWORD   DataIsDirectory:1// 高位是 1,则 OffsetToDirectory 指向新的 IMAGE_RESOURCE_DIRECTORY                                 // 否则指向 IMAGE_RESOURCE_DATA_ENTRY 结构  };} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
预定义的资源类型 ID:
/* * Predefined Resource Types */#define RT_CURSOR           MAKEINTRESOURCE(1)#define RT_BITMAP           MAKEINTRESOURCE(2)#define RT_ICON             MAKEINTRESOURCE(3)#define RT_MENU             MAKEINTRESOURCE(4)#define RT_DIALOG           MAKEINTRESOURCE(5)#define RT_STRING           MAKEINTRESOURCE(6)#define RT_FONTDIR          MAKEINTRESOURCE(7)#define RT_FONT             MAKEINTRESOURCE(8)#define RT_ACCELERATOR      MAKEINTRESOURCE(9)#define RT_RCDATA           MAKEINTRESOURCE(10)#define RT_MESSAGETABLE     MAKEINTRESOURCE(11)

 NameOffset 指向的 IMAGE_RESOURCE_DIR_STRING_U 结构

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {    WORD    Length; // 字符串的长度    WCHAR   NameString[1]; // UNICODE字符串} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;
资源数据|文件(IMAGE_RESOURCE_DATA_ENTRY
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {    DWORD   OffsetToData; // 资源数据的偏移(RVA)*    DWORD   Size; // 资源数据的长度*    DWORD   CodePage; // 代码页, 一般为 0    DWORD   Reserved; // 保留字段} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
用自己的程序测完后更换其他程序测试,看看输出结果是否正确,这里使用的是 procexp64.exe
#include <Windows.h>#include <stdio.h>
static PCH LoadFileT(PCSTR FilePath) { // 获取文件对象 HANDLE hFile = CreateFileA(FilePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = GetFileSize(hFile, NULL); // 获取文件大小
PCH szBuffer = new CHAR[dwFileSize]; // 用于接收从文件读取的数据 memset(szBuffer, 0, dwFileSize); // 初始化内存
BOOL Result = ReadFile(hFile, szBuffer ,dwFileSize, NULL, NULL); // 读取数据
CloseHandle(hFile); // 关闭文件句柄
if (Result) { return szBuffer; } else { return NULL; }}
// RVA 转 FOAstatic DWORD RvaToFoa(DWORD dwRva, PCH szbuffer) { PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(szbuffer); // DOS Header PIMAGE_NT_HEADERS64 pNt = PIMAGE_NT_HEADERS64(szbuffer + pDos->e_lfanew); // NT Header PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); // 节
// 判断是否落在头部 if (dwRva < pSection->VirtualAddress) { return dwRva; } for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++) { // 区段的开始 = VirtualAddress // 区段的结束 = VirtualAddress + VirtualSize // 判断是否在区段 if (dwRva >= pSection[i].VirtualAddress && dwRva <= pSection[i].VirtualAddress + pSection[i].Misc.VirtualSize) { // 数据的 RVA - 区段的 RVA = 数据的 FOA - 区段的 FOA // 数据的 FOA = 数据的 RVA(传参) - 区段的 RVA + 区段的 FOA return dwRva - pSection[i].VirtualAddress + pSection[i].PointerToRawData; } }}
// 资源static VOID ResourcesTableT(PCH szbuffer, PIMAGE_DATA_DIRECTORY pDataDirectory) { PIMAGE_DATA_DIRECTORY pResourceDir = pDataDirectory + IMAGE_DIRECTORY_ENTRY_RESOURCE; // 定位到资源表 // 使用资源表结构体指针指向资源表所在的内存地址 PIMAGE_RESOURCE_DIRECTORY pResource = PIMAGE_RESOURCE_DIRECTORY(RvaToFoa(pResourceDir->VirtualAddress, szbuffer) + szbuffer);
// 资源总数 = 以字符串命名的资源数量 + 以 ID 命名的资源数量 WORD wCount = pResource->NumberOfIdEntries + pResource->NumberOfNamedEntries; printf("根目录ID条目: %d, 根目录名称条目: %dnn", pResource->NumberOfIdEntries, pResource->NumberOfNamedEntries); // 获取第一个 IDEntries 结构 PIMAGE_RESOURCE_DIRECTORY_ENTRY pFirstEntry = PIMAGE_RESOURCE_DIRECTORY_ENTRY(pResource + 1); // 解析第一层 for (size_t i = 0; i < wCount; i++) { if (pFirstEntry[i].NameIsString == 0) // 为 0,则是资源 ID 类型 { printf("资源类型ID: %dn", pFirstEntry[i].Id); } else // 否则指向 PIMAGE_RESOURCE_DIR_STRING_U { PIMAGE_RESOURCE_DIR_STRING_U pResourceName = (PIMAGE_RESOURCE_DIR_STRING_U)((PCH)pResource + pFirstEntry[i].NameOffset); // 申请内存 PWCHAR pName = new WCHAR[pResourceName->Length + 1]; memset(pName, 0, (pResourceName->Length + 1) * sizeof(WCHAR)); wcsncpy_s(pName, pResourceName->Length + 1, pResourceName->NameString, pResourceName->Length); printf("资源类型名称: %lsn", pName); delete[] pName; // 释放内存 }; // 解析第二层 if (pFirstEntry[i].DataIsDirectory == 1) // 为 1,指向新的根目录 { // 下一层的结构 PIMAGE_RESOURCE_DIRECTORY pSecondResource = (PIMAGE_RESOURCE_DIRECTORY)((PCH)pResource + pFirstEntry[i].OffsetToDirectory); WORD wSecondCount = pSecondResource->NumberOfIdEntries + pSecondResource->NumberOfNamedEntries; // 资源总数 //printf("名称ID条目: %d, 名称条目: %dn", pSecondResource->NumberOfIdEntries, pSecondResource->NumberOfNamedEntries); // 获取第二个 IDEntries 结构 PIMAGE_RESOURCE_DIRECTORY_ENTRY pSecondEntry = PIMAGE_RESOURCE_DIRECTORY_ENTRY(pSecondResource + 1); for (size_t i = 0; i < wSecondCount; i++) { if (pSecondEntry[i].NameIsString == 1) // 为 1,则指向 IMAGE_RESOURCE_DIR_STRING_U { PIMAGE_RESOURCE_DIR_STRING_U pResourceName = (PIMAGE_RESOURCE_DIR_STRING_U)((PCH)pResource + pSecondEntry[i].NameOffset); // 申请内存 PWCHAR pName = new WCHAR[pResourceName->Length+1]; memset(pName, 0, (pResourceName->Length + 1) * sizeof(WCHAR)); wcsncpy_s(pName, pResourceName->Length + 1, pResourceName->NameString, pResourceName->Length); printf("资源编号名称: %lsn", pName); delete[] pName; // 释放内存 } else { printf("资源编号ID: %dn", pSecondEntry[i].Id); } // 第三层 if (pSecondEntry[i].DataIsDirectory == 1) // 为 1,指向新的根目录 { // 第三层的结构(具体资源) PIMAGE_RESOURCE_DIRECTORY pThirdResource = (PIMAGE_RESOURCE_DIRECTORY)((PCH)pResource + pSecondEntry[i].OffsetToDirectory); // 获取第三个 IDEntries 结构 PIMAGE_RESOURCE_DIRECTORY_ENTRY pThirdEntry = PIMAGE_RESOURCE_DIRECTORY_ENTRY(pThirdResource + 1); if (pThirdEntry[i].OffsetToDirectory != 1) // 指向 IMAGE_RESOURCE_DATA_ENTRY 结构 { // 资源数据 PIMAGE_RESOURCE_DATA_ENTRY pResourceData = PIMAGE_RESOURCE_DATA_ENTRY((PCH)pResource+pThirdEntry[i].OffsetToData); printf("资源文件RVA: %X 资源文件FOA: %X 资源文件SIZE: %dnn", pResourceData->OffsetToData, RvaToFoa(pResourceData->OffsetToData, szbuffer), pResourceData->Size); } } } } }}
int main() { //PCH buffer = LoadFileT("C:/project/C#/WinForms/ResourceTableTest/bin/x64/Debug/ResourceTableTest.exe"); PCH buffer = LoadFileT("C:/tools/procexp64.exe");
PIMAGE_DOS_HEADER pDos = PIMAGE_DOS_HEADER(buffer); // DOS Header PIMAGE_NT_HEADERS64 pNt = PIMAGE_NT_HEADERS64(buffer + pDos->e_lfanew); // NT Header PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt); // 节 PIMAGE_OPTIONAL_HEADER64 pOptionalHeader = &pNt->OptionalHeader; // 扩展文件头 PIMAGE_DATA_DIRECTORY pDataDirectory = pOptionalHeader->DataDirectory; // 获取目录表头指针 ResourcesTableT(buffer, pDataDirectory);}
使用 010Editor 验证输出的最后一个和第一个。
PE 资源表解析(x64)

PE 资源表解析(x64)

PE 资源表解析(x64)

PE 资源表解析(x64)

原文始发于微信公众号(走在网安路上的哥布林):PE 资源表解析(x64)

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月22日15:54:50
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   PE 资源表解析(x64)https://cn-sec.com/archives/2515677.html

发表评论

匿名网友 填写信息