PE-资源表

news/2024/12/21 22:47:26/

资源表

PE中的相关资源可以通过程序进行深度定位,所获取的二进制字节码与资源脚本语句之间是一一对应的

这些数据可能是源代码内部需要用到的常景,比如 菜单选项、界面描述等;也可能是源代码外部的,比如程序的图标文件、背景音乐文件、配置文件等,以上这些数据统称为资源。

5.1 资源分类

资源数据在PE里是最复杂的一种。其难度主要体现在对资源数据的遍历定位上,以及资源块的不易阅读性。因为即使通过信息定位方法找到了资源块,其内部结构还需要进一步解析。

程序中常用的六类资源包括:
1.位图资源
2.光标资源
3.图标资源
4.菜单资源
5.对话框资源
6.自定义资源

5.1.1 位图、光标、图标资源

位图、光标和图标是标识程序用途、修饰程序的最简约的符号,一般对应ico、cur、ani 和bmp文件内容。

在对资源脚本文件进行定义时,通常使用文件名,最后由资源编译器rc.exe将像素数据读人,再转换为二进制格式存储在PE的资源表指向的位置。位图、光;标、图标这三类资源在脚本文件中的定义格式如下:
1.位图定义:namelD BITMAP [DISCARDABLE]位图文件名
2.光标定义:namelD CURSOR [DISCARDABLE]光标文件名
3.图标定义:namelD ICON [DISCARDABLE]图标文件名

1.namelD表示该资源的名字,在程序中使用资源时需要用到它,类似于文件的句柄。
2.BITMAP. CURSOR. ICON表示资源的类型。
3.DISCARDABLE关键字是可选项,表示在不用的时候可以从内存中暂时卸载掉。
注意 当文件名包含空格时,需要使用英文半角状态下的双引号引起来。

对应的外部文件可以使用绝对路径。

5.1.2 菜单资源

菜单是大部分应用程序都具备的资源。在资源脚本文件中,菜单的定义格式如下所示:

菜单 ID MENU [DISCARDABLE] BEGIN菜单项定义.......
END

其中,菜单ID可以是16位的整数,其赋值范围在1〜65535之间。菜单项的定义可以有三种,分别表示:
1.普通菜单项
2.菜单分隔符
3.弹出菜单
其语法结构分别如下所示:

MENUITEM 菜单文字,命令ID [,选项列表]MENUITEM SEPARATOR
POPUP 菜单文字[,选项列表] 
EGIN菜单项定义.......
END

5.1.3 对话框资源

对话框也是大部分程序具备的一种资源。弹出式对话框人性化地排列若文本框、说明文 字和按钮等可视化控件,使复杂的计算机操作变得容易。
在资源脚本的定义中,对话框最为复杂,其语法如下:

对话枢 ID DIALOG [DISCARDABLE] x 坐标,y 坐标,宽度,高度[options]BEGIN
子窗口控件1 
子窗口控件2
.......
END

5.1.4 自定义资源

通常,当开发者需要在PE文件中附带自定义数据时,可以使用自定义资源。其在资源 文件中的定义语法如下:

资源 ID	类型 ID (DISCARDABLE]
BEGIN数据定义.......
END

大部分情况下,都是将一个磁盘文件当做资源的内容。此时的语法简化为:

资源ID 类型ID [DISCARDABLE]文件名 

类型ID可以是大于255的数值或字符串

5.2 PE资源表组织

5.2.1 资源表的组织方式

PE的资源组织方式类似于操作系统的文件管理方式。从根目录开始,下设一级子目滾、二级子目录和三级子目录:三级子冃录下才是文件。

一级子目录按照资源类型分类,如“光标” 一级子目录、“位图” 一级子目录、“菜单” 一级子目录、“字符串” 一级子目录、“加速键” 一级子目录等多个资源类型。
二级子目录按照资源的ID分类。例如,同样是“菜单” 一级子目录的内容,其下可以有: IDM_OPEN的ID号为2001、IDM_EXIT的ID号为2002、IDM_1的ID号为4000等多个菜单项。 三级子目录是按照资源的代码页分类,即不同的语言代码页对应不同的数据。其中,根 据语言可以分为简体中文、英文、繁体中文等多个代码页。
三级目录后即为节点,也就是所说的“文件”。这里的“文件”其实就是包含了资源数据 的指针和大小等信息的一个数据结构而已。对所有资源数据块的访问均可从这里开始。

从数据结构角度来看,资源表是一个四层的二叉排序树结构。其中,第一层为主干,第 二、三层为枝干,叶子节点为第四层。主干和枝干的节点即为资源目录结构单元

在这里插入图片描述

在这里插入图片描述

5.2.2 资源表数据定位

资源表是一张描述资源数据在PE中的分布情况的表。资源表是数据目录中注册的数据类型之一,其描述信息位于数据目录的第3个目录项中。

资源表数据所在地址RVA
源表数据大小

5.2.3 资源目录头 IMAGE_RESOURCE_DIRECTORY

资源表数据从第一级资源目录开始。资源的毎一级目录都会有一个资源目录头,它标识了该类资源的属性、创建日期和版本等信息.其中也包含了随后的目录项的数量描述信息。

详细结构定义如下:

IMAGE_RESOURCE_DIRECTORY STRUCT Characteristics                 //dd   0000h资源属性TimeDatestamp                   //dd   0004h时间戳MajorVersion                    //dw   0008h资源大版本号MinorVersion                    //dw   0008h资源小版本号NumberOfNamedEntries            //dw   以名称命名的入口数量NumberOfIdEntries               //dw   命名的入口数量
IMAGE RESOURCE DIRECTORY ENDS

各字段的详细解释:

73.IMAGE_RESOURCE_DIRECTORY.Characteristics
+0000h,双字。资源属性,保留为将来使用,必须为0。

74.IMAGE_RESOURCE_DIRECTORYTimeDateStamp
+0004h,双字。时间戳,即创建该资源的时间。

75.IMAGE_RESOURCE_DIRECTORY. MajorVersion IMAGE_RESOURCE_DIRECTORY. MinorVersion
+0008h,双字。资源的版本号。未用,大部分情况下为0。

76.IMAGE_RESOURCE_DIRECTORY. NumberOfNamedEntries
+000ch,双字。以名称命名的资源个数。

77.IMAGE_RESOURCE_DIRECTORY. NumberOfldEntries +000eh,双字。以ID命名的资源个数。
以上字段中,最重要的是76和77两个字段。在资源脚本文件中,定义资源时,既可以使用字符串作为名称来标识一个资源,也可以通过ID号来标识资源。资源目录项的数量等于两者之和。

5.2.4 资源目录项 IMAGE_RESOURCE_DIRECTORY_ENTRY

紧跟在资源目录后的数据结构,就是在资源目录中声明的资源目录项。一个资源目录可能有多个资源目录项(以名称定义的资源目录项或以ID定义的资源口录项,或者两者组合),目录项和目录项之间是线性排列的。首先按照字母升序(不分大小写)排列名称资源目录项, 然后再按ID升序排列ID资源目录项。

在这里插入图片描述

资源目录项数据结构的详细定义:

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {union {struct {DWORD NameOffset : 31;			//资源名偏移DWORD NameIsString : 1;			//资源名为字符串};DWORD   Name;					//资源/语言类型WORD    Id;					//资源数字ID};union {DWORD   OffsetToData;				//数据偏移地址struct {DWORD   OffsetToDirectory : 31;         //子目录偏移地址DWORD   DataIsDirectory : 1;	        //数据为目录};};
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;

77.IMAGE_RESOURCE_DIRECTORY_ENTRY.Name1
+0000h,双字。第一个union字段,它定义了目录项的名称或者ID。
该双字的髙位(即31位)如果为1,则表示低地址部分为一个指向Unicode字符串的指针 (注意,这里的字符串不是Ansi字符串,所以另有规定);如果为0,则表示该字段为一个编号。 资源中对字符串的定义全部采用Unicode编码,该指针并不直接指向一个以“\0”结尾 的字符串所在地址,而是指向了结构IMAGE_RESOURCE_DIR_STRING_U。该结构完善了指针的定义(即不仅包含指针,还包含指针指向的块长度,大家可以自己想想为什么这里需 要长度字段),其详细定义如下:

MAGE_RESOURCE_DIR_STRING_U STRUCT 
Lengthl    //dw  OOOOh -字符串长度
Namestring //dw  0002h - Unicode 字符串,长度不确定
IMAGE_RESOURCE_DIR_STRING_U ENDS

78.IMAGE_RESOURCE_DIRECTORY_ENTRY.OffsetToData
+0004h,双字。这个字段是一个指针,当它的高位(第31位)为0时,指针指向的是描 述资源数据块的指针,通常出现在第三级目录中;当高位为1时,低位数据指向下一级目录块的起始地址。
提示:字段78和79中的地址并不是基于文件起始地址的,它的偏移是基于资源表的起始位置。

5.2.5 资源数据项 IMAGE_RESOURCE_DATA_ENTRY

资源数据项其实就是前面所说的“目录-文件”结构中的“文件”。它是通过三次目录 定位后找到的一个数据结构
如图,第三级目录项中的字段IMAGE_RESOURCE_DIRECTORY_ENTRY. OffsetToData指向了资源数据项,而资源数据项中的OfftetToData字段则指向了资源数据块。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JWfJBANr-1639892528787)(C:\Users\megaparsec\AppData\Roaming\Typora\typora-user-images\image-20211114191029716.png)]

IMAGE_RESOURCE_DATA_ENTRY STRUCTOffsetToData	dd 0000h  -资源数据的 RVASizel	        dd 0004h	-资源数据的长度CodePage	    dd 0008h	-代码页Reserved	    dd 000Ch	-保留字段
IMAGE_RESOURCE_DATA_ENTRY ENDS

80.IMAGE_RESOURCE_DATA_ENTRY.OffsetToData
+0000h,双字。该字段是一个指向资源数据块的指针,是一个RVA值,在文件中访问时需要注意转换成文件偏移。此处指向的资源数据块还不是赤裸裸的资源信息,而是附加了一 些数据结构的资源块。后面7.4节还会对常用的资源块进行进一步的解析。

81.IMAGE_RESOURCE_DATA_ENTRYSizel
+0004h,双字。资源数据的大小。

82.IMAGE_RESOURCE_DATA_ENTRY.CodePage
+0008h,双字。代码页,未用,大多数情况下为0。

83.IMAGE_RESOURCE_DATA_ENTRY.Reserved
+000ch,双字。保留字段。总是为0。
对资源表的大部分编程,只要能解析出该结构中指定资源块所处的地址和资源块的大小

5.2.6 三级结构中目录项的区别

由于目录处的级别不同,目录中各字段所表述的内容也不一样;尽管它们具有相同的数 据结构和完全相同的字段,在不同级别的目录项中有些字段的含义是不一样的。本小节就专 门研究三级目录中目录项各字段不一样的地方。

1.IMAGE_RESOURCE_DATA_ENTRY.Name1
(1)字段最髙位(即31位)为1
当结构用于第一层目录时,表示这是一个非标准的类型。由该字段的低31位组成一个偏移值,该偏移是相对于资源基地址的特殊偏移地址。该地址指向一个IMAGE_RESOURCE_ DIR_STRING_U结构表示的Unicode字符串。字符串为非标准的类型的名字。类似于本章第 1节自定义资源中的“DLLTYPE”。
当结构用于第二层目录时,表示这是一个非标准的命名。由该字段的低31位组成一个偏移值,该偏移是相对于资源基地址的特殊偏移地址。该地址指向一个IMAGE_RESOURCE_ DIR_STRING_U结构表示的Unicode字符串。字符串为非标准的类型下的命名,类似于本章 第1节自定义资源中的“DIB_WINRESULT”。
当结构用于第三层目录时,表示这是一个标准的语言(没有预定义的代码页)。由该字段的低31位组成一个偏移值,该偏移足相对干资源基地址的特殊偏移地址。该地址指向一个IMAGE_ RESOURCE_DIR_STRING_U结构表示的Unicode字符串。字符串为彳麻准的语言的名字。
(2)字段第31位为0
当结构用于第一层目录时,表示这是标准的类型(预定义的类型)。由该字段的低16位组成整数标识符ID,由于该类型巳定义,所以可以通过该标识符获取预先定义的名字。例如 该值为03h,则名字表示预定义当中的“ICON”。
当结构用于第二层目录时,表示这是标准的命名(预定义的类型)。由该字段的低16位组成整数标识符ID来定义名字。
当结构用于第三层B录时,表示这是标准的语言代码(预定义的类型)。由该字段的低16位组成整数标识符ID,可以通过该标识符获取预先定义的语言的名称。如ID=2052,表 示该语言为Simpled_Chinese (简体中文)。大多数情况下,毎个资源的代码页只定义一种。

2.IMAGE_RESOURCE_DATA_ENTRY.OffsetToData
(1)字段第31位为1
当结构用于第-层目录时,由该字段低31位组成一个整数偏移地址。该地址是相对干资 源起始地址的偏移,该偏移指向下一个目录。
当结构用于第二层目录时,由该字段低31位组成一个整数偏移地址。该地址是相对于资 源起始地址的偏移,该偏移指向下一个目录。
第三层目录的该值第31位不为1。
(2)字段第31位为0
第一层目录的该值第31位不为0。 第二层目录的该值第31位不为0。
当结构用于第三层目录时,表示该卞段指向一个数据项IMAGE_RESOURCE_DATA_ ENTRY。 .
注意由低31位组成的地址是基于资源起始地址的。

5.2.7 遍历资源表

代码1:

// from:《WindowsPE权威指南》
#define _CRT_SECURE_NO_WARNINGS
#include<Windows.h>
#include<stdlib.h>
#include<stdio.h>#define FILE_PATH_IN    "C:/Windows/System32/notepad.exe"static const char* szResName[0x11]
{0,"Corsor","Bitmap","Icon","Menu","Dialog","StringTable","FontDir","Font","Accelerator","RCDATA","MessageTable","GroupCursor","zz","GroupIcon","xx","Version"
};DWORD RVA2FOA(IN DWORD stRVA, IN LPVOID pFileBuffer)
{//重置头指针PIMAGE_DOS_HEADER pDosHeader = nullptr;PIMAGE_NT_HEADERS pNTHeader = nullptr;PIMAGE_FILE_HEADER pFileHeader = nullptr;PIMAGE_OPTIONAL_HEADER pOptionalHeader = nullptr;PIMAGE_SECTION_HEADER pSectionHeader = nullptr;pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + sizeof(pNTHeader->Signature));pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);//内存对齐DWORD dwMemAlignCount = pOptionalHeader->FileAlignment;//节区头个数DWORD dwSectionCount = pFileHeader->NumberOfSections;//距节区的偏移DWORD dwDiffer = 0;for (DWORD i = 0; i < dwSectionCount; i++,pSectionHeader++){//在内存中对齐后的大小DWORD dwBlockCount = 0;dwBlockCount = pSectionHeader->SizeOfRawData / dwMemAlignCount;dwBlockCount += (pSectionHeader->SizeOfRawData % dwMemAlignCount ? 1 : 0);DWORD dwBeginRVA = pSectionHeader->VirtualAddress;DWORD dwEndRVA = pSectionHeader->VirtualAddress + dwBlockCount * dwMemAlignCount;if (stRVA >= dwBeginRVA && stRVA < dwEndRVA){dwDiffer = stRVA - dwBeginRVA;return dwDiffer + pSectionHeader->PointerToRawData;}else if (stRVA < dwBeginRVA){return stRVA;}}return 0;
}DWORD ReadFile(OUT LPVOID* pFileBuffer,IN const char* lpszFile)
{LPVOID pTempFileBuffer = nullptr;FILE* pFile = nullptr;DWORD FileSize = 0;size_t n = 0;//打开文件pFile = fopen(lpszFile, "rb");if (pFile == nullptr){printf("文件打开失败");return 0;}//计算文件长度fseek(pFile, 0, SEEK_END);FileSize = ftell(pFile);fseek(pFile, 0, SEEK_SET);//分配缓冲区pTempFileBuffer = calloc(FileSize, sizeof(char));if (pTempFileBuffer == nullptr){printf("缓冲区分配失败");fclose(pFile);return 0;}//初始化缓冲区memset(pTempFileBuffer, 0, FileSize);//读取文件n = fread(pTempFileBuffer, FileSize, 1, pFile);if (n == 0){printf("读取文件失败");fclose(pFile);free(pTempFileBuffer);pTempFileBuffer = nullptr;return 0;}//关闭文件fclose(pFile);*pFileBuffer = pTempFileBuffer;pTempFileBuffer = nullptr;return n;
}void PrintResourceTable()
{PIMAGE_DOS_HEADER pDosHeader = nullptr;PIMAGE_NT_HEADERS pNTHeader = nullptr;PIMAGE_FILE_HEADER pFileHeader = nullptr;PIMAGE_OPTIONAL_HEADER pOptionalHeader = nullptr;PIMAGE_DATA_DIRECTORY pDataDirectory = nullptr;PIMAGE_RESOURCE_DIRECTORY pResourceTable = nullptr;PIMAGE_RESOURCE_DIRECTORY_ENTRY pResourceEntry = nullptr;LPVOID pFileBuffer = nullptr;DWORD dwSize = 0;dwSize = ReadFile(&pFileBuffer, FILE_PATH_IN);if (dwSize == 0 || pFileBuffer == nullptr){printf("读取文件失败");}pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + sizeof(pNTHeader->Signature));pOptionalHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//定位资源表pDataDirectory = (PIMAGE_DATA_DIRECTORY)(&pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE]);pResourceTable = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pFileBuffer + RVA2FOA(pDataDirectory->VirtualAddress, pFileBuffer));pResourceEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pResourceTable + 1);//解析第一层DWORD dwTypeCount = pResourceTable->NumberOfIdEntries + pResourceTable->NumberOfNamedEntries;for (DWORD i = 0; i < dwTypeCount; i++){//最高位为0if (pResourceEntry[i].NameIsString == 0){if (pResourceEntry[i].Id < 0x11){printf("资源类型ID:%d %s\n", pResourceEntry[i].Id, szResName[pResourceEntry[i].Id]);}else{printf("资源类型ID:%d\n", pResourceEntry[i].Id);}}//最高位为1else if (pResourceEntry[i].NameIsString == 1){PIMAGE_RESOURCE_DIR_STRING_U pStr = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResourceTable + pResourceEntry[i].NameOffset);WCHAR szStr[MAX_PATH] = { 0 };memcpy(szStr, pStr->NameString, pStr->Length * sizeof(WCHAR));printf("资源类型名称:%ls\n", szStr);}//解析第二层if (pResourceEntry[i].DataIsDirectory == 1){printf("第二层目录偏移:%x\n", pResourceEntry[i].OffsetToDirectory);PIMAGE_RESOURCE_DIRECTORY pRes2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResourceTable + pResourceEntry[i].OffsetToDirectory);PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes2 + 1);DWORD dwCount = pRes2->NumberOfIdEntries + pRes2->NumberOfNamedEntries;for (DWORD i = 0; i < dwCount; i++){//最高位为0if (pResEntry2[i].NameIsString == 0){printf("  ->资源标识ID:%d\n", pResEntry2[i].Id);}else{PIMAGE_RESOURCE_DIR_STRING_U pStr = (PIMAGE_RESOURCE_DIR_STRING_U)((DWORD)pResourceTable + pResEntry2[i].NameOffset);WCHAR szStr[MAX_PATH] = { 0 };memcpy(szStr, pStr->NameString, pStr->Length * sizeof(WCHAR));printf("  ->资源名称:%ls\n", szStr);}//解析第三层if (pResEntry2[i].DataIsDirectory == 1){PIMAGE_RESOURCE_DIRECTORY pRes3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)pResourceTable + pResEntry2[i].OffsetToDirectory);PIMAGE_RESOURCE_DIRECTORY_ENTRY pResEntry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pRes3 + 1);printf("    -->代码页标号为:%x\n", pResEntry3->Id);if (pResEntry3->DataIsDirectory == 0){PIMAGE_RESOURCE_DATA_ENTRY pResDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)pResourceTable+pResEntry3->OffsetToData);printf("    --数据RVA:%x\n", pResDataEntry->OffsetToData);printf("    --数据大小:%x\n", pResDataEntry->Size);}}}}}}int main()
{PrintResourceTable();return 0;
}

代码2:

// from:《WindowsPE权威指南》
void RESOURCE_Dlg::MakeTree(HTREEITEM hitem,IMAGE_RESOURCE_DIRECTORY* lpIMAGE_RESOURCE_DIRECTORY)
{WORD nCount = lpIMAGE_RESOURCE_DIRECTORY->NumberOfIdEntries + lpIMAGE_RESOURCE_DIRECTORY->NumberOfNamedEntries;IMAGE_RESOURCE_DIRECTORY_ENTRY* lpRESOURCE_DIRECTORY = (struct _IMAGE_RESOURCE_DIRECTORY_ENTRY *)(lpIMAGE_RESOURCE_DIRECTORY + 1);for (int i = 0 ; i < nCount; i ++){if (lpRESOURCE_DIRECTORY->DataIsDirectory == 1){CString csTemp;DWORD RESOURCEAdder = lpRESOURCE_DIRECTORY->OffsetToDirectory + (DWORD)m_lpIMAGE_RESOURCE_DIRECTORY;csTemp.Format("资源编号%d : 目录地址%p",lpRESOURCE_DIRECTORY->Id,lpRESOURCE_DIRECTORY->OffsetToDirectory + m_dwMemImageBase);HTREEITEM newhitem = m_TreeRESOURCE.InsertItem(csTemp,hitem);MakeTree(newhitem,(struct _IMAGE_RESOURCE_DIRECTORY *)RESOURCEAdder);}else{CString csTemp;DWORD RESOURCEAdder = lpRESOURCE_DIRECTORY->OffsetToDirectory + (DWORD)m_lpIMAGE_RESOURCE_DIRECTORY;IMAGE_RESOURCE_DATA_ENTRY * lpRESOURCE_DATA = (struct _IMAGE_RESOURCE_DATA_ENTRY *)(lpRESOURCE_DIRECTORY->OffsetToDirectory + (DWORD)m_lpIMAGE_RESOURCE_DIRECTORY);csTemp.Format("数据地址%p,数据长度%p",lpRESOURCE_DATA->OffsetToData,lpRESOURCE_DATA->Size);m_TreeRESOURCE.InsertItem(csTemp,hitem);}lpRESOURCE_DIRECTORY++;}return;
}BOOL RESOURCE_Dlg::OnInitDialog()
{CDialog::OnInitDialog();m_TreeRESOURCE.DeleteAllItems();HTREEITEM hitem = m_TreeRESOURCE.InsertItem("资源表");MakeTree(hitem,m_lpIMAGE_RESOURCE_DIRECTORY);SetWindowLong(m_hWnd,GWL_STYLE,GetWindowLong(m_hWnd,GWL_STYLE)|TVS_CHECKBOXES|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT);return TRUE;  // return TRUE unless you set the focus to a control// EXCEPTION: OCX Property Pages should return FALSE
}

5.4 PE资源表解析

5.4.1 资源脚本

PE.exe的资源定义在文件pe.rc中,文件中一共定义了三种资源:图标、菜单和对话框。 详细定义如下:

// from:《WindowsPE权威指南》
#include <resource.h>
#define	ICO_MAIN	1000;常量定义
#define	DLG_MAIN	1000
#define	IDC_INFO	1001
#define	IDM_MAIN	2000
#define	IDM_OPEN	2001
#define	IDM_EXIT	2002#define	IDM_1	4000
#define	IDM_2	4001
#define	IDM_3	4002
#define	IDM_4	4003ICO_MAIN  ICON "mian.ico"      ;图标定义DLG_MAIN DIALOG 50,50,544,399	;对话框定义STYLE DS_MODALFRAME | WSPOPUP | WS_VISIBLE | WS_CAPTlON | WS_SYSMENU CAPTION "PE 文件基本信息 by  qixiaorui"MENU IDM_MAINFONT 9,"宋体"BEGINCONTROL "",IDC_INFO,"RichEdit20A",196 | ES_WANTRETURN | WSCHILD | ES_READONLY | WS_VISIBLE |WSBORDER                 | WSVSCROLL | WSTABSTOP,0,0,540,396 ENDIDM MAIN menu discardable	;主菜单定义BEGINPOPUP "文件(&F)" BEGINmenuitem "打开文件(&0)…",IDM_OPEN menuitem separator menuitem "退出(&x) " , IDM_EXITENDPOPUP "查看"BEGINmenuitem "源文件",IDM_1 menuitem "窗口透明度",IDM 2menuitem separatormenuitem "大小",IDM_3 menuitem "宽度",I DM 4END
END

5.4.2 要明确的概念

1.资源表中的Unicode字符
资源文件中的所有字符串都以Unicode格式存储,每个字符都由一个16位(单字)值表 示,字符串以UNICODE_NULL (该值是两个“\0”字节)结束。
**资源编译器调用Windows API中的MultiByteToWideChar函数将ASCII字符串转换为Unicode字符串,所有溢出的字符 都被当做合法的Unicode字符直接存储。**当这些字符串被程序以ASCII字符读出(例如使用 LoadString API)时,系统将它们再由Unicode转换为ASCII字符。
仅有的例外是在RCDATA语句中的字符串。这些“伪”字符串并不是真正的字符串,只 被当做一些字节的集合。用户可能会用RCDATA i§句存储一些自定义的数据结构,如果一个“伪”字符串被自动转换为Unicode字符串存储起来,这个“伪”字符串就会按照Unicode 字符串的格式被存储,从而导致这个“伪”字符串字节码内容的改动。假设这个“伪”字符 串是一个PE文件的字节码,再次被释放以后,这个PE文件就可能无法运行了。因此,这些 “伪”字符串必须以它的本来面目存储下来,即字节码。若想在RCDATA语句中包含Unicode 字符串,用户可以使用带“L”前缀的字符串。

2.资源字节码对齐
为使二进制资源文件更容易读写,在Win32下,文件中的所有对象都是双字对齐的,包 括头信息和数据项。这并不会改变资源数据结构中每个字段的顺序,但会在这些字段中间增 加一些填充域;通常情况下,这些填充域的值均为0。
资源中的大部分类型都遵循该规则,但有两个除外,一个是字体(font),另一个是字体 目录(fontdir)。因为这两个结构直接复制自别的文件,它们并不被资源编译器所使用。

3.一个字段的多重定义
在接下来的数据结构中,大家会看到类似于“[名称或序数]”这样的字段描述。“[名称或序数]”字段的第一个单字,标志这个字段到底是一个数字还是一个字符串。如果它等于Oxffff (一个非法的Unicode字符),那么在它后面的单字信息就是一个类型序号(一个数字);否则, 这个字段就是一个Unicode字符串。
如果类型域是一个数字,那它就代表一个标准的或者用户自定义的资源类型。所有标准的Windows资源类型都被赋予一个特定的值(如下所示),它包含了绝大多数资源类型的类型序数。

/* 预定义的资源类型*/ 
#define RT_NEWRESOURCE 0x2000 
#define RT_ERROR 0x7fff 
#define RT_CURSOR 1 
#define RT_BITMAP 2 
#define RT_ICON 3 
#define RT_MENU 4 
#define RT_DIALOG 5 
#define RT_STRING 6 
#define RT_FONTDIR 7 
#define RT_FONT 8 
#define RT_ACCELERATORS 9 
#define RT RCDATA 10 
#define RT_MESSAGETABLE 11
#define RT_GROUP_CURSOR 12
#define RT_GROUP_ICON 14
#define RT_VERSION 16 
#define RT_NEWBITMAP (RT_BITMAP | RT_NEWRESOURCE) 
#define RT_NEWMENU (RT_MENU | RT_NEWRESOURCE) 
#define RT_NEWDIALOG (RT_DIALOG | RT_NEWRESOURCE)

5.4.3 菜单资源表

菜单资源表数据结构
菜单资源由一个菜单头加一个菜单项的序列组成。菜单项有两种:弹出式(POPUP)菜 単项和普通菜单项。
菜单头结构定义如下:

MenuHeader STRUCTwVersion	    //dw 版本号。暂时取值为0cbHeaderSize	//dw 头大小。暂时取值为0
MenuHeader ENDS

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

NormalMenuItem STRUCTfItemFlags	//dw	菜单标志wMenuID	    //dw	菜单IDszItemText	//dd	UnicOde 格式字符串,不确定长度
NormalMenuItem ENDS

其中,菜单分隔符MENUITEM SEPARATE也是普通菜单项,不过,其名称为空,ID为0, 标志也为0
fltemFlags是描述菜单项的标志集合。如果POPUP位被设置,则此项为弹出式的菜单项, 否则就是一个普通的菜单项。

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

PopupMenuItem STRUCTfltemFlags //dw ?	菜单标志szItemText //dd ?  Unicode 字符.大小不确定
PopupMenuItem ENDS

5.4.4 图标资源

图标有一套标准的大小和属性格式,且通常是小尺寸的。以ICO文件为例,一个ICO文 件就是一套相似的图片,每一张图片具有不同的尺寸和颜色数,目的是适应不同的计算机操 作系统和显示设备。
操作系统在显示一个图标时,会按照一定的标准选择图标中最适合当前显示环境和状态的图像。

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

ICON_DIR STRUCTidreserved       dw 	;保留字,必須为0idtype	       dw 	;资源类别,如粟是1表示为ICO文件idcount	       dw 	;图标敎.量icondirentry ICON_DIR_ENTRY [idcount] <?>	;图标项,一个图标一项
ICON_DIR ENDS	

idcount表示该ICO文件包含图标的数量。理论上,一个ICO文件最多可以包含65535个图标。接下来,是该文件所包含的毎一个图标的详细描述。

ICON_DIR_ENTRY STRUCTbWidth	    db		;宽度bHeight	    db		;高度bColorCount	db		;颜色数bReserved	    db		;保留字.必埙为0wPlanes	    dw		;调色板wBitCount	    dw		;每个像素的位数dwBytesInRes	dd	    ;资源长度
dwImageOffset	dd		;资源在文件偏移
ICON_DIR_ENTRY ENDS

ICON_DIR_ENTRY结构记录了每一个图标的尺寸、色深、图标资源占用的字节数。 dwImageOffset是一个文件偏移地址,指向图标资源数据起始位置。PE文件中的图标保存格 式与“.ico”文件中图标的保存格式略有不同。PE文件中,把ICON_DIR和图标资源作为两 种资源类型分别保存,前者是RT_GROUP_ICON类型,后者是RT_ICON类型。
在“.ico”文件中,ICON_DIR_ENTRY结构最后一个成员dwImageOffset表示图标资源 文件偏移地址,是一个双字;而在PE文件中,GRP_ICON_DIR_ENTRY结构最后一个成员 nID是一个字,表示图标的索引ID。

在这里插入图片描述

ICO文件分为三部分:图标头、图标项和图标数据。这三部分在PE文件 中会被電新组合,其中,图标头会被i新组合为资源类型RT_GROUP_ICON,图标项和图标 数据则被组合为资源类型RT_ICON。

图标头的idcount字段定义ICO 里图标的个数;每个图标的属性由图标项定义;图标项的字段dwImageOffset则指向了 ICO文 件中该图标数据的位置。

在这里插入图片描述

在这里插入图片描述

5.4.4 对话框资源

对话框的头是一个DialogBoxHeader结构

PE中的资源表实际是一个四层的 二叉树 二进制排序树的典型应用
注:二进制排序树 来自该链接:PE Format 中The .rsrc Section部分:Resources are indexed by a multiple-level binary-sorted tree structure.


http://www.ppmy.cn/news/763669.html

相关文章

PE是什么塑料?

聚乙烯&#xff08;polyethene &#xff0c;简称PE&#xff09;是乙烯经聚合制得的一种热塑性树脂。在工业上&#xff0c;也包括乙烯与少量α-烯烃的共聚物。聚乙烯无臭&#xff0c;无毒&#xff0c;手感似蜡&#xff0c;具有优良的耐低温性能&#xff08;最低使用温度可达-100…

软件测试流程进阶----5年软件测试经验总结

工作5年了&#xff0c;我一直希望让自己每年对测试的理解更深入一层。工作一年的时候我也写了一篇&#xff0c;谈轮了自己对各种测试的理解&#xff0c;这一年来&#xff0c;虽然对那些理概念的有所加强&#xff0c;自我感觉没有什么质的变化。前些天听我们公司的一位测试经理讲…

测试中SE、SSE、BSE、PE、PL、TL代表什么职位

1、SE 是 Software Engineer 软件工程师&#xff1a; 软件工程师分类有&#xff1a;高级软件工程师、软件工程师、助理软件工程师、软件技术员。2、SSE 是 Senior Software Engineer 高级软件工程师&#xff1a; 高级软件工程师是IT行业中的重要岗位。根据开发进度和任务分配…

PE、PM、PD、PR是什么岗位?

PE&#xff08;private equity&#xff09;&#xff1a;私募股权投资&#xff0c;金融行业。 PR&#xff08;public relationship&#xff09;&#xff1a;公共关系&#xff0c;即平时说的公关&#xff0c;多见于传媒、互联网、品牌营销等各种行业。 PD&#xff08;product des…

IC领域常见职位简称AE、FAE、PE、SE、VE、ME、TE、PTE

AE Application Engineer 应用工程师。定位&#xff1a;IC流片后&#xff0c;需要在通用应用系统或者关键刻画的系统平台上进行功能验证&#xff0c;发现问题反馈给IC设计工程师。与FAE相比&#xff0c;AE偏向IC设计&#xff0c;FAE偏向市场对一点。 FAE Field Appilcation …

【电子通识】PE和SQE是什么职位

要学习一个专业&#xff0c;就得先了解一些专业内的词汇。因为这些词汇往往是把人们挡在专业外面的围栏。只有穿过重重栅栏&#xff0c;才能探得真知。 对于硬件工程师&#xff0c;工作中需要打交道的人并不少&#xff0c;有PE/QE/FQC/SQE等其他工程师。也常常会遇到很多专业的…

腾讯云部署vitepress,高颜值文档博客

首先上官方网站。https://vitepress.dev/ 先看两张效果图。 一定要用这个官网&#xff0c;之前看了一个翻译版本&#xff0c;好像翻了一半不弄了&#xff0c;坑了半天时间也解决不了。目前看起来还没有官翻。 目前使用到的是腾讯云的轻量应用服务器&#xff0c;效果还是非常好…

2022年真题 - 08 - web 服务(apache)

web 服务 题目安装 httpd 软件创建用户并修改 httpd 运行用户限制web服务只能使用系统500M物理内存服务器证书重定向安全加固其他参数配置搭建 www.chinaskills.cn 站点StorageSrv 服务器配置 mariadbAppSrv 导入 wordpress,安装 PHP发布 www.chinaskills.cn 站点搭建 downloa…