Chinese:
不得不说资源的结构比较复杂。。其实就类似于文件系统,一个文件夹中嵌套着另一个文件夹,而文件则是我们需要找的资源数据。
1、数据目录表中的IMAGE_DIRECTORY_ENTRY_RESOURCE的第三项包含资源的RVA和大小。
2、数据目录结构中的每一个节点都是由IMAGE_RESOURCE_DIRECTORY结构和紧跟其后的数个IMAGE_RESOURCE_DIRECTORY_DIRECTORY_ENTRY结构组成的。
3、IMAGE_RESOURCE_DIRECTORY结构
IMAGE_RESOURCE_DIRECTORY STRUCT Characteristic DWORD ? ;理论上为资源的属性,不过事实上总是0 TimeDateStamp DWORD ? ;资源的产生时刻 MajorVersion WORD ? ;理论上为资源的版本,不过事实上总是0 MinorVersion WORD ? NumberOfNamedEntries WORD ? ;以名称(字符串)命名的入口数量 NumberOfIdEntries WORD ? ;以ID(整形数)命名的入口数量 IMAGE_RESOURCE_DIRECTORY ENDS
4、资源目录入口结构IMAGE_RESOURCE_DIRECTORY_ENTRY
IMAGE_RESOURCE_DIRECTORY_ENTRY STRUCT Name DWORD ? ;目录项的名称字符串指针或ID OffsetToData DWORD ? ;目录项指针 IMAGE_RESOURCE_DIRECTORY_ENTRY ENDS
[Name]
当结构用于第一层目录时,定义的是资源类型;当结构定义于第二层目录时,定义的是资源名称;当结构用于第三层目录时,定义的是代码页编号。
当最高位为0时,表示字段值作为ID使用;最高位为1时,字段的低位作为指针使用(资源名称字符串使用Unicode编码),该指针指向一个IMAGE_RESOURCE_DIR_STRING_U结构
IMAGE_RESOURCE_DIR_STRING_U STRUCT Length DWORD ? ;字符串的长度 NameString DWORD ? ;Unicode字符串,由于字符串是不定长得。由Length指定长度 IMAGE_RESOURCE_DIR_STRING_U ENDS
Name的ID意义如下图:
[OffsetOfData]
当最高位为1时,指向下一层目录块的地址;最高位为0时,指针指向IMAGE_RESOURCE_DATA_ENTRY结构
注意:将Name和OffsetToData用作指针时,该指针是从资源区块开始的地方计算的偏移量。
IMAGE_RESOURCE_DATA_ENTRY STRUCT OffsetToData DWORD ? ;资源数据的RVA Size DWORD ? ;资源数据的长度 CodePage DWORD ? ;代码页,一般为0 Reserved DWORD ? ;保留字段 IMAGE_RESOURCE_DATA_ENTRY ENDS
这个结构就是真正的资源数据了。OffsetToData就是指向资源数据的指针,RVA
5、实例寻找练练手
(1) 通过PEInfo获得数据目录的资源地址
(2) 查找该地址所在区块,对应的物理地址(ROffset)为0x00206200
(3) 用16进制编辑器打开该程序,定位到0x00206200地址,第一层就是IMAGE_RESOURCE_DIRECTORY结构,我们可以看到该结构没有以字符串命名的入口,但有4个以ID命名的入口,这说明,该结构体并不是我们要找的资源结构,还要深入。
(4) 我们看到接下来的IMAGE_RESOURCE_DIRECTORY_ENTRY结构,发现Name=3,最高位果然是0(说明是ID),3代表的资源是图标,而指针指向的地址是0x80000030,可见最高位是1(8=1000b),所以不是指向真正的资源数据,而是下一个IMAGE_RESOURCE_DIRECTORY结构(第二层),偏移为0x30,我们加上资源目录的首地址得到0x206230。
(5) 同理我们在该地址找到Name=1,仍然是ID,OffsetToData=0x800000A8,最高位仍然是1,所以继续指向下一个IMAGE_RESOURCE_DIRECTORY结构(第三层),偏移为0xA8,加上资源目录首地址为0x2062A8
(6) 同理Name=0x0411,仍然是ID,OffsetToData=0x00000150,最高位是0了!这说明我们终于找到了头,也就是指向了真正的资源结构IMAGE_RESOURCE_DATA_ENTRY,地址为0x206350。
(7) 该地址的前两个双字类型记录了资源数据的RVA和长度,OffsetToData=0x5021D0,Size=0xD3D6,我们定位到0x206200+0x1D0=0x2063D0。从这个地址开始计算0xD3D6个长度就是我们的第一个图标资源。