一、概述
想要获取一个可执行文件(PE文件)里包含的资源文件,首先要解析可执行文件,得到资源存储的地址及大小,可参考 https://blog.csdn.net/zhyulo/article/details/85717711 。然后,根据资源存储方式,得到各资源的数据内容及其大小,可参考 https://blog.csdn.net/zhyulo/article/details/85930045 。
PE文件的资源中,版本信息的资源类型ID=16。在RC文件中,版本信息的定义方式如下:
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x4L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "080404B0"
BEGIN
VALUE "CompanyName", "\0"
VALUE "FileDescription", "PE文件资源提取 Microsoft 基础类应用程序\0"
VALUE "FileVersion", "1, 0, 0, 1\0"
VALUE "InternalName", "PE文件资源提取\0"
VALUE "LegalCopyright", "版权所有 (C) 2017\0"
VALUE "LegalTrademarks", "\0"
VALUE "OriginalFilename", "PE文件资源提取.EXE\0"
VALUE "ProductName", "PE文件资源提取 应用程序\0"
VALUE "ProductVersion", "1, 0, 0, 1\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x804, 1200
END
END
相对应的程序版本信息,可以通过右键->属性->详细信息查看,如下:

二、版本信息的资源结构
版本信息在资源中的存储是以顺序储存的多叉树式数据结构,同时,4字节对齐存储。每个树节点包括数据头和数据内容两部分,都是4字节对齐,其中数据头的数据结构如下:
struct VersionInfo
{
WORD wLength;//结构总长度
WORD wValueLength;//信息长度
WORD wType;//信息类型(1-字符串,0-二进制)
WCHAR szKey[1];//Unicode 字符串 KEY 域
};
wLength:以该树节点为起点的所有节点的字节大小,包括本节点;
wValueLength:本节点的数据内容大小;
wType:本节点的数据类型(1-字符串,0-二进制);
szKey:Unicode字符串,标识该节点的KEY值,以'\0'为结束符。
数据头后紧跟着的就是数据内容,数据类型以wType(1-字符串,0-二进制)区分。注意:如果是字符串的话,数据字节数=wValueLength*2,因为是Unicode字符串,该字符串中间可能包括'\0'。如果wValueLength=sizeof(VS_FIXEDFILEINFO),则说明是跟节点。VS_FIXEDFILEINFO结构体定义如下:
typedef struct tagVS_FIXEDFILEINFO
{
DWORD dwSignature; /* e.g. 0xfeef04bd */
DWORD dwStrucVersion; /* e.g. 0x00000042 = "0.42" */
DWORD dwFileVersionMS; /* e.g. 0x00030075 = "3.75" */
DWORD dwFileVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwProductVersionMS; /* e.g. 0x00030010 = "3.10" */
DWORD dwProductVersionLS; /* e.g. 0x00000031 = "0.31" */
DWORD dwFileFlagsMask; /* = 0x3F for version "0.42" */
DWORD dwFileFlags; /* e.g. VFF_DEBUG | VFF_PRERELEASE */
DWORD dwFileOS; /* e.g. VOS_DOS_WINDOWS16 */
DWORD dwFileType; /* e.g. VFT_DRIVER */
DWORD dwFileSubtype; /* e.g. VFT2_DRV_KEYBOARD */
DWORD dwFileDateMS; /* e.g. 0 */
DWORD dwFileDateLS; /* e.g. 0 */
} VS_FIXEDFILEINFO;
数据内容后如果还有数据,则该数据为本节点的子节点数据,可以通过递归实现。
三、示例程序
以下程序展示了如何从PE文件的版本信息资源流中读取并输出成RC文件形式的代码。
void CopyOut(const char *str, int n)
{
for(int i=0; i<n; i++) Puts(str);
}
char* GetVersion(char *buf, int lay=0)
{
//计算版本信息块大小
struct VersionInfo
{
WORD wLength;//结构总长度
WORD wValueLength;//信息长度
WORD wType;//信息类型(1-字符串,0-二进制)
WCHAR szKey[1];//Unicode 字符串 KEY 域
} *Head = (VersionInfo*)buf;
int i;
for(i=0; Head->szKey[i]; i++);
i = (char*)(Head->szKey + i + 1) - buf;
i = (i + 3) /4*4;
WCHAR *Value = (WCHAR*)(buf + i);
i += Head->wValueLength * (Head->wType ? 2 : 1);
i = (i + 3) /4*4;
//输出版本信息块
CopyOut("\t", lay);
if(Head->wValueLength == sizeof(VS_FIXEDFILEINFO)) ;
else if(i < Head->wLength) Puts("BLOCK \"");
else Puts("VALUE \"");
WPuts(Head->szKey);
if(Head->wType)
{
if(i < Head->wLength) Puts("\"\n");
else if(!Head->wValueLength) Puts("\", \"\\0\"\n");
else
{
Puts("\", \"");
WPuts(Value, Head->wValueLength);
Puts("\"\n");
}
}
else if(Head->wValueLength == sizeof(VS_FIXEDFILEINFO))
{
Puts(" VERSIONINFO\n");
VS_FIXEDFILEINFO *Info = (VS_FIXEDFILEINFO*)Value;
Print(" FILEVERSION %d,%d,%d,%d\n",
Info->dwFileVersionMS>>16, Info->dwFileVersionMS&0xFFFF,
Info->dwFileVersionLS>>16, Info->dwFileVersionLS&0xFFFF);
Print(" PRODUCTVERSION %d,%d,%d,%d\n",
Info->dwProductVersionMS>>16, Info->dwProductVersionMS&0xFFFF,
Info->dwProductVersionLS>>16, Info->dwProductVersionLS&0xFFFF);
Print(" FILEFLAGSMASK 0x%xL\n",Info->dwFileFlagsMask);
Print(" FILEFLAGS 0x%xL\n",Info->dwFileFlags);
Print(" FILEOS 0x%xL\n",Info->dwFileOS);
Print(" FILETYPE 0x%xL\n",Info->dwFileType);
Print(" FILESUBTYPE 0x%xL\n",Info->dwFileSubtype);
}
else Print("\", %#x, %d\n",Value[0],Value[1]);
//递归输出子信息块
if(i < Head->wLength)
{
CopyOut("\t", lay++);
Puts("BEGIN\n");
while(i < Head->wLength)
{
i = GetVersion(buf + i, lay) - buf;
i = (i + 3) /4*4;
}
CopyOut("\t", lay-1);
Puts("END\n");
}
return buf + Head->wLength;
}
本文详细介绍了如何解析PE文件中的资源,特别是版本信息。首先概述了获取资源文件的方法,接着详细剖析了版本信息的资源结构,包括其多叉树存储方式和数据头的组成。最后,通过示例程序演示了如何读取和输出版本信息资源的内容。

1万+

被折叠的 条评论
为什么被折叠?



