不知为何,网上关于 x64 平台下的 PE 解析基本没有... ...
(如果有人见过请留言 ^-^,通过学 x32 改 x64 多多少少有些烦躁)
其中 Win32 API 是个菜单栏,点击里面的 GetComputerName 弹出模态窗口然后在 label 里显示计算机名。
资源目录头|根目录(IMAGE_RESOURCE_DIRECTORY)
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics; // 资源属性,未使用
DWORD TimeDateStamp; // 资源的创建时间
WORD MajorVersion; // 主版本号
WORD MinorVersion; // 次版本号
WORD NumberOfNamedEntries; // 用户自定义资源类型的数量*
// 预定义资源 ID 类型的数量*
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
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;
/*
* Predefined Resource Types
*/
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;
typedef struct _IMAGE_RESOURCE_DATA_ENTRY {
DWORD OffsetToData; // 资源数据的偏移(RVA)*
DWORD Size; // 资源数据的长度*
DWORD CodePage; // 代码页, 一般为 0
DWORD Reserved; // 保留字段
} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;
#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 转 FOA
static 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);
}
原文始发于微信公众号(走在网安路上的哥布林):PE 资源表解析(x64)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论