PE文件格式详解(八)
0x00 前言
前面了解了PE文件的输入和输出,今天来看看另一个重要的结构——资源。资源结构是很典型的树形结构,层层查找,最终找到资源位置。
0x01 资源结构介绍
Windows程序的各种界面成为资源,包括加速键,位图,光标,对话框,图标,菜单,串标,工具栏,版本信息等等,在所有的PE文件中资源结构是最为复杂的。下图为资源的树形结构图:

通常来讲,资源的目录为三层结构。最上面的为根目录,它存储了资源的类型同时存储指向下一级的指针,二级目录包含资源id(至于啥事资源ID后面会讲到)和指向第三级的指针,三级目录存储资源代码和指向真正资源的指针。这里最要理解的是这三级目录用的都是同一结构。这个结构包含两个子结构——资源目录结构(IMAGE_RESOURCE_DIRECTORY)和资源目录入口地址结构(IMAGE_RESOURCE_DIRECTOTY_RNTRY)。理解了这两个结构各自的作用就能明白整个资源结构。下面分别从这两个结构讲起。
0x02 资源目录结构(IMAGE_RESOURCE_DIRECTORY)
资源目录结构是由数据目录表的第三个子项(Resource Table)所指向的(想想数据目标表有多重要把,基本上后面所讲都要从它开始)。该结构包含16个字节,共6个字段,下面的该结构的定义:
// 【资源表位于数据目录表的第三项,共动态分配字节, 其中结构体中的成员指出的RVA偏移量都是对于此结构体的地址作为基地址】
typedef struct _IMAGE_RESOURCE_DIRECTORY
{
DWORD Characteristics; //理论上为资源的属性,不过事实上总是0
DWORD TimeDateStamp; //资源的产生时刻
WORD MajorVersion; //理论上为资源的版本,不过事实上总是0
WORD MinorVersion
WORD NumberOfNamedEntries; //以名称(字符串)命名的入口数量
WORD NumberOfIdEntries; //以ID(整型数字)命名的入口数量
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;
这六个字段真正有用的是最后两个字段:
WORD NumberOfNamedEntries:标明入口数量,所谓入口数量,资源目录入口地址结构(IMAGE_RESOURCE_DIRECTOTY_RNTRY)结构的数量,这个结构都是紧紧跟着资源目录结构(IMAGE_RESOURCE_DIRECTORY),不过它标记数量的方式是用字符串。
WORD NumberOfIdEntries:这个字段本质上和前一个字段一模一样,不过它是用id来标明数量。注意:最后统计资源入口地址结构数量的时候是这两个的相加的和。
0x03 资源目录入口地址结构(IMAGE_RESOURCE_DIRECTORY_ENTRY)
资源目录入口地址结构虽然很简单,但是却非常重要。它不仅包含了指向下一级目录的地址,好包含了指向真正资源的数据入口地址。
typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY
{
DWORD Name; //目录项的名称字符串指针或者ID
DWORD OffsetToData; //资源数据偏移地址或者子目录偏移地址
}_IMAGE_RESOURCE_DIRECTORY_ENTRY, *P_IMAGE_RESOURCE_DIRECTORY_ENTRY;
这两个字段都是极为重要的,下面将详细讲解这两个字段的含义:
Name:大小为双字,该字段定义了目录项的名称或者ID。当用于第一级目录时,它表示的是资源的类型,用于第二级目录时表示资源的名称,用于第三级目录时,用于表示资源的代码页号编号。当最高位是0的时候,表示字段的值作为ID来使用(前面说了该字段可以表示资源的类型,资源的类型就是用ID来区分的,不同资源类型ID不一样)。当最高位是1时,地位字段表示一个指针,这个指针就是指向了资源的名称的UNICODE编码(前面也讲到该字段可以表示目录的名称)。下表统计了系统已经定义的资源类型。表一
|
类型ID值 |
资源类型 |
类型ID值 |
资源类型 |
|
01h |
光标(Cursor) |
08h |
字体(Font) |
|
02h |
位图(Bitmap) |
09h |
加速键(Accelerator) |
|
03h |
图标(Icon) |
0ah |
未格式资源(Unformatted) |
|
04h |
菜单(Menu) |
Obh |
消息表(MessageTable) |
|
05h |
对话框(Dialog) |
0ch |
光标组(Group Cursor) |
|
06h |
字符串(Stiring) |
0eh |
图标组(Group Icon) |
|
07h |
字体目录(Font Directory) |
10h |
版本信息(Version Information) |
OffsetToData:双字结构,是一个指针。当最高为为1时,低位指向的是下一级目录的起始地址,当最高为是0时,指向的是真正的数据资源的目录入口结构(IMAGE_RESOURCR_DIRCTORY_DATA)。
重要说明:当字段Name和Offset作为指针时,该指针是从资源区块开始的地方算起的偏移量,不是RVA,即根目录的起始位置偏移量(就是从第一级资源目录结构(IMAGE_RESOURCE_DIRECTORY)的起始地址算起)
0x03 实例讲解资源的结构
工具:hexworkshop,lordPE,目标PE文件:pediy.exe。
下面我将一级级逐级追击资源:
1)根目录
首先找到数据目录表的第三项,它指向了第一级目录即根目录地址。它在PE文件头偏移88h处。地址为:0ch+88h=148h。直接跳转至148h处,如下图:

标明RVA=4000h,这里本来如果要跳转至根目录是要进行地址转化,但是这里是特殊情况,由于这个PE文件磁盘分页和内存分页的值相等,故RVA=Offset,缘由可看下图:

我们现在直接跳转至4000h处,该处即为根目录起始地址,如下图:

现在我们按照顺序以西读出这个第一个结构IMAGE_RESOURCE_DIRECTORY的六个字段的值。记在下表中:表二
|
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
|
|
0000 0000 |
0000 0000 |
0000 |
0000 |
0000 |
0003 |
由最后两个字段可知紧随IMAGE_RESOURCE_DIRECTORY的共有三个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,如下图:

依次将数据读出登记在下表:表三
|
第一个Dir_entry结构 |
第二个Dir_entry结构 |
第三个Dir_entry结构 |
|
|
偏移地址 |
4010h |
4018h |
4020h |
|
字段Name |
0000 0003h |
0000 0004h |
0000 000Eh |
|
字段OffsetToData |
8000 0028h |
8000 0040h |
8000 0058h |
由于有三个下级目录,我们就以第二个IMAGE_RESOURCE_DIRECTORY_ENTRY结构来分析下一级资源结构。
2)二级目录
根目录的IMAGE_RESOURCE_DIRECTORY_ENTRY结构的字段Name=0000 0004h,最高为0,所以它表示资源类型,低位值为0004h,这个资源的ID号,查询表一可得这是个菜单(Mune资源)。再来看字段OffsetToData=8000 0040h,明显最高为是1,所以这个指针指向的是下一级资源目录。其低31位值是40h,这个就是下级目录距离根目录的偏移地址,地址为:
4000h+40h=4040h。我们跳往地址4040h处,得到下图:

依次读出IMAGE_RESOURCE_DIRECTORY结构的六个字段统计在下表:
|
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
|
0000 0000 |
0000 0000 |
0000 |
0000 |
0000 |
0000 0001 |
根据最后一个字段可知,紧跟这结构IMAGE_RESOURCE_DIRECTORY的结构IMAGE_RESOURCE_DIRECTORY_ENTRY只有一个,如下图:

我们依次读出两个字段的值:Name=8000 00E8h OffsetToData=8000 0088h。由于字段Name的最高位是1,所以低位字段表示指向结构IMAGE_RESOURCE_DATA_STRING_U结构,这个结构存储了资源名的unicod值,我们跳往40E8h处,如下图:

由上图可知资源名是PEDIY。第二个字段OffsetToData的最高位是1,低位表示指向下级目录的地址,我们跳往4088h。
3)第三级目录
跳转至地址4088h,我们来到了第三级目录,如下图:

依次读出六个字段统计在下表:
|
Charateristics |
TimeStamp |
MajorVersion |
MinnorVersion |
NumberOfEntris |
NumberOfIdEntries |
|
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0000 |
0000 0001 |
根据最后一个字段可知紧随结构IMAGE_RESOURCE_DIRECTORY只有一个IMAGE_RESOURCE_DIRECTORY_ENTRY结构,如下图:

字段Name=0000 0409h,字段OffsetToData=0000 00C8h。在第三级目录中Name字段表示代码页编号,这里是409表示英语,OffsetToData最高位是0,低31位作为指针指向资源数据入口结构(IMAGE_RESOURCE_DATA_ENTRY)。该结构的定义如下:
IMAGE_RESOURCE_DATA_ENTRY STRUCT
{
OffsetToData DWORD //资源数据的RVA
Size DWORD//资源数据的长度
CodePage DWORD//代码页,一般为0
Reserved DWORD//保留字段
};IMAGE_RESOURCE_DATA_ENTRY ENDS
我们直接跳转至40c8h处,如下图:

逐个读出各个字段的值统计在下表中:
|
OffsetToData |
Size |
CodePage |
Reserved |
|
0000 4400 |
0000 005a |
0000 0000 |
0000 0000 |
至此,我们总算找到了资源的真正地址即在字段OffsetToData=4400h,大小为字段Size=5ah。
如下图就是资源:

PE文件格式详解(八)的更多相关文章
- PE文件格式详解(七)
PE文件格式详解(七) Ox00 前言 前面好几篇在讲输入表,今天要讲的是输出表和地址的是地址重定位.有了前面的基础,其实对于怎么找输出表地址重定位的表已经非常熟悉了. 0x01 输出表结构 ...
- PE文件格式详解,第一讲,DOS头文件格式
PE文件格式详解,第一讲,DOS头文件格式 今天讲解PE文件格式的DOS头文件格式 首先我们要理解,什么是文件格式,我们常说的EXE可执行程序,就是一个文件格式,那么我们要了解它里面到底存了什么内容 ...
- PE文件格式详解,第二讲,NT头文件格式,以及文件头格式
PE文件格式详解,第二讲,NT头文件格式,以及文件头格式 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) PS:本篇博客 ...
- PE文件格式详解,第三讲,可选头文件格式,以及节表
PE文件格式详解,第三讲,可选头文件格式,以及节表 作者:IBinary出处:http://www.cnblogs.com/iBinary/版权所有,欢迎保留原文链接进行转载:) 一丶可选头结构以及作 ...
- PE文件格式详解(六)
0x00 前言 前面两篇讲到了输出表的内容以及涉及如何在hexWorkShop中找到输出表及输入DLL,感觉有几个地方还是没有理解好,比如由数据目录表DataDirectory[16]找到输出表表后以 ...
- PE文件格式详解(下)
作者:MSDN译者:李马 预定义段 一个Windows NT的应用程序典型地拥有9个预定义段,它们是.text..bss..rdata..data..rsrc..edata..idata..pdata ...
- PE文件格式详解(上)
作者:MSDN 译者:李马 摘要 Windows NT 3.1引入了一种名为PE文件格式的新可执行文件格式.PE文件格式的规范包含在了MSDN的CD中(Specs and Strategy, Spec ...
- PE 文件格式详解
PE文件 是微软 Win32 环境下可执行文件的标准格式. 所谓的可执行文件并不仅仅是常见的 EXE 文件,DLL,SYS,VXD 等文件也都属于 PE 格式. |-------> DOS_MZ ...
- PE文件格式详解(一)
PE文件格式介绍(一) 0x00 前言 PE文件是portable File Format(可移植文件)的简写,我们比较熟悉的DLL和exe文件都是PE文件.了解PE文件格式有助于加深对操作系统的理解 ...
随机推荐
- STL中常用算法
一.排序 sort sort(first_pointer,first_pointer+n,cmp) 默认为升序 若要使用降序,自行写cmp 函数 bool cmp(int a,int b){ retu ...
- Java创建ES索引实现
1.pom.xml文件 <dependency> <groupId>org.springframework.boot</groupId> <artifactI ...
- k8s学习-Ingress
4.5.Ingress Ingress-Nginx github 地址:https://github.com/kubernetes/ingress-nginxIngress-Nginx 官方网站:ht ...
- PAT 1041 Be Unique (20分)利用数组找出只出现一次的数字
题目 Being unique is so important to people on Mars that even their lottery is designed in a unique wa ...
- AWS—Xshell工具远程登录EC2虚拟机
一.创建AWS账户(已有AWS账号的请跳过) 访问 https://amazonaws-china.com/cn/,点击创建AWS账户,根据提示填写个人相关信息,创建AWS账户. 二.创建EC2虚拟机 ...
- MyISAM 和 InnoDB 索引结构及其实现原理
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询.更新数据库表中数据. 索引的实现通常使用B_TREE. B_TREE索引加速了数据访问,因为存储引擎不会再去扫描整张表得到需要的数据; ...
- 商城08——activeMQ 使用消息队列同步索引库
1. 课程计划 1.什么是MQ 2.MQ的应用场景 3.ActiveMQ的使用方法. 4.使用消息队列实现商品同步. 2. 同步索引库分析 方案一:在taotao-manager中,添加商品的业务 ...
- cb01a_c++_数据结构_顺序容器_STL_deque类
/*cb01a_c++_数据结构_顺序容器_STL_deque类deque是一个动态数组,比vector更加灵活.两者都属于动态数组deque与vector非常类似deque可以在数组开头和末尾插入和 ...
- Newtonsoft 六个超简单又实用的特性,值得一试 【下篇】
一:讲故事 上一篇介绍的 6 个特性从园子里的反馈来看效果不错,那这一篇就再带来 6 个特性同大家一起欣赏. 二:特性分析 1. 像弱类型语言一样解析 json 大家都知道弱类型的语言有很多,如: n ...
- 黎活明8天快速掌握android视频教程--21_监听ContentProvider中数据的变化
采用ContentProvider除了可以让其他应用访问当前的app的数据之外,还有可以实现当app的数据发送变化的时候,通知注册了数据变化通知的调用者 其他所有的代码都和第20讲的一样,不同的地方看 ...