appsion 发表于 2020-11-3 09:27

PE 文件解析5-数据目录[资源表]

本帖最后由 appsion 于 2020-11-3 12:33 编辑

PE 文件解析5-数据目录[资源表]
新手学习PE文件解析记录,持续更新. 部分参考来源于网络, 无法逐一标注, 如果侵权,请及时联系. 如有错误请指正,误喷.

上文连接: https://www.52pojie.cn/thread-1295307-1-1.html




资源树分为三层:
      第一层                资源类型的数量
      第二层                资源的数量
      第三层                资源数据的数量

示意图:




结构体: IMAGE_RESOURCE_DIRECTORY
说明: 资源目录表
头文件: winnt.h
帮助文档: 仅存在于头文件, 没有帮助文档
参考文档: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#resource-directory-table



typedef struct _IMAGE_RESOURCE_DIRECTORY
{
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    WORD    NumberOfNamedEntries;
    WORD    NumberOfIdEntries;
//IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;

参数说明: 仅供参考, 详细说明请参考原文.
    Characteristics
      资源描述
    TimeDateStamp
      时间日期戳
    MajorVersion
      资源主要版本
    MinorVersion
      资源次要版本
    NumberOfNamedEntries
      资源用字符串命名的数量.
    NumberOfIdEntries
      资源用ID命名的数量.



结构体: IMAGE_RESOURCE_DIRECTORY_ENTRY
说明: 资源目录项
头文件: winnt.h
帮助文档: 仅存在于头文件, 没有帮助文档
参考文档: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#resource-directory-entries


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;

参数说明: 仅供参考, 详细说明请参考原文.
DUMMYUNIONNAME
    第一层: 资源类型标识, 第二层: 资源标识, 第三层: 国家地区语言标识
    DUMMYUNIONNAME.DUMMYSTRUCTNAME.NameOffset
      DUMMYUNIONNAME 的低31位值.
            当 NameIsString == 0 时表示资源目录类型. 也就是 DUMMYUNIONNAME.Id.
            当 NameIsString == 1 时表示资源目录名称的偏移. IMAGE_RESOURCE_DIR_STRING_U 结构. 相对于资源目录表(IMAGE_RESOURCE_DIRECTORY)的起始位置 + NameOffset 的偏移.
    DUMMYUNIONNAME.DUMMYSTRUCTNAME.NameIsString
      DUMMYUNIONNAME 的最高位值.用于判断 NameOffset 为名称还是资源类型.
    DUMMYUNIONNAME.Name
      DUMMYUNIONNAME 的值.
    DUMMYUNIONNAME.Id
      资源类型的ID.

DUMMYUNIONNAME2
    第一层: 资源偏移, 第二层: 资源数据偏移, 第三层: 资源数据的偏移
    DUMMYUNIONNAME2.OffsetToData
      DUMMYUNIONNAME2 的值
    DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.OffsetToDirectory
      DUMMYUNIONNAME2 值的低31位值.
            当 DataIsDirectory == 0 时, 表示资源数据的偏移, IMAGE_RESOURCE_DATA_ENTRY 结构.
            当 DataIsDirectory == 1 时, 表示资源目录的偏移, IMAGE_RESOURCE_DIRECTORY 结构. 相对资源目录表(IMAGE_RESOURCE_DIRECTORY)的起始位置 + OffsetToDirectory偏移.
    DUMMYUNIONNAME2.DUMMYSTRUCTNAME2.DataIsDirectory
      DUMMYUNIONNAME2 值的最高位值


LCID, 区域设置标识符
参考地址:
https://docs.microsoft.com/zh-cn/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c#Appendix_A_8
https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/70feba9f-294e-491e-b6eb-56532684c37f

资源类型
原文地址: https://docs.microsoft.com/en-us/windows/win32/menurc/resource-types

RT_CURSOR1光标资源
RT_BITMAP2位图资源
RT_ICON3图标资源
RT_MENU4菜单资源
RT_DIALOG5对话框资源
RT_STRING6字符串表条目。
RT_FONTDIR7字体目录资源。
RT_FONT8字体资源
RT_ACCELERATOR9加速器表。
RT_RCDATA10应用程序定义的资源(原始数据)
RT_MESSAGETABLE11消息表条目。
RT_GROUP_CURSOR12与硬件无关的游标资源。
RT_GROUP_ICON14与硬件无关的图标资源。
RT_VERSION16版本资源。
RT_DLGINCLUDE17允许资源编辑工具将字符串与.rc文件关联。 通常,字符串是提供符号名称的头文件的名称。 资源编译器解析该字符串,但忽略该值。 例如,1 DLGINCLUDE“ MyFile.h”
RT_PLUGPLAY19即插即用资源。
RT_VXD20VXD。
RT_ANICURSOR21动画的光标。
RT_ANIICON22动画的图标。
RT_HTML23HTML资源。
RT_MANIFEST24并排组装清单。


结构体: IMAGE_RESOURCE_DATA_ENTRY
说明: 资源数据
头文件: winnt.h
帮助文档: 仅存在于头文件, 没有帮助文档
参考文档: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#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;

参数说明: 仅供参考, 详细说明请参考原文.
    OffsetToData
      资源数据的RVA
    Size
      资源数据大小
    CodePage
      代码页
    Reserved
      保留


结构体: IMAGE_RESOURCE_DIR_STRING_U
说明: 资源目录项名称
头文件: winnt.h
帮助文档: 仅存在于头文件, 没有帮助文档
参考文档:https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#resource-directory-string


typedef struct _IMAGE_RESOURCE_DIR_STRING_U
{
    WORD    Length;
    WCHAR   NameString[ 1 ];
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

参数说明: 仅供参考, 详细说明请参考原文.
    Length
      字符串资源目录项名称的长度.
    NameString
      资源目录项名称. 该字符串为Unicode字符串.


5.1 获取资源目录树
注: 资源分为二种, 字符串命名的资源和ID命名的资源

示意图
         

注释说明:
// 资源总数量
IMAGE_RESOURCE_DIRECTORY.NumberOfIdEntries + IMAGE_RESOURCE_DIRECTORY.NumberOfNamedEntries

// 节表
SectionHead

// 节表数量
SectionCount

// 资源表RVA
m_DataDirRVA

// RVA 转 FOA,详情说明参见前面章节.
RVAToRaw(SectionHead, SectionCount, m_DataDirRVA)

// 文件指针
m_DataBuff

// 资源FOA
DWORD ResourceFOA = (DWORD)m_DataBuff + RVAToRaw(SectionHead, SectionCount, m_DataDirRVA);

// 获取资源树
GetResourceEntry(ResourceFOA, 0, 0);


// 参数说明:
//    ResourceFOA, 资源FOA
//    ResourceFOAOffset, 资源偏移
//    depth, 资源树深度
void GetResourceEntry(DWORD ResourceFOA, DWORD ResourceFOAOffset, int depth)
{
      // 获取节表
      int SectionCount = 0;
      IMAGE_SECTION_HEADER *SectionHead = GetSectionHead(m_DataBuff, SectionCount);

      // 资源树
      IMAGE_RESOURCE_DIRECTORY *ResourceDir = (IMAGE_RESOURCE_DIRECTORY*)(ResourceFOA + ResourceFOAOffset);

      // 遍历资源
      for (int i = 0; i < ResourceDir->NumberOfIdEntries + ResourceDir->NumberOfNamedEntries; i++)
      {
                // 获取资源
                IMAGE_RESOURCE_DIRECTORY_ENTRY ResourceEntry = ((IMAGE_RESOURCE_DIRECTORY_ENTRY*)((DWORD)ResourceDir + sizeof(IMAGE_RESOURCE_DIRECTORY)));

                // 获取资源名称
                CString RCTypeName;
                int RCTypeId = 0;
                GetResourceName(ResourceFOA, &ResourceEntry, depth, RCTypeName, RCTypeId);



                // 最高位为1时, 低32位为 IMAGE_RESOURCE_DIRECTORY 结构的偏移
                // 最高位为0时, 低32位为 IMAGE_RESOURCE_DATA_ENTRY 结构的偏移
                if (ResourceEntry.DataIsDirectory)
                {
                        GetResourceEntry(ResourceFOA, ResourceEntry.OffsetToDirectory, depth + 1);
                }
                else
                {
                        // LCID
                        m_ResourceData->ResourceLCID = ResourceEntry.Id;

                        // 资源数据
                        IMAGE_RESOURCE_DATA_ENTRY * ResourceData = (IMAGE_RESOURCE_DATA_ENTRY *)(ResourceFOA + ResourceEntry.OffsetToDirectory);
                        m_ResourceData->ResourceDataRVA = ResourceData->OffsetToData;
                        m_ResourceData->ResourceDataFOA = RVAToRaw(SectionHead, SectionCount, ResourceData->OffsetToData);
                        m_ResourceData->ResourceDataSize = ResourceData->Size;
                }
      }
}



// 资源类型
void GetResourceName(DWORD ResourceFOA, IMAGE_RESOURCE_DIRECTORY_ENTRY *ResourceEntry, int depth, CString &RCTypeName, int &RCTypeId)
{
      CString Tempstr;
      if (ResourceEntry->NameIsString)
      {
                IMAGE_RESOURCE_DIR_STRING_U *ResourceDirName = (IMAGE_RESOURCE_DIR_STRING_U*)(ResourceFOA + ResourceEntry->NameOffset);
                WCHAR *Buff = new WCHAR;
                memset(Buff, 0, (ResourceDirName->Length + 1) * 2);
                wcsncpy_s(Buff, ResourceDirName->Length + 1, ResourceDirName->NameString, ResourceDirName->Length);
                Tempstr = (PWCHAR)Buff;
                delete Buff;
      }
      else
      {
                switch (depth)
                {
                case 0:
                              switch (ResourceEntry->Id)
                              {
                              case 1: Tempstr = "光标"; break;
                              case 2: Tempstr = "位图"; break;
                              case 3: Tempstr = "图标"; break;
                              case 4: Tempstr = "菜单"; break;
                              case 5: Tempstr = "对话框"; break;
                              case 6: Tempstr = "字符串"; break;
                              case 7: Tempstr = "字体目录"; break;
                              case 8: Tempstr = "字体"; break;
                              case 9: Tempstr = "加速键"; break;
                              case 10: Tempstr = "RC数据"; break;
                              case 11: Tempstr = "消息表"; break;
                              case 12: Tempstr = "光标组"; break;
                              case 14: Tempstr = "图标组"; break;
                              case 16: Tempstr = "版本信息"; break;
                              case 23: Tempstr = "HTML"; break;
                              case 24: Tempstr = "清单"; break;
                              default: Tempstr.Format("%d", ResourceEntry->Id); break;
                              }
                        break;

                case 1: Tempstr.Format("%d", ResourceEntry->Id); break;

                case 2:

                        Tempstr.Format("区域语言标识: 0x%04X", ResourceEntry->Id);
                        break;

                default:
                        Tempstr = "未知";
                        break;
                }
      }
      
      RCTypeName = Tempstr;
}

a522wds 发表于 2022-3-18 23:36

碎步流年 发表于 2020-11-3 14:20

挺不错,来看看

teachqwe 发表于 2020-11-3 17:20

虽然看不懂,但能知道楼主博学,楼主真牛,老湿这波操作稳了。

zouyibuzjc 发表于 2020-11-3 19:10

谢谢您的鼓励,挺不错

Insist_2020 发表于 2020-11-3 19:32

感谢分享!!!!!!!!

huangxw 发表于 2020-11-3 20:24

谢谢分享,最近正好有用

归来仍是英雄 发表于 2020-11-4 14:01

挺不错,虽然看不懂,但博主牛逼

lifz888 发表于 2020-11-4 15:36

非常好的学习资料,支持分享

kssak 发表于 2020-11-4 20:18

感谢楼主分享,学习了

碎步流年 发表于 2020-11-4 21:19

感谢,向大佬学习
页: [1] 2
查看完整版本: PE 文件解析5-数据目录[资源表]