00013850h: 10 25 03 00 / 30 01 00 00 / E4 04 00 00 / 00 00 00 00
00013850h: 40 26 03 00 / E8 02 00 00 / E4 04 00 00 / 00 00 00 00
00013860h: 28 29 03 00 / E8 02 00 00 / E4 04 00 00 / 00 00 00 00
00013870h: 10 2C 03 00 / 70 00 00 00 / E4 04 00 00 / 00 00 00 00
00013880h: 80 2C 03 00 / 70 00 00 00 / E4 04 00 00 / 00 00 00 00
00013890h: F0 2C 03 00 / 56 03 00 00 / E4 04 00 00 / 00 00 00 00
000138a0h: 48 30 03 00 / C0 01 00 00 / E4 04 00 00 / 00 00 00 00
000138b0h: F0 2C 03 00 / 56 03 00 00 / E4 04 00 00 / 00 00 00 00
000138c0h: 48 30 03 00 / C0 01 00 00 / E4 04 00 00 / 00 00 00 00
000138d0h: 08 32 03 00 / A8 01 00 00 / E4 04 00 00 / 00 00 00 00
000138e0h: B0 33 03 00 / F4 00 00 00 / E4 04 00 00 / 00 00 00 00
000138f0h: A4 34 03 00 / B6 00 00 00 / E4 04 00 00 / 00 00 00 00
00013900h: 5C 35 03 00 / 94 00 00 00 / E4 04 00 00 / 00 00 00 00
00013910h: F0 35 03 00 / 40 04 00 00 / E4 04 00 00 / 00 00 00 00
00013920h: 30 3A 03 00 / DC 02 00 00 / E4 04 00 00 / 00 00 00 00
00013930h: 0C 3D 03 00 / 32 02 00 00 / E4 04 00 00 / 00 00 00 00
00013940h: 40 3F 03 00 / 90 01 00 00 / E4 04 00 00 / 00 00 00 00
00013950h: D0 40 03 00 / FC 04 00 00 / E4 04 00 00 / 00 00 00 00
00013960h: CC 45 03 00 / C0 03 00 00 / E4 04 00 00 / 00 00 00 00
00013970h: 8C 49 03 00 / B6 00 00 00 / E4 04 00 00 / 00 00 00 00
00013980h: 44 4A 03 00 / 84 00 00 00 / E4 04 00 00 / 00 00 00 00
00013990h: C8 4A 03 00 / 22 00 00 00 / E4 04 00 00 / 00 00 00 00
000139a0h: EC 4A 03 00 / 22 00 00 00 / E4 04 00 00 / 00 00 00 00
000139b0h: 10 4B 03 00 / 60 03 00 00 / E4 04 00 00 / 00 00 00 00
000139c0h: 70 4E 03 00 / 60 03 00 00 / E4 04 00 00 / 00 00 00 00
000139d0h: 06 00 / 54 00 45 00 4C 00 4E 00 45 00 54 00 00 00
(IMAGE_RESOURCE_DIR_STRING结构,长度可变。第一个字段2个字节长,值为6。表明其后的Unicode字符串长度为6。第二字段是一个Unicode字符串,不包括最后的结束符,长度为6,内容是"TELNET\0")
000139e0h: 28 00 00 00 20 00 00 00 40 00 00 00 01 00 01 00
000139f0h: ...
需要补充说明的是,每个directory后面紧跟的是directory_entry数组,directory_entry数组的每个元素,有两个字段,每个字段的高位用来判断该字段代表的含义。尤其是第二字段 OffsetToData ,如果高位为1表明还有下一层,指向另一个directory。如果高位为0,表明指向一个data_entry。directory_entry第一个字段通常都是作为id,里面低WORD中的值,用来标示这个directory_entry,很少的情况下,第一字段保存一个到unicode字符串的偏移(本例中000136a0h),用字符串来标示这个directory_entry。如果一个directory后两个字段都不为0的话,即后面紧跟的directory_entry数组既有NamedEntries,又有IDEntries,那么directory_entry数组首先是NamedEntries之后紧跟着IDEntries。一般情况下都是一般来说都是有三层,第一层中的directory_entry数组的每个元素的id,代表不同的类型,不同类型的值在 WINGDI.H 中定义如下
#define RT_CURSOR MAKEINTRESOURCE(1)
#define RT_BITMAP MAKEINTRESOURCE(2)
#define RT_ICON MAKEINTRESOURCE(3)
#define RT_MENU MAKEINTRESOURCE(4)
#define RT_DIALOG MAKEINTRESOURCE(5)
#define RT_STRING MAKEINTRESOURCE(6)
#define RT_FONTDIR MAKEINTRESOURCE(7)
#define RT_FONT MAKEINTRESOURCE(8)
#define RT_ACCELERATOR MAKEINTRESOURCE(9)
#define RT_RCDATA MAKEINTRESOURCE(10)
#define RT_MESSAGETABLE MAKEINTRESOURCE(11)
#define DIFFERENCE 11
#define RT_GROUP_CURSOR MAKEINTRESOURCE((DWORD)RT_CURSOR + DIFFERENCE)
#define RT_GROUP_ICON MAKEINTRESOURCE((DWORD)RT_ICON + DIFFERENCE)
#define RT_VERSION MAKEINTRESOURCE(16)
#define RT_DLGINCLUDE MAKEINTRESOURCE(17)
#if(WINVER >= 0x0400)
#define RT_PLUGPLAY MAKEINTRESOURCE(19)
#define RT_VXD MAKEINTRESOURCE(20)
#define RT_ANICURSOR MAKEINTRESOURCE(21)
#define RT_ANIICON MAKEINTRESOURCE(22)
#endif /* WINVER >= 0x0400 */
#define RT_HTML MAKEINTRESOURCE(23)
也有可能有不到三层的情况,比如只有类型和Name两层,没有Language层。
我们再来看几个data_entry
00013850h: 10 25 03 00 / 30 01 00 00 / E4 04 00 00 / 00 00 00 00
00013850h: 40 26 03 00 / E8 02 00 00 / E4 04 00 00 / 00 00 00 00
00013860h: 28 29 03 00 / E8 02 00 00 / E4 04 00 00 / 00 00 00 00
可以算出 00013850h 处的 data_entry 中,资源的文件位置为 13B10h(32510-32000+13600) 长度为 130h,所以该资源结束处的位置在文件中的 13C40 h处。而一下个data_entry (00013850h处)中,资源在文件中的位置为 13C40h (32640-32000+13600) 长度为 2E8h。我们可以看到两个资源是首尾相接的,就是说一个资源和另一个资源是紧挨在一起的,中间没有空隙,其他的资源用相同的方法计算,也可以得到同样的结论。
总结,找到资源节开始的位置,首先是一个directory,后面紧跟着directory_entry数组,数组的每个元素代表的资源类型不同,通过每个元素,我们可以找到第二层另一个directory,后面紧跟着directory_entry数组。这一层的数组的每个元素代表的资源Name不同。然后我们可以找到第三层的每个directory,后面紧跟着directory_entry数组。这一层的数组的每个元素代表的资源Language不同。然后通过每个directory_entry我们可以找到每个data_entry。通过每个data_entry,我们就可以找到每个真正的资源。
本部分内容较为复杂,需要多阅读几遍。
三 遍历PE文件中的资源
遍历那个树型结构,找到每个资源的方法之一是,
一个函数,用来处理directory和它后面紧跟着的directory_entry数组。比如叫 DumpResourceDirectory(),它的参数中的一个是一个directory的地址,函数根据这个地址,得到一个directory结构,从中得到directory_entry数组元素的个数。然后for循环遍历每个元素,对于每个元素做判断看是否已经到了叶子,也就是directory_entry的第二个字段的高位是否为0,是1表示没有到叶子,递归调用本函数,不过传入的参数,是根据这个directory_entry中保存的另一个directory的地址。是0表示已经到了叶子,调用另一个处理叶子的函数,传入相关地址。
处理叶子的函数,用来处理data_entry结构,负责根据data_entry结构找到真正的资源。比如叫DumpResourceEntry(),它的参数中的一个是一个data_entry的地址。然后跟据data_entry中的值作处理。
这样,通过递归和判断,就能遍历PE文件中所有的资源。
用这种方法遍历图4.1中的树,顺序会是11,21,31,32,22,33,34,35,36,37,38,39,310,23,311,24,312。
这种遍历方法的源程序,可以参考 PEDUMP - Matt Pietrek 1995 。《Windows95系统程式设计大奥秘》附书源码中有。
完
上一页 1 2 3 4