PE文件格式学习(五):资源表

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tutucoo/article/details/83828694

1.概述

程序内部和外部的界面等元素的二进制数据统称为资源,程序把它们放在一个特定的表中,符合数据和程序分离的设计原则。

Windows程序中的资源大致分为六类:菜单、对话框、位图、光标、图标、自定义资源

资源表是数据目录表中的第三个元素,排在导入表的后面。

2.资源表解析

资源表的解析比较复杂,可能是所有数据目录表中最复杂的一个。我将结合例子说明,这样会比较好理解。

我们的文件在十六进制工具010editor的帮助下,将PE中的结构全部标记了出来。资源表是数据目录表的第三个,查找到它的RVA是0x4000,转换成offset还是0x4000,找到它的位置如下图所示:

从0x4000开始就是资源表的结构体了,这个结构体叫IMAGE_RESOURCE_DIRECTORY,有如下字段:

struct IMAGE_RESOURCE_DIRECTORY
{
    DWORD Characteristics;
    DWORD  TimeDateStamp;
    WORD MajorVersion;
    WORD  MinorVersion;
    WORD  NumberOfNamedEntries;
    WORD  NumberOfIdEntries;
}

Characteristics:属性,一般为0,对应上图中的0x00000000

TimeDateStamp:时间戳,一般为0(前面说过文件头也有这个字段,导出表、导入表都有这个字段,都有值,在这里很多时间为0),对应上图中的0x00000000

MajorVersion:资源的版本,一般为0,对应上图中的0x0000

MinorVersion:一般为0,对应上图中的0x0000

扫描二维码关注公众号,回复: 4024033 查看本文章

NumberOfNamedEntries:以字符串命名的资源数量,对应上图中的0x0000

NumberOfIdEntries:以整型数字命名的资源数量,对应上图中的0x0003

上图中NumberOfNamedEntries和NumberOfIdEntries的和是0x3,表明这个程序有3个资源项目,也就是说紧跟着IMAGE_RESOURCE_DIRECTORY结构体的有3个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,这里展示一下IMAGE_RESOURCE_DIRECTORY_ENTRY结构体:

struct IMAGE_RESOURCE_DIRECTORY_ENTRY
{
     DWORD Name;
     DWORD OffsetToData;
}

Name:对应上图中的0x00000003(第一个IMAGE_RESOURCE_DIRECTORY_ENTRY),当它的最高位是1的时候,表示低位作为一个指针使用,指向IMAGE_RESOURCE_DIR_STRING_U结构;当它的最高位是0的时候,分为三种情况:当结构用于第1层目录时定义的是资源类型,用于第2层目录时定义的是资源ID,用于第3层目录时定义的是资源语言类型(0x409表示是英语)。第1层-第3层是什么意思呢?简单的理解就是,资源表的解析分为三层,每一层都是IMAGE_RESOURCE_DIRECTORY+IMAGE_RESOURCE_DIRECTORY_ENTRY的形式,第二个字段OffsetToData的最高位是1的时候就会指向下一层,每一层都是一样的IMAGE_RESOURCE_DIRECTORY和IMAGE_RESOURCE_DIRECTORY_ENTRY的结构,大概是这个样子,后面会更详细的介绍,现在有个印象就好。IMAGE_RESOURCE_DIR_STRING_U的结构体如下:

struct IMAGE_RESOURCE_DIR_STRING_U
{
    WORD Length;
    WCHAR NameString;
}

这个资源的Name是0x00000003,最高位是0,并且它在第一层,也就是说0x00000003代表的是资源类型,那么资源类型又有哪些呢?下表列出了一些类型,所以这个资源是图标。

offsetToData:对应上图的0x80000028,当它的最高位是1的时候,低位数据指向下一层的IMAGE_RESOURCE_DIRECTORY的起始地址,当它的最高位是0的时候,指针指向IMAGE_RESOURCE_DATA_ENTRY结构,这个结构后面会说,它是真正的资源数据的地址,一般情况下,分析到第三层的时候,offsetToData的最高位会是0。值得注意的是offsetToData作为指针的时候(0x80000028),28是从资源块最开始的地方开始计算偏移的,它不是RVA。IMAGE_RESOURCE_DATA_ENTRY的结构体是:

IMAGE_RESOURCE_DATA_ENTRY STRUCT

    OffsetToData DWORD ? ; 资源数据的RVA
    Size1 DWORD ? ; 资源数据的长度
    CodePage DWORD ? ; 代码页, 一般为0
    Reserved DWORD ? ; 保留字段

IMAGE_RESOURCE_DATA_ENTRY ENDS

这个结构体,暂时先不用管它,等分析到第三层的时候再回来看。

先总结一下,我已经分析了第一层的IMAGE_RESOURCE_DIRECTORY,它从0x4000开始,占据16个字节,它的NumberOfNameEntries和NumberOfNameIds的总和是0x3,因此从0x4010开始有3个IMAGE_RESOURCE_DIRECTORY_ENTRY结构体,也就是3个资源,每个结构体占8个字节,所以从0x4010-0x4027是它的内容,这三个结构体的Name字段的最高位都是0,所以它们代表的是类型,分别是0x03图标、0x04菜单、0x0E图标组,它们的OffsetToData字段的最高位是1,所以指向的位置是下一层。

接下来我们找到位置开始分析第二层的内容。

根据第一个资源的OffsetToData的低位字节0x28,我们从0x4000往后数0x28个字节,找到如下图的内容,我们知道OffsetToData这时指向的是第二层的IMAGE_RESOURCE_DIRECTORY结构体。所以它同样是16个字节。

最后的NumberOfIdEntries为0x01,所以它的后面紧跟着一个IMAGE_RESOURCE_DIRECTORY_ENTRY,也就是如下图中所示:

Name字段的最高位是0,我们知道最高位是0,并且Name在第二层代表的是资源ID,所以这个资源的ID是0x01,OffsetToData最高位是1,所以它的最低位指向的是最后一层,也就是第三层。通过0x4000+0x70,我们找到0x4070,这里就是第三层的IMAGE_RESOURCE_DIRECTORY。如下图

我们看到NumberOfIdEntries为0x01,所以第三层也只有一个IMAGE_RESOURCE_DIRECTORY_ENTRY。如下图:

这时我们看到它的第一个元素Name的最高位是0,在第三层Name最高位为0就代表资源语言类型,这时表示的资源语言类型就是0x804,OffsetToData的最高位是0,所以0xB8+0x4000处就是IMAGE_RESOURCE_DATA_ENTRY,如下图:

我们再来回顾一下这个结构体:

IMAGE_RESOURCE_DATA_ENTRY STRUCT

    OffsetToData DWORD ? ; 资源数据的RVA
    Size1 DWORD ? ; 资源数据的长度
    CodePage DWORD ? ; 代码页, 一般为0
    Reserved DWORD ? ; 保留字段

IMAGE_RESOURCE_DATA_ENTRY ENDS

所以资源的RVA是0x4100,转为offset仍然是0x4100,这个资源的起始位置就是这个0x4100,长度是0x2e8。

3.总结

我们还没有碰到IMAGE_RESOURCE_DIRECTORY_ENTRY里面的Name为1的情况,以后可能会补充。资源表的理论说起来很复杂,其实没那么复杂,只要写一遍PE分析工具,在代码中可以更好地理解资源表。

猜你喜欢

转载自blog.csdn.net/tutucoo/article/details/83828694