首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

2.5  PE文件中的资源

一些PE格式(Portable Executable)的EXE文件常常存在很多资源,如图标、位图、对话框、声音等。若要把这些资源取出为自己所用,或修改这些文件中的资源,则需要对PE文件中资源数据结构有所了解。

2.5.1  查找资源在文件中的起始位置

要找出一个PE文件中的某种资源,首先需要确定资源节在PE文件中的起始位置。有两种方法来确定资源在文件中的起始位置。

第一种方法,首先根据FileHeader中的成员NumberOfSections的值,确定文件中节的数目,再根据节的数目,遍历节表数组。也就是从0到(节表数–1)的每一个节表项。比较每一个节表项的Name字段,看看是否等于“.rsrc”,如果是,就找到了资源节的节表项。这个节表项的PointerToRawData 中的值,就是资源节在文件中的位置。

第二种方法,取得PE Header中的IMAGE_OPTIONAL_HEADER中的DataDirectory数组中的第三项,也就是资源项。DataDirectory[]数组的每项都是IMAGE_DATA_ DIRECTORY结构,该结构定义如下:

l           

typedef struct _IMAGE_DATA_DIRECTORY {

    DWORD VirtualAddress;

    DWORD Size;

} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

l           

从以上结构对象取得DataDirectory数组中的第三项中的成员VirtualAddress的值。这个值就是在内存中资源节的RVA。然后根据节的数目,遍历节表数组,也就是从0~(节表数–1)的每一个节表项。每个节在内存中的RVA的范围是从该节表项的成员VirtualAddress字段的值开始(包括这个值),到VirtualAddress+Misc.VirtualSize的值结束(不包括这个值)。遍历整个节表,看看所取得的资源节的RVA是否在那个节表项的RVA范围之内。如果在范围之内,就找到了资源节的节表项。这个节表项中的PointerToRawData 中的值,就是资源节在文件中的位置。如果这个PE文件没有资源   的话,DataDirectory数组中的第三项内容为0。这样也可以得到了资源在文件中开始的位置。

2.5.2  确定PE文件中的资源

得到了资源节在文件中的位置后,就可以确定某个资源类型及其二进制数据在PE文件中的位置和数据块的大小。

资源节最开始是一个IMAGE_RESOURCE_DIRECTORY结构,在winnt.h文件中有这个结构的定义。这个结构长度为16字节,共有6个参数,其结构的原型如下:

l           

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;

l           

其中各个参数的含义如下所述

Characteristics: 标识此资源的类型。

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

MajorVersion:资源主版本号。

MinorVersion:资源次版本号。

NumberOfNamedEntries和NumberofIDEntries:分别为用字符串和整形数字来进行标识的IMAGE_RESOURCE_DIRECTORY_ENTRY项数组的成员个数。

紧跟着IMAGE_RESOURCE_DIRECTORY后面的是一个IMAGE_RESOURCE_ DIRECTORY_ENTRY数组。这个结构长度为8个字节,共有两个字段,每个字段4个字节。其结构原型如下:

l           

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;

l           

其中,对于第一个字段,当其最高位为1(0x80000000)时,这个DWORD剩下的31位表明相对于资源开始位置的偏移,偏移的内容是一个IMAGE_RESOURCE_DIR_ STRING_U,用其中的字符串来标明这个资源类型;当第一个字段的最高位为0时,表示这个DWORD的低WORD中的值作为Id标明这个资源类型。

对于第二个字段,当第二个字段的最高位为1时,表示还有下一层的结构。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容将是一个下一层的IMAGE_RESOURCE_DIRECTORY结构;当第二个字段的最高位为0时,表示已经没有下一层的结构了。这个DWORD的剩下31位表明一个相对于资源开始位置的偏移,这个偏移的内容会是一个IMAGE_RESOURCE_DATA _ENTRY结构,此结构会说明资源的位置。对于资源标示号Id,当Id等于1时,表示资源为光标,等于2时表示资源为位图等,等于3时表示资源为图标等。在winuser.h文件中有定义。

标识一个IMAGE_RESOURCE_DIRECTORY_ENTRY一般都是使用Id,就是一个整数。但是也有少数使用IMAGE_RESOURCE_DIR_STRING_U来标识一个资源类型。这个结构定义如下:

l           

typedef struct _IMAGE_RESOURCE_DIR_STRING_U {

    WORD Length;

    WCHAR NameString[1];

} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

l           

这个结构中将有一个Unicode的字符串,是字对齐的。这个结构的长度可变,由第一个字段Length指明后面的Unicode字符串的长度。

经过3层IMAGE_RESOURCE_DIRECTORY_ENTRY(一般是3层,也有可能更少些)最终可以找到一个IMAGE_RESOURCE_DATA_ENTRY结构,这个结构中存有相应资源的位置和大小。这个结构长16个字节,有4个参数,其原型如下:

l           

typedef struct _IMAGE_RESOURCE_DATA_ENTRY {

    DWORD OffsetToData;

    DWORD Size;

    DWORD CodePage;

    DWORD Reserved;

} IMAGE_RESOURCE_DATA_ENTRY, *PIMAGE_RESOURCE_DATA_ENTRY;

l           

其中各个参数的含义如下所述。

OffsetToData:这是一个内存中的RVA,可以用来转化成文件中的位置。用这个值减去资源节的开始RVA,就可以得到相对于资源节开始的偏移。再加上资源节在文件中的开始位置,即节表中资源节中PointerToRawData的值,就是资源在文件中的位置。注意,资源节的开始RVA可以由Optional Header中的DataDirectory数组中的第三项中的VirtualAddress的值得到,或者节表中资源节那项中的VirtualAddress的值得到。

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

CodePage:代码页。

Reserved:保留项。

总之,资源一般使用树来保存,通常包含3层,最高层是类型,其次是名字,最后是语言。在资源节开始的位置,首先是一个IMAGE_RESOURCE_DIRECTORY结构,后面紧跟着IMAGE_RESOURCE_DIRECTORY_ENTRY数组,这个数组的每个元素代表的资源类型不同;通过每个元素,可以找到第二层另一个IMAGE_RESOURCE_ DIRECTORY,后面紧跟着IMAGE_RESOURCE_DIRECTORY_ENTRY数组。这一层的数组的每个元素代表的资源名字不同;然后可以找到第三层的每个IMAGE_ RESOURCE_DIRECTORY,后面紧跟着IMAGE_RESOURCE_DIRECTORY_ENTRY数组。这一层的数组的每个元素代表的资源语言不同;最后通过每个IMAGE_RESOURCE_ DIRECTORY_ENTRY可以找到每个IMAGE_RESOURCE_DATA_ENTRY。通过每个IMAGE_RESOURCE_DATA_ENTRY,就可以找到每个真正的资源。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论