WINDOWS+PE权威指南读书笔记(13)

目录

资源表

菜单资源解析:

图标资源解析:

图标组资源解析:

对话框资源解析:

资源表编程:

更改图标实验:

提取程序图标实例:

更改程序图标实例:

总结:


资源表

菜单资源解析:

以下内容以 PE.exe 为例,首先来看对菜单资源的解析。

首先回顾一下前面菜单目录的菜单的汇编语言语法:

菜单项的语法格式如下:

菜单资源定位:

从 PEInfo 中查看到菜单资源的位置和大小分别是:

口文件位置: 0x00001018

口菜单项大小: 134 字节

菜单资源数据提取:

使用PEDump 将该位置的数据提取出来,字节码显示如下;

以上字节码对应以下定义:

继续看下面的字节码:

以上字节码对应以下定义:

菜单资源数据结构:(PE文件中的)

菜单资源由一个菜单头加一个菜单项的序列组成。菜单项有两种 : 弹出式 (POPUP) 菜单项和普通菜单项:

菜单头结构定义如下:

菜单头后面紧跟着菜单项,不同的菜单项具有不同数据结构的定义。一般菜单项数据结构完整定义如下:

其中,菜单分隔符 MENUITEM SEPARATE 也是普通菜单项,不过,其szItemText名称为空,wMenuID 为 0,fItemFlags标志也为 0。

ftemFlags 是描述菜单项的标志集合。如果 POPUP 位被设置,则此项为弹出式的菜单项,否则就是一个普通的菜单项。

常见标志及对应的值见表 7-3:

弹出式菜单项数据结构定义如下:

菜单资源PE的字节码解析:(按顺序来,注意菜单项标志的含义)

第一部分:

>>00 00 00 00

菜单头数据结构版本号和头大小。两个字段的值均为 0x00。

>>10 00

>> 87 65 F6 4E 28 00 26 00 46 00 29 00 00 00 //(一个字的0,所以这里是结尾)

第一行PopupMenuItem.fftemFlags=0x0010(小端反序),表示这是一个弹出式菜单项,没有ID。

第二行为 Unicode 字符串,以一个字的 0 结尾,内容是“文件 (&F)”。

>>00 00 D1 07

>>53 62 00 5F 87 65 F6 4E 28 00 26 00 4F 00 29 00 2E 00 2E 00 2E 00 00 00 //(一个字的0,所以这里是结尾)

第一行说明是普通菜单项。标志为 0x0000,ID为 0x07d1,十进制为 2001。

第二行为 Unicode 字符串,以一个字的 0 结尾,内容是“打开文件 (&O)…”。

>>00 00 00 00 00 00

这是一个菜单分隔符,即“menuitem separate ”。

>>80 00 D2 07

>>00 90 FA 51 28 00 26 00 78 00 29 00 00 00 //(一个字的0,所以这里是结尾)

普通菜单项,第一行的标志 0x0080 说明这个菜单项是弹出菜单的最后一个。0x07D2 是菜单的ID。

第二行的 Unicode 字符,内容是“退出 (&x)”。

第二部分:

>>90 00 E5 67

>>0B 77 00 00

第一行的标志 0x0090 虽然不明觉厉,但是应该是普通菜单项,0x67E5 是菜单的ID。

第二行的 Unicode 字符。

>>00 00 A0 0F

>>90 6E 87 65 F6 4E 00 00 //(一个字的0,所以这里是结尾)

第一行说明是普通菜单项。标志为 0x0000,ID为 0x0FA0,十进制为 4000。

第二行为 Unicode 字符串,以一个字的 0 结尾。

>>00 00 A1 0F

>>97 7A E3 53 0F 90 0E 66 A6 5E 00 00 //(一个字的0,所以这里是结尾)

第一行说明是普通菜单项。标志为 0x0000,ID为 0x0FA1,十进制为 4001。

第二行为 Unicode 字符串,以一个字的 0 结尾。

>>00 00 00 00 00 00

这是一个菜单分隔符(多个0排列的都可以认为是菜单分隔符),即“menuitem separate ”。

>>00 00 A2 0F

>>27 59 0F 5C 00 00

第一行说明是普通菜单项。标志为 0x0000,ID为 0x0FA2,十进制为 4002。

第二行为 Unicode 字符串,以一个字的 0 结尾。

>>80 00 A3 0F

>>BD 5B A6 5E 00 00

普通菜单项,第一行的标志 0x0080 说明这个菜单项是弹出菜单的最后一个。0x0FA3 是菜单的ID。

第二行的 Unicode 字符,内容是“退出 (&x)”。

图标资源解析:

图标有一套标准的大小和属性格式,且通常是小尺寸的。以 ICO 文件为例,一个ICO 文件就是一套相似的图片,每一张图片具有不同的尺寸和颜色数,目的是适应不同的计算机操作系统和显示设备。

操作系统在显示一个图标时,会按照一定的标准选择图标中最适合当前显示环境和状态的图像。如果使用 Windows 98 操作系统,其显示环境是 800X600 分辩率,32 位色深,你在桌面上看到的每个图标的图像格式就是 256 色、32X32 像素大小。

如果在相同的显示环境下,这些图标在Windows XP 操作系统中的图像格式就是:真彩色 (32 位色深))32X32像素大小。

扩展阅读 Windows 各个操作系统中的标准图标格式(单位: 大小像素一颜色)

注意,*这种格式在 Windows XP 图标中并不是必需的。在 Vista 系统下最大可以支持256X256; 同时非标准的ICO 文件也支持不规则尺寸的存储。

要想保证良好的显示效果,必须确保你所设计的图标中至少含有以上所列的图像格式。如果操作系统在图标中找不到特定的图像格式,它总是采用最接近的图像格式来显示,比如把大小为48X48 的图标缩小为 24X24 像素大小,当然,效果就差些了。如果我们开发的软件同时支持 Windows XP 和 Windows 2000,那么为了达到视觉上的最佳效果,每一个 ICO文件应至少包含两个图标,一个是 32 位色的,一个是 256 色的。

ICO 文件结构:(注意这里不是在PE文件中)

每个ICO 文件的开始都有一个头部信息。该头部信息描述了 ICO 文件中存在多少个图标,以及每个图标的基本信息。

头部信息的结构如下:

其中 idcount 表示该 ICO 文件包含图标的数量。理论上一个ICO 文件最多可以包含 65535 个图标。

接下来,是ICO文件的图标资源结构:

ICON_DIR_ENTRY 结构记录了每一个图标的尺寸、色深、图标资源占用的字节数。其中 dwImageOffset 属性是一个文件偏移地址,指向图标资源数据起始位置。

PE 文件中的图标保存格式与“.ico”文件中图标的保存格式略有不同。PE 文件中,把 ICON_DIR 和图标资源作为两

种资源类型分别保存,前者是 RT_GROUP_ICON 类型,后者是 RIT_ICON 类型。

在 .ico 文件中,ICON_DIR_ENTRY 结构最后一个成员 dwImageOffset 表示图标资源文件偏移地址,是一个双字;

而在 PE 文件中,GRP_ICON_DIR_ENTRY 结构最后一个成员 nID 是一个字,表示图标的索引 ID 。

图 7-6 是 ICO 文件大致结构图:(其中嵌入了PE文件中有关联的属性)

如图 7-6 所示,ICO 文件分为三部分 : 图标头、图标项和图标数据。这三部分在 PE 文件中会被重新组合,其中图标头会被重新组合为资源类型 RT_GROUP_ICON。图标项和图标数据则被组合为资源类型 RT_ICON。三部分间关系如图所示,图标头的 idcount 字段定义 ICO里图标的个数,每个图标的属性由图标项定义; 图标项的字段 dwImageOffset 则指向了 ICO 文件中该图标数据的位置。

图标资源定位:

从 PEInfo 中查到 PE.exe 程序的图标资源的位置和大小分别是:

图标资源数据提取:

使用PEDump 将该位置的数据提取出来,字节码显示如下:

以上所列字节码是从 PE.exe 文件中提取的图标数据的一部分,这部分数据的资源类型被定义为RT_ICON。

注意:这些字节码并不包含图7-6 中的图标头和图标项两部分。如前所述,以上数据只是 main.ico 的一部分。

main.ico 数据:

为了将 PE 中的图标资源数据与原始的 main.ico 文件内容进行比对,以下获取了 main.ico文件的部分字节码:

黑体部分即为资源表中的图标数据。可以看出,PE 中的图标数据是直接复制 ico 文件内容入 pe (b60处)文件中去。

main.ico文件字节码解析:

首先回顾一下ICO文件的图标头和图标项的定义:

main.ico 文件开头的 38 字节为 ICO 头(图 7-6 中图标头和图标项两部分),其内容如下:

(1) 图标头

>>00 00 保留,必须为 0。

>>01 00 资源类别,表示该格式符合 ICO 文件格式。

>>02 00 ICO 中图标的个数 ,0002h 为两个。

(2) 第一个图标项定义

>>20 宽 32。

>>20 高 32。

>>10 颜色数 16 位。

>>00 保留。

>>00 00 调色板 。

>>00 00 RGB 每字节的位。

>>E8 02 00 00 该图标大小 744 字节,与从 PE 资源里取出的数是相等的。

>>26 00 00 00 图标在文件中的偏移为 0x00000026 。

(3) 第二个图标项定义

>>10 宽 16。

>>10 高 16。

>>10 颜色数 16 位。

>>00 保留。

>>00 00 调色板。

>>00 00 RGB 每字节的位。

>>28 01 00 00 该图标数据大小为 296 字节。

>>0E 03 00 00 图标在文件中的偏移为 0x0000030E。

图标组资源解析:

前面“pe.rc”脚本文件里只定义了三种资源,在 PEInfo 中却显示四种资源(多了一个图标组) , 前面我们只用了一个图标 main.ico 文件,在 PEInfo 中却显示有两个图标资源(就是图标项目)。

PE 里的图标组资源 RT_GROUP_ICON 的数据来源于 ICO 文件的头部,但与 ICO 文件头部内容并不完全一致。它记录了某个图标具有相似性的一系列图片数据的基本信息,这些基本信息包括 : 相似图片数量及每个图片的宽、高、颜色数、在图标资源(RT_ICON)里的编号等。如果没有图标组资源,系统就无法识别在图标资源里哪些编号的图片数据是属于一个系列的(即相似图片)。

图标组资源定位:

从PEInfo 中可以定位到 PE.exe 文件中图标组的地址和大小,图标组其实就是 ICO 文件的文件头描述。只是其中一个字段的解释和值略有不同而已。

口 在文件中的偏移地址: 0x00000F70

口 图标组资源大小: 34 字节

图标组资源数据提取:(第一个是PE文件,第二个是ICO文件)

以下是pe.exe文件中对应的值:

与 ICO 文件头比较发现,每一个图标项除了最后一个字段的值不一样外,其他基本(中间有些许不一样不用管)就是上一节字节码解析部分解释过的图标头 + 图标项的数据。粗体部分数据为图标的 ID 标识。

(1) 图标头

>>00 00 保留,必须为 0。

>>01 00 资源类别,表示该格式符合 ICO 文件格式。

>>02 00 ICO 中图标的个数 ,0002h 为两个。

(2) 第一个图标项定义

>>20 宽 32。

>>20 高 32。

>>10 颜色数 16 位。

>>00 保留。

>>01 00 调色板 。

>>04 00 RGB 每字节的位。

>>E8 02 00 00 该图标大小 744 字节,与从 PE 资源里取出的数是相等的。

>>01 00 图标在文件中的索引为 0x0001 。

(3) 第二个图标项定义

>>10 宽 16。

>>10 高 16。

>>10 颜色数 16 位。

>>00 保留。

>>01 00 调色板。

>>04 00 RGB 每字节的位。

>>28 01 00 00 该图标数据大小为 296 字节。

>>02 00 图标在文件中的索引为 0x0002。

以下是文件 main.ico 中对应的值:

可以看到,在 PE 资源表中图标的 ID 标识值(一个字) 到了文件中就变成了两个字,其含义也更改为对应图标数据的偏移地址。

(这也是我们前面提到过的在 .ico 文件中,ICON_DIR_ENTRY 结构最后一个成员 dwImageOffset 表示图标资源文件偏移地址,是一个双字; 而在 PE 文件中,GRP_ICON_DIR_ENTRY 结构最后一个成员 nID 是一个字,表示图标的索引 ID 。)

(1) 图标头

>>00 00 保留,必须为 0。

>>01 00 资源类别,表示该格式符合 ICO 文件格式。

>>02 00 ICO 中图标的个数 ,0002h 为两个。

(2) 第一个图标项定义

>>20 宽 32。

>>20 高 32。

>>10 颜色数 16 位。

>>00 保留。

>>00 00 调色板 。

>>00 00 RGB 每字节的位。

>>E8 02 00 00 该图标大小 744 字节,与从 PE 资源里取出的数是相等的。

>>26 00 00 00 图标在文件中的偏移为 0x00000026 。

(3) 第二个图标项定义

>>10 宽 16。

>>10 高 16。

>>10 颜色数 16 位。

>>00 保留。

>>00 00 调色板。

>>00 00 RGB 每字节的位。

>>28 01 00 00 该图标数据大小为 296 字节。

>>0E 03 00 00 图标在文件中的偏移为 0x0000030E。

对话框资源解析:

PE.EXE对话框资源解析:

根据 PEInfo 分析,获取 PE.exe 对话框资源所在文件位置和大小如下:

口 在文件偏移地址: 0x00000f98

口 对话框资源大小: 122 字节

PE.exe的资源脚本文件中有关对话框定义的代码部分:(存在与pe.rc文件中)

对话框资源提取:

使用PEDump 获取字节码,内容如下:

对话框资源数据结构:

对话框的头是一个 DialogBoxHeader 结构,其详细定义如下:

其中 lStyle 属性项是一个标准窗口样式,由 windows.inc 文件中的标志组成。对话框的默认样式为:

其中 lExtendedStyle 属性项用来指定扩展窗口样式:

当在 DIALOG 语句或其他可设置的语句中指定了扩展样式,那么它们的值就会被存储在这个双字字段中。菜单名和类名存储一个名称或一个序数 ID ; 若第一个字为 0xffff,则第二个字就是一个序数 ID ,如果第一个字为 0x0000,则表示一个空字符串。

wPointSize 和 szFontName 项仅当对话框中包含FONT 语句时才会设置:

可以通过检查 lStyle 项确定对话框是否包含字体设置。若 lStyle & DS_SETFONT(DS_SETFONT = 0x40) 为真,则这两项就会被设置。

对话框资源中NumberofItems对应的控件的样式:

注意:每个控件都由一个数据结构开始,每个控件的数据开始于一个双字,同样以双字对齐的方式结束,所以两个控件中间可能会存在填充用的数据。

以下是每个控件开始的数据结构 ControlData 的定义:

与前面一样,lStyle 属性项是一个标准窗口样式:

由 windows.h 文件中的标志组成。控件的类型由 class 指定。为节省空间、加速处理,许多通用 Windows 类型都以一个单字表示。由于 Unicode 中 0x8000 是一个合法字符,类型序数必须前置 0xffff,和前面讲到的 Type 和Name 域的序数表示方式相似。

常用 classID 的值列表如下:

IExtendedStyle 双字属性用来指定此控件的扩展样式:

扩展样式标志置于 CONTROL 语句的最后,跟在坐标后面。控件数据最后的附加信息当前并不使用,但将来可能会被用来存储菜单项信息,通常它的长度为0。对话框脚本中使用的绝大多数语句都被映射为这些类型(包括它们的样式),这些样式的值可以在 windows.h 中找到。所有对话框控件都有默认的 WS_CHILD 和 WS_VISIBLE 样式。

表 7-4 为脚本语句使用的默认样式:

控件文本存储在上面介绍的“名称或序数”字段中。

字节码解析:

第一部分: 对话框定义的控件二进制字节码分析如下:

>>C0 00 C8 90 标准窗口样式。

对应脚本文件定义中的语句:

STYLE DS_MODRALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU

>>00 00 00 00 扩展窗口样式。

>>01 00 表示该对话框中只有一个控件。

>>32 00 32 00 20 02 8F 01 对话框的坐标 : 起点 (50,50)。长 544,高 399。

对应资源脚本文件定义中的语句:

DLG_MAIN DIRLOG 50,50,544,399

>>FF FF D0 07 窗口菜单编号为 2000。

对应资源脚本文件定义中的语句:

MENU IDM_MAIN

>>00 00 对话框类为空 。

>>50 00 45 00 87 65 F6 4E FA 57 2C 67 E1 4F 6F 60

>>20 00 62 00 79 00 20 00 71 00 69 00 78 00 69 00

>>61 00 6F 00 72 00 75 00 69 00 00 00

对话框标题栏文字为“PE 文件基本信息 by qixiaorui”。

对应资源脚本文件定义中的语句:

CRPTION "PE 文件基本信息 by qixiaoruin"

>>09 00 8B 5B 53 4F 00 00 第一个字 0x0009 表示字体大小,后面紧跟着的是字体的名称。

该二进制代码对应资源脚本文件定义中的语句:

FONT 9,"宋体"

第二部分,控件定义:

>>C4 18 A1 50 控件基本样式定义。

对应资源脚本文件中的以下部分:

>>00 00 00 00 扩展样式,为 0。

>>00 00 00 00 1C 02 8C 01 控件相对于对话框的坐标,起点 (0,0),宽度 540,高度396。

对应资源脚本文件中的以下部分:

0,0,540,396

>>E9 03 控件的ID 号为 1001。

对应资源脚本文件中的以下部分:

IDC_INFO

>>52 00 69 00 63 00 68 00 45 00

>>64 00 69 00 74 00 32 00 30 00 41 00 00 00

类名。Unicode 字符串。控件的名称 "RichEdit20A"。

对应资源脚本文件中的以下部分:

"RichEdit20A"

>>00 00 字段 text 的值。

>>00 00 字段 nExtraStuff 的值。暂时为0。

PE2.EXE对话框资源解析:

参照书本标注:

因为 PE.exe 对话框资源中只存在一个控件,相对简单。现在分析一下 PE2.exe 中的对话框资源。在分析时请注意资源的双字对齐补足部分。

PE2 的资源脚本文件中有关对话框定义的部分如下:(在pe2.rc资源文件中)

字节码解析:

第一部分: 对话框定义的控件二进制字节码分析如下:

>>C0 00 C8 90 标准窗口样式。

对应脚本文件定义中的语句:

STYLE DS_MODRALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU

>>00 00 00 00 扩展窗口样式。

>>04 00 表示该对话框中有4个控件。

>>32 00 32 00 20 02 8F 01 对话框的坐标 : 起点 (50,50)。长 544,高 399。

对应资源脚本文件定义中的语句:

DLG_MAIN DIRLOG 50,50,544,399

>>FF FF D0 07 窗口菜单编号为 2000。

对应资源脚本文件定义中的语句:

MENU IDM_MAIN

>>00 00 对话框类为空 。

>>50 00 45 00 87 65 F6 4E FA 57 2C 67 E1 4F 6F 60

>>20 00 62 00 79 00 20 00 71 00 69 00 78 00 69 00

>>61 00 6F 00 72 00 75 00 69 00 00 00

对话框标题栏文字为“PE 文件基本信息 by qixiaorui”。

对应资源脚本文件定义中的语句:

CRPTION "PE 文件基本信息 by qixiaoruin"

>>09 00 8B 5B 53 4F 00 00 第一个字 0x0009 表示字体大小,后面紧跟着的是字体的名称。

该二进制代码对应资源脚本文件定义中的语句:

FONT 9,"宋体"

第二部分之控件1的定义:

>>C4 18 A1 50 控件基本样式定义。

对应资源脚本文件中的以下部分:

>>00 00 00 00 扩展样式,为 0。

>>00 00 00 00 1C 02 8C 01 控件相对于对话框的坐标,起点 (0,0),宽度 540,高度396。

对应资源脚本文件中的以下部分:

0,0,540,396

>>E9 03 控件的ID 号为 1001。

对应资源脚本文件中的以下部分:

#define IDC_INFO 1001中的 IDC_INFO

>>52 00 69 00 63 00 68 00 45 00

>>64 00 69 00 74 00 32 00 30 00 41 00 00 00

类名。Unicode 字符串。控件的名称 "RichEdit20A"。

对应资源脚本文件中的以下部分:

"RichEdit20A"

>>00 00 字段 text 的值。

>>00 00 字段 nExtraStuff 的值。暂时为0。

>>00 00 最后为了双字对齐而补足的0x00,对齐后就是以16进制的4结尾了

第二部分之控件2的定义:

>>00 00 02 50 控件基本样式定义。

对应资源脚本文件中的以下部分:

>>00 00 00 00 扩展样式,为 0。

>>0A 00 0D 00 C8 00 08 00 控件相对于对话框的坐标,起点 (10,13),宽度 200,高度8。

对应资源脚本文件中的以下部分:

LTEXT "请选择要执行的文件:",-1,10,13,200,8 中的 10,13,200,8

>>FF FF 控件的ID 号为 -1。

对应资源脚本文件中的以下部分:

LTEXT "请选择要执行的文件:",-1,10,13,200,8 中的 -1 部分

>>FF FF 82 00 F7 8B 09 90 E9 62

>>81 89 67 62 4C 88 84 76 87 65 F6 4E 1A FF

类名。Unicode 字符串。控件的名称 ?????

对应资源脚本文件中的以下部分:

?????

>>00 00 字段 text 的值。

>>00 00 字段 nExtraStuff 的值。暂时为0。

>>00 00 最后为了双字对齐而补足的0x00,对齐后就是以16进制的0结尾了

第二部分之控件3的定义:

>>00 00 81 50 控件基本样式定义。

对应资源脚本文件中的以下部分:

>>00 00 00 00 扩展样式,为 0。

>>5A 00 0A 00 2C 01 0E 00 控件相对于对话框的坐标,起点 (90,10),宽度 300,高度14。

对应资源脚本文件中的以下部分:

EDITTEXT ID_TEXT,90,10,300,14 中 90,10,300,14

>>B8 0B 控件的ID 号为 3000。

对应资源脚本文件中的以下部分:

#define ID_TEXT 3000 中的 ID_TEXT

>>FF FF 81 00

类名。Unicode 字符串。控件的名称 ?????

对应资源脚本文件中的以下部分:

?????

>>00 00 字段 text 的值。

>>00 00 字段 nExtraStuff 的值。暂时为0。

因为刚好对齐,所以结尾也没有附加的0了。

第二部分之控件4的定义:

>>02 00 01 50 控件基本样式定义。

对应资源脚本文件中的以下部分:

>>00 00 00 00 扩展样式,为 0。

>>0A 00 1E 00 64 00 0E 00 控件相对于对话框的坐标,起点 (10,30),宽度 100,高度14。

对应资源脚本文件中的以下部分:

CHECKBOX "控制台程序",ID_CONSOLE,10,30,100,14 中的 10,30,100,14

>>B9 0B 控件的ID 号为 3001。

对应资源脚本文件中的以下部分:

#define ID_CONSOLE 3001 中的 3001

>>FF FF 80 00 A7 63 36 52 F0 53 0B 7A 8F 5E

类名。Unicode 字符串。控件的名称 ?????

对应资源脚本文件中的以下部分:

?????

>>00 00 字段 text 的值。

>>00 00 字段 nExtraStuff 的值。暂时为0。

因为刚好对齐,所以结尾也没有附加的0了。

资源表编程:

更改图标实验:

在“PE.rc”文件的基础上,再加入一个ICO 文件,生成另外一个 PEDumpIcon.rc,其资源脚本代码如下:

复制 pe.asm 到文件PEDumpIcon.asm,人然后编译链接 PEDumpIcon.asm 文件,发现生成的程序图标发生了变化,由原来的 main.ico 变成了 boy.ico。

这里编译连接资源命令找了好久,首先要用 rc 编译 rc 文件生成 RES 文件,再用 Link 一起链接才行:

rc PEDumpIcon.rc

ml -c -coff PEDumpIcon.asm

link -subsystem:windows PEDumpIcon.obj PEDumpIcon.RES

使用PEInfo 查看资源信息如下:

PE.exe的字节码解析:

(1) 图标头

>>00 00 保留,必须为 0。

>>01 00 资源类别,表示该格式符合 ICO 文件格式。

>>01 00 ICO 中图标的个数 ,0001h 为一个。

(2) 第一个图标项定义

>>20 宽 32。

>>20 高 32。

>>10 颜色数 16 位。

>>00 保留。

>>01 00 调色板 。

>>04 00 RGB 每字节的位。

>>E8 02 00 00 该图标大小 744 字节,与从 PE 资源里取出的数是相等的。

>>03 00 图标在文件中的索引为 0x0003 。

修改索引 03 为索引 01 :

用 WinHex 打开 PEDumpIcon.exe 文件,在文件偏移位置 0x002B03 处,将原来的值 03 更改为 01,然后回到文件夹,刷新一下,发现图标显示果然回到原来的 main.ico。如图 7-7 所示。(额~,这里我的本地环境对图标的显示有问题,所以我只能通过winhex中的图标区域来回显)

01 号图标的数据长度和 03 号图标的数据长度刚好相等。这就是为什么我们直接修改一个字节就可以成功的原因 :

现在将 03 的值修改为长度不相等的 02,发现图标的大小改变了,所以也模糊了:

如果更改为一个不存在的值时系统会在显示时帮助你画出一个图标来当做该程序的图标:

提取程序图标实例:

回顾图前面知识,一个 ICO 文件由三大部分组成: 第一部分是图标头,第二部分是图标项,第三部分为图标数据。其中,对应到 PE 资源表中,第一部分和第二部分组合成图标组资源(某些值稍有变化,见 7.4.5 小节),第三部分的每个图标数据对应一个图标资源。

本实例程序假设图标是由多个图标数据组成,即资源表中一定存在图标组,以下程序实现的是提取图标组中包含的所有的图标。

从PE资源文件到 ICO 文件的数据组合:

ICO 头部分 +ICO 项描述在资源图标组里定义,每一部分数据则在资源图标里定义。程序首先检测是否存在图标组资源,如果不存在则退出,如果存在,就定位到图标组资源数据,通过将 PE 文件的字段编号 (1 个字) 修改为 ICO 文件的偏移(2 个字),重新组合 ICO 头部使其符合 ICO 文件头部要求。最后,分别读取各部分图标资源数据,顺序连接到 ICO 头部后即可。

源码分析:

完整源代码见随书文件 chapter7\PEDumpIcon.asm,以下是对部分源代码的简单分析。程序复制自PE.asm,更新的代码从函数 _openFile 开始,代码如下:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~代码略~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

提取 Internet Explorer 程序的图标:

编译链接 PEDumpIcon.asm 后运行 PEDumpIcon.exe 程序。打开 IExplorer.exe 文件,运行结果如下:

如图所示,程序首先显示待处理的 PE 文件中一共有多少个图标组。然后依次显示每个图标组的相关信息,这些信息包括 : 图标组的编号、所在文件的位置,以及图标组资源的长度。最后,将该图标组中所有的图标,按每个图标一个 ICO 文件的方式将资源表中的数据转储到文件中。

本节依据 PE 资源表中对图标文件的处理方式,完成了从资源数据到图标的反抓取。在编写本程序时,重点阅读如何在 PE 资源表中得到指定类别、指定名称(或编号) 的资源的位置和资源的长度。

更改程序图标实例:

因为通过直接修改 PE 资源文件实现图标的修改,涉及许多还设有讲过的知识,本实例将使用现有的 Windows API 函数来完成这个任务。

Windows 操作系统为开发者提供了几个 API 函数,用来更新 PE 文件中资源的函数有 :

BeginUpdateResource、UpdateResource、EndUpdateResource。

用来枚举 PE 文件中资源的函数有 :

EnumResourceTypes、EnumResourceNames、EnumResourceLanguages。

上有具体的使用方法可以参见 MSDN,以下将使用这些函数实现图标资源的替换。

本实例的目标是将指定PE 程序显示的图标更改为 boy.ico 图标:

这里还是从函数 _openFile 开始,不再对 PE 文件进行格式检测,而是直接处理:

代码清单 7-7 ”选择 PE 文件并处理的函数 _openFile (chapter7\PEUpdatelcon.asm)

代码略~~~~~

调用函数 _doUpdate 写入 PE 图标 boy.ico。先修改第一个图标组,然后再更新编号为 1 的图标数据:

代码清单 7-8 用指定 1CO 文件替换 PE 程序的图标 (chapter7\PEUpdatelcon.asm)

代码略~~~~~~

运行测试:(由于我这里是windows10环境所以执行不成功)

如以上代码所示,18 一 24行打开指定的 ICO 文件,26 一 31 行读取 ICO 文件的图标头和图标项内容分别存储在变量 @stID 和 @stIDE 中。46 一 56 行通过读取的 ICO 文件的图标头和图标项生成 PE 文件的图标组资源数据。在这里要特别注意,从 ICO 文件到 PE 资源图标组数据的变化,即每个图标项的双字偏移地址要更改为单字的编号。行 58 一 71 通过调用相应的 API 函数实现 PE 程序的图标的替换。

总结:

本章着重介绍了 PE 中的资源表。PE 中的资源表实际是一个四层的排序二又树的典型应用。通过对资源表数据结构的解析,我们可以获得某个资源在文件中的位置及该资源数据块的大小,进一步获取该资源块。本章还针对常见的资源类型对资源块的字节码进行深度解析,让读者了解在资源脚本文件中,脚本与资源编译程序最终生成的资源数据块字节码之间存在一一对应的关系。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值