PE文件解析-资源中的版本信息结构

一、概述

    想要获取一个可执行文件(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;
}

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页