0
点赞
收藏
分享

微信扫一扫

2.8 PE结构:资源表详细解析

在Windows PE中,资源是指可执行文件中存放的一些固定不变的数据集合,例如图标、对话框、字符串、位图、版本信息等。PE文件中每个资源都会被分配对应的唯一资源ID,以便在运行时能够方便地查找和调用它们。PE文件中的资源都被组织成一个树形结构,其中最顶层为根节点(Root),下一级为资源类型(Type),再下一级为资源名称(Name),最终是实际的资源内容。

PIMAGE_RESOURCE_DIRECTORY是Windows PE可执行文件中的一个结构类型,用于描述资源(Resource)的树形结构,其中包括了每个资源的类型(Type)、名称(Name)和语言(Language),以及指向下一级PE资源目录的地址和相关信息等。

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY
{
union {
struct {
DWORD NameOffset:31;
DWORD NameIsString:1;
} DUMMYSTRUCTNAME;
DWORD Name;
WORD Id;
} DUMMYUNIONNAME;
union {
DWORD OffsetToData;
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
} DUMMYSTRUCTNAME2;
} DUMMYUNIONNAME2;
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

PIMAGE_RESOURCE_DIRECTORY描述了Windows PE资源的目录结构,每个资源目录包括以下字段:

  • Characteristics:指定该目录的属性,如是否允许命名、是否允许ID等;
  • TimeDateStamp:指定该目录的时间戳;
  • MajorVersion / MinorVersion:指定PE文件中允许的最高版本和最低版本;
  • NumberOfNamedEntries:指定该目录中已经命名的资源条目数量;
  • NumberOfIdEntries:指定资源ID类型的数量;
  • PIMAGE_RESOURCE_DIRECTORY_ENTRY:指针,指向资源入口表,即PE文件中每个资源的入口地址。
  • PIMAGE_RESOURCE_DIRECTORY_ENTRY用于引用PE文件中资源的名称、类型和语言信息,它包括了Name/Id:指定资源的名称或ID,根据缩小范围的优先级进行查找,ID的优先级高于名称;
  • OffsetToData:指向该资源的数据偏移地址或其Resource Data Entry的地址。

读者在解析时通常需要在数据目录表PIMAGE_DATA_DIRECTORY中定位到IMAGE_DIRECTORY_ENTRY_RESOURCE资源表,通过循环的方式以此遍历出PIMAGE_RESOURCE_DIRECTORY_ENTRY中的每一个节点,最终输出资源信息,这段输出代码如下所示;

// --------------------------------------------------
// 定义资源表解析结构
// --------------------------------------------------
static char* szResName[0x11] = { 0, (char*)鼠标指针, (char*)位图, (char*)图标, (char*)菜单, (char*)对话框, (char*)字符串列表, (char*)字体目录, (char*)字体, (char*)快捷键, (char*)非格式化资源, (char*)消息列表, (char*)鼠标指针组, (char*)zz, (char*)图标组, (char*)xx, (char*)版本信息 };


int main(int argc, char * argv[])
{
BOOL PE = IsPeFile(OpenPeFile(c://pe/x86.exe), 0);

if (PE == TRUE)
{
// 获取数据目录表
PIMAGE_DATA_DIRECTORY pData = NtHeader->OptionalHeader.DataDirectory;

// 获取到资源目录表
pData = &(pData[IMAGE_DIRECTORY_ENTRY_RESOURCE]);

// 获取资源目录表的偏移
DWORD dwResOffset = RVAtoFOA(pData->VirtualAddress);

// 获取到资源目录表
PIMAGE_RESOURCE_DIRECTORY pRes = (PIMAGE_RESOURCE_DIRECTORY)(GlobalFileBase + dwResOffset);

// 获取紧跟着的IMAGE_RESOURCE_DIRECTORY_ENTRY的个数
DWORD dwResSize = pRes->NumberOfNamedEntries + pRes->NumberOfIdEntries;

// 获取到PIMAGE_RESOURCE_DIRECTORY_ENTRY
PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes + 1);

printf(资源类型ID \t 类型 \n);

for (DWORD i = 0; i < dwResSize; i++)
{

// 如果为0则执行
if (!pResEntry[i].NameIsString)
{
if (pResEntry[i].Id < 0x11)
{
// printf(资源类型ID: %p --> 类型: %s\n, pResEntry[i].Id, szResName[pResEntry[i].Id]);
printf(%p \t %s \n, pResEntry[i].Id, szResName[pResEntry[i].Id]);
}
else
{
char type[20];
sprintf_s(type, %d, pResEntry[i].Id);
// printf(资源类型ID: %p --> 类型: %s\n, pResEntry[i].Id, type);
printf(%p \t %s \n, pResEntry[i].Id, type);
}
}
// 如果为1则执行
else
{
PIMAGE_RESOURCE_DIR_STRING_U pstcString = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pRes + pResEntry[i].NameOffset);
WCHAR szStr[MAX_PATH] = { 0 };
memcpy_s(szStr, MAX_PATH, pstcString->NameString, pstcString->Length * sizeof(WCHAR));
// printf(资源字符串: %ls\n, szStr);
}
}
}
else
{
printf(非标准程序 \n);
}

system(pause);
return 0;
}

编译并运行上述程序片段,则读者可以看到当前程序中所包含的所有资源信息,为了简单可用此处并没有输出递归资源,仅仅输出了第一层,输出效果图如下所示;

本文作者: 王瑞 本文链接: https://www.lyshark.com/post/6532d336.html 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

举报

相关推荐

0 条评论