分享
 
 
 

JIURL PE 格式学习总结(四)-- PE文件中的资源

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

JIURL PE 格式学习总结(四)-- PE文件中的资源

作者: JIURL

主页: http://jiurl.yeah.net/

日期: 2003-4-24

程序所用到的各种资源,比如 bmp,cursor,menu,对话框等都存在PE文件中。

我们将详细介绍关于资源的各种结构,通过一个例子来说明资源及其相关结构是怎么放在PE文件中的。以及如何在遍历PE文件中的所有资源。我们只最终找到这些资源在文件中的位置和长度。而不具体分析某种资源的格式,比如有个BMP的资源,我们不分析BMP格式。

一 找到资源在文件中位置。

资源都放在PE文件的某个节中,该节的节表项中的PointerToRawData,就是资源节在文件中的位置。

1.1 得到PE Header在文件中的位置。

通过DOS Header结构的成员e_lfanew,可以确定PE Header的在文件中的位置。

1.2 得到文件中节的数目。

确定PE Header的在文件中的位置之后,就可以确定PE Header中的成员FileHeader和成员OptionalHeader在文件中的位置。根据 FileHeader 中的 成员NumberOfSections 的值,就可以确定文件中节的数目,也就是节表数组中元素的个数。

1.3 得到节表在文件中的位置。

PE Header在文件中的位置加上PE Header结构的大小就可以得到节表在文件中的开始位置。PE Header结构的大小可以由Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade来确定。其实到目前为止SizeOfOptionalHeade也就是结构Optional Header的大小也是固定的,所以整个PE Header结构的大小也是固定。不过为了安全起见,还是用Signature的大小加上FileHeader的大小再加上FileHeader中的SizeOfOptionalHeade来确定比较保险。

1.4 得到资源节在文件中的位置。

第1.2步中我们确定了文件中节的数目,第1.3步中我们确定了节表在文件中的位置。

现在有两种方法来确定资源在文件中的位置。

第一种方法,根据节的数目,遍历节表数组。也就是从0到(节表数-1)的每一个节表项。

比较每一个节表项的Name字段,看是否等于".rsrc"。如果等于。就找到了资源节的节表项。

这个节表项中的 PointerToRawData 中的值,就是资源节在文件中的位置。

第二种方法,取得PE Header中的Optional Header中的DataDirectory数组中的第三项,

也就是资源项。DataDirectory[]数组的每项都是IMAGE_DATA_DIRECTORY结构,该结构定义如下。

typedef struct _IMAGE_DATA_DIRECTORY {

DWORD VirtualAddress;

DWORD Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

取得DataDirectory数组中的第三项中的成员VirtualAddress的值。这个值就是在内存中资源节的RVA。

然后根据节的数目,遍历节表数组。也就是从0到(节表数-1)的每一个节表项。

每个节在内存中的RVA的范围是从该节表项的成员VirtualAddress字段的值开始(包括这个值),

到VirtualAddress+Misc.VirtualSize的值结束(不包括这个值)。

我们遍历整个节表,看我们取得的资源节的RVA,在哪个节表项的RVA范围之内。

如果在范围之内,就找到了资源节的节表项。

这个节表项中的 PointerToRawData 中的值,就是资源节在文件中的位置。

如果这个PE文件没有资源的话,DataDirectory数组中的第三项内容为0。

这样我们就得到了资源在文件中开始的位置。

二 PE文件中的资源。

我们已经得到了资源节在文件中的位置。

资源节最开始是一个IMAGE_RESOURCE_DIRECTORY结构。

在WINNT.H中定义如下。

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;

这个结构长度为16字节,共有6个字段。

各字段含义如下:

Characteristics: Resource flags,保留用于以后使用,目前都为0。

TimeDateStamp:资源编译器产生资源的时间。

MajorVersion:

MinorVersion:

NumberOfNamedEntries:用字符串来标示IMAGE_RESOURCE_DIRECTORY_ENTRY项的,紧跟着本结构的IMAGE_RESOURCE_DIRECTORY_ENTRY数组的成员个数。

Number of ID Entries:用整形数字来表示IMAGE_RESOURCE_DIRECTORY_ENTRY项的,紧跟着本结构的IMAGE_RESOURCE_DIRECTORY_ENTRY数组的成员个数。

IMAGE_RESOURCE_DIRECTORY后面一定会紧跟着一个IMAGE_RESOURCE_DIRECTORY_ENTRY数组。

IMAGE_RESOURCE_DIRECTORY_ENTRY结构定义如下。

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {

union {

struct {

DWORD NameOffset:31;

DWORD NameIsString:1;

};

DWORD Name;

WORD Id;

};

union {

DWORD OffsetToData;

struct {

DWORD OffsetToDirectory:31;

DWORD DataIsDirectory:1;

};

};

} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

这个结构长度为8个字节。共有两个字段,每个字段4个字节。

根据不同情况,这两个字段的含义不一样。这个结构的定义如果看不懂的话,后面的例子一看就会明白了。

第一个字段,当第一个字段的最高位是1的时候,表示,这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容是一个IMAGE_RESOURCE_DIR_STRING,用里面的字符串来标明这个IMAGE_RESOURCE_DIRECTORY_ENTRY。当第一个字段的最高位是0的时候,表示,这个DWORD的低WORD中的值作为id标明这个IMAGE_RESOURCE_DIRECTORY_ENTRY。

第二个字段,当第二个字段的最高位是1的时候,表示,还有下一层的结构。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容会是一个下一层的IMAGE_RESOURCE_DIRECTORY结构,这个请看后面的例子中的说明。

当第二个字段的最高位是0的时候,表示,已经没有下一层的结构了。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容会是一个IMAGE_RESOURCE_DATA_ENTRY结构,IMAGE_RESOURCE_DATA_ENTRY结构会说明资源的位置。

标示一个IMAGE_RESOURCE_DIRECTORY_ENTRY一般都是使用id,就是一个整数。

但是也有少数的使用IMAGE_RESOURCE_DIR_STRING来标示一个IMAGE_RESOURCE_DIRECTORY_ENTRY。

IMAGE_RESOURCE_DIRECTORY_ENTRY结构定义如下。

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {

WORD Length;

WCHAR NameString[ 1 ];

} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

这个结构中将有一个Unicode的字符串,是字对齐的。所有这些用来标识的IMAGE_RESOURCE_DIR_STRING都放在一起,这个结构的长度是可变的,由第一个字段Length指明后面的Unicode字符串的长度。

经过3层IMAGE_RESOURCE_DIRECTORY_ENTRY(一般是3层,也有可能更少些。第一层资源类型bmp,menu等等,第二层资源名,第三层是资源的Language。)最终会找到一个IMAGE_RESOURCE_DATA_ENTRY结构,这个结构中存有相应(某资源类型,某资源名,某资源Language)资源的位置和大小,就真正找到资源了。IMAGE_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;

这个结构长16个字节,有4个字段。

OffsetToData:这是一个内存中的RVA,要转化成文件中的位置,需要用这个值减去资源节的开始RVA,

资源节的开始RVA可以由Optional Header中的DataDirectory数组中的第三项中的VirtualAddress的值得到。

或者节表中,资源节那项中的VirtualAddress的值得到。相减之后,就可以得到相对于资源节开始的偏移。

再加上资源节在文件中的开始位置,节表中资源节那项中的PointerToRawData的值,就是资源在文件中的位置。

Size:资源的大小,以字节为单位。

CodePage:一般来说是Unicode code page。

Reserved:保留,值为0。

上面是资源各种结构的说明,知道这些结构还远远不够,下面我们通过一个例子来看如何通过这些结构找到资源。

我们的例子是Win2k中的可执行文件telnet.exe。为了防止大家版本不同,本文附带了这个PE文件。

PE文件的资源的各种结构放在一个树型结构中,这个结构一般有3层,如图4.1,就是telnet.exe中的情况。

图4.1

图中长的长方形表示一个IMAGE_RESOURCE_DIRECTORY结构,长16个字节,简称directory。

图中短的长方形表示一个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,长8个字节,简称directory_entry。

图中圆圈表示一个IMAGE_RESOURCE_DATA_ENTRY结构,长16个字节,简称data_entry。

为了以后的叙述方便还给树的每一个节点起了名字,第一层的叫11,第二层的叫21,22,23,24,第三层的叫31,32,33,34,35,36,37,38,39,310,311,312。

在资源节开始处,是一个directory结构,这个结构中指明了紧跟在它后面的一个directory_entry结构数组中的元素的个数。这个directory结构之后,紧跟着的就是那个directory_entry结构数组。他们一起组成了11。就如图4.1中所示。其他的每个节点,21,22..31,32..312,都是这样,每个 directory结构后面紧跟directory_entry 结构数组。11中的directory_entry结构数组中的每一个元素,都存有到下一层某个节点的偏移。也就是通过directory_entry结构数组的每个元素可以找到21,22,23,24。其他的节点中情况也是一样。图中看不到的一点是,所有的节点之间都是紧紧的挨在一起存放的,11之后紧跟着的是21,21之后紧跟着的是22,22之后紧跟着的是23。依此类推。directory_entry结构数组中的每一个元素除了有到下一层某节点的偏移,(是下一层的节点,还是已经到了最终的data_entry,后面详细叙述)还有一个Name或者Id字段(是Name还是Id后面详细叙述),根据不同的层,代表的含义也不一样。第一层的每个directory_entry的这个值,代表类型。比如11的第一个directory_entry的Id值为3,3代表icon,从这个directory_entry往下的都是都是图标了(关于不同类型值的定义,后面详细叙述)。第二层每个directory_entry的这个值代表Name,第三层代表Language。11,21,31的左边那个data_entry,的三个值分别为3,1,409(都是16进制),就是说是一个图标类型,Name为1h,Language为409h的资源。

下面我们来通过telnet.exe中资源节的具体内容来看,用开始讲到的寻找资源节在文件中位置的方法,我们找到了资源节在文件中的位置为00013600h。

我们为了看起来清楚,每一行是一个结构,并且每个结构的不同成员用 / 分开,例如,

一个directory结构 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 04 00

可以看到结构成员,Characteristics为0,TimeDateStamp为0,MajorVersion为4,(如果你不明白为什么是0004而不是0400的话,请看 《JIURL PE 格式学习总结(一)》中关于 big-endian和little-endian的介绍),MinorVersion为0,NumberOfNamedEntries为0,NumberOfIdEntries为4。

一个directory_entry结构 03 00 00 00 / 30 00 00 80

可以看到结构成员,第一个字段的第一个字节00h的二进制为00000000,最高位为0,所以低两个字节中的值为Id,Id为3。第二个字段的第一个字节80h(如果你不明白为什么第一个字节是80h而不是30h的话,请看 《JIURL PE 格式学习总结(一)》中关于 big-endian和little-endian的介绍)的二进制为10000000,最高位为1 所以说明还有下一层,还没有到叶子,所以第二字段代表到下一层某个节点的偏移 OffsetToData 值为30。

一个data_entry结构 E0 23 03 00 / 30 01 00 00 / E4 04 00 00 / 00 00 00 00

可以看到结构成员,OffsetToData为323E0h(这是一个内存中的RVA,要转化成文件中的位置,需要用这个值减去资源节的开始RVA,资源节的开始RVA可以由Optional Header中的DataDirectory数组中的第三项中的VirtualAddress的值得到。或者节表中,资源节那项中的VirtualAddress的值得到。相减之后,就可以得到相对于资源节开始的偏移。再加上资源节在文件中的开始位置,节表中资源节那项中的PointerToRawData的值,就是资源在文件中的位置。),Size为130h,CodePage为4E4h,Reserved为0。

下面就是telnet.exe中的内容,可以用16进制编辑器打开附带的telnet.exe对照着看。

00013600h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 04 00

(directory结构,16字节长。图4.1中11中的directory。0个NamedEntries,4个IdEntries。)

00013610h: 03 00 00 00 / 30 00 00 80

(directory_entry结构,8字节长。图4.1中11中的directory_entry数组第一个元素。第一个字段高位为0,说明第一个字段表示id,由于是第一层,所以类型id为3。第二个字段高位为1,说明还有下一层,第二字段中的低31位为到图4.1中21的偏移,30+00013600h=00013630h。)

00013618h: 06 00 00 00 / 50 00 00 80

00013620h: 0E 00 00 00 / A0 00 00 80

00013628h: 10 00 00 00 / B8 00 00 80

00013630h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory21)

00013640h: 01 00 00 00 / D0 00 00 80 (d0+00013600h=000136d0h。)

00013648h: 02 00 00 00 / F0 00 00 80

00013650h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 08 00 (directory22)

00013660h: 08 00 00 00 / 10 01 00 80

00013668h: 09 00 00 00 / 30 01 00 80

00013670h: 0C 00 00 00 / 50 01 00 80

00013678h: 0D 00 00 00 / 70 01 00 80

00013680h: 10 00 00 00 / 90 01 00 80

00013688h: 11 00 00 00 / B0 01 00 80

00013690h: 12 00 00 00 / D0 01 00 80

00013698h: 39 00 00 00 / F0 01 00 80

000136a0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 01 00 / 00 00

(directory结构,16字节长。图4.1中23。1个NamedEntries,0个IdEntries。)

000136b0h: D0 03 00 80 / 10 02 00 80

(directory结构中已经表明这是一个NamedEntries,第一个字段中的高位为1,说明第一个字段中的值为一个指向IMAGE_RESOURCE_DIR_STRING结构的偏移,3D0+00013600h=000139D0h。)

000136b8h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 01 00 (directory24)

000136c8h: 01 00 00 00 / 30 02 00 80

000136d0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory31)

000136e0h: 09 04 00 00 / 50 02 00 00

(directory_entry结构,8字节长。第一个字段高位为0,说明第一个字段表示id,由于是第三层,所以Language id为409h。第二个字段高位为0,说明已经是叶子了,第二字段中的低31位为到一个data_entry结构的偏移,250+00013600h=00013850h。)

000136e8h: 04 08 00 00 / 60 02 00 00

000136f0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory32)

00013700h: 09 04 00 00 / 70 02 00 00

00013708h: 04 08 00 00 / 80 02 00 00

00013710h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory33)

00013720h: 09 04 00 00 / 90 02 00 00

00013728h: 04 08 00 00 / A0 02 00 00

00013730h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory34)

00013740h: 09 04 00 00 / B0 02 00 00

00013748h: 04 08 00 00 / C0 02 00 00

00013750h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory35)

00013760h: 09 04 00 00 / D0 02 00 00

00013768h: 04 08 00 00 / E0 02 00 00

00013770h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory36)

00013780h: 09 04 00 00 / F0 02 00 00

00013788h: 04 08 00 00 / 00 03 00 00

00013790h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory37)

000137a0h: 09 04 00 00 / 10 03 00 00

000137a8h: 04 08 00 00 / 20 03 00 00

000137b0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory38)

000137c0h: 09 04 00 00 / 30 03 00 00

000137c8h: 04 08 00 00 / 40 03 00 00

000137d0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory39)

000137e0h: 09 04 00 00 / 50 03 00 00

000137e8h: 04 08 00 00 / 60 03 00 00

000137f0h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory310)

00013800h: 09 04 00 00 / 70 03 00 00

00013808h: 04 08 00 00 / 80 03 00 00

00013810h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory311)

00013820h: 09 04 00 00 / 90 03 00 00

00013828h: 04 08 00 00 / A0 03 00 00

00013830h: 00 00 00 00 / 00 00 00 00 / 04 00 / 00 00 / 00 00 / 02 00 (directory312)

00013840h: 09 04 00 00 / B0 03 00 00

00013848h: 04 08 00 00 / C0 03 00 00

00013850h: E0 23 03 00 / 30 01 00 00 / E4 04 00 00 / 00 00 00 00

(data_entry结构,16字节长,存有一个资源的RVA和大小。资源节开始处的RVA为32000。先算出该资源相对于资源开始处的偏移323E0-32000=3E0h。再用偏移加上资源节开始处的文件偏移13600得到该资源在文件中的位置,3E0+13600=139E0h。)

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系统程式设计大奥秘》附书源码中有。

本文所使用的PE文件 telnet

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有