本节我们将通过一个示例程序实现对资源表的遍历。
本节必须掌握的知识点:
资源表遍历
7.3.1 资源表遍历
实验四十九:遍历资源表
●模块1:resource.h
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 peinfo.rc 使用
//
#define ICO_MAIN 111
#define DLG_MAIN 1000
#define IDC_INFO 001
#define IDM_MAIN 2000
#define IDM_OPEN 2001
#define IDM_EXIT 2002
#define IDM_1 4000
#define IDM_2 4001
#define IDM_3 4002
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 113
#define _APS_NEXT_COMMAND_VALUE 4003
#define _APS_NEXT_CONTROL_VALUE 1002
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
●模块2:peinfo.rc
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/
#undef APSTUDIO_READONLY_SYMBOLS
/
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/
//
// Dialog
//
DLG_MAIN DIALOGEX 50, 50, 427, 300
STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "PE文件头中几个关键地址的定位:"
MENU IDM_MAIN
FONT 9, "宋体", 0, 0, 0x0
BEGIN
CONTROL "",IDC_INFO,"RichEdit20W",ES_MULTILINE | ES_NOHIDESEL | ES_READONLY | ES_WANTRETURN | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP,7,7,411,280,WS_EX_ACCEPTFILES
END
/
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
DLG_MAIN, DIALOG
BEGIN
LEFTMARGIN, 7
TOPMARGIN, 7
END
END
#endif // APSTUDIO_INVOKED
/
//
// AFX_DIALOG_LAYOUT
//
DLG_MAIN AFX_DIALOG_LAYOUT
BEGIN
0
END
RESULT_MODULE AFX_DIALOG_LAYOUT
BEGIN
0
END
/
//
// Menu
//
IDM_MAIN MENU
BEGIN
POPUP "文件(&F)"
BEGIN
MENUITEM "打开文件(&O)...", IDM_OPEN
MENUITEM SEPARATOR
MENUITEM "退出(&x)", IDM_EXIT
END
POPUP "编辑(&E)"
BEGIN
MENUITEM SEPARATOR
END
POPUP "格式(&O)"
BEGIN
MENUITEM SEPARATOR
END
POPUP "查看(&V)"
BEGIN
MENUITEM "源文件", IDM_1
MENUITEM "窗口透明度", IDM_2
MENUITEM SEPARATOR
MENUITEM "大小", IDM_3
END
POPUP "帮助(&H)"
BEGIN
MENUITEM SEPARATOR
END
END
/
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
ICO_MAIN ICON "main.ico"
#endif // 中文(简体,中国) resources
/
#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//
/
#endif // not APSTUDIO_INVOKED
●模块三:info.h
#pragma once
#ifndef INFO_H_
#define INFO_H_
#include <windows.h>
#include <richedit.h> //CHARFORMAT富文本结构定义
#include <commctrl.h> //通用控件
#pragma comment(lib,"comctl32.lib")
#include <strsafe.h> //StringCchCopy
#include <stdlib.h>
//函数声明
BOOL CALLBACK DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void Exception(void);
void init(); //初始化
void _OpenFile();//打开PE文件并处理
DWORD RVAToOffset(IMAGE_DOS_HEADER * lpFileHead, DWORD dwRVA);// 将内存偏移量RVA转换为文件偏移
DWORD GetRVASection(IMAGE_DOS_HEADER * lpFileHead, DWORD dwRVA);//查找 RVA 所在的节区
int CALLBACK _Handler(EXCEPTION_POINTERS * lpExceptionPoint);
void ShowErrMsg();
void _AppendInfo(const TCHAR * _lpsz);//往文本框中追加文本
//PE文件处理模块
void _getMainInfo(PBYTE, IMAGE_NT_HEADERS *, int);//从内存中获取PE文件的主要信息
void _getResourceInfo(PBYTE, IMAGE_NT_HEADERS *, int);//获取PE文件的资源信息
//将PEInfo.txt文本信息读入RichEdit控件
void _readToRichEdit();
#endif
●模块四:pemain.c(略)
●模块五:RvaToFileOffset.c(略)
●模块六:Getpeinfo.c(略)
●模块七:GetResourceInfo.c
/*
获取PE资源信息
*/
#include <windows.h>
#include "info.h"
extern TCHAR szFileName[MAX_PATH]; //pemian.c模块中定义
extern HANDLE hWinEdit,hFileDump;
extern HWND hWinMain;
void _AppendInfo(const TCHAR * _lpsz);
DWORD RVAToOffset(IMAGE_DOS_HEADER * lpFileHead, DWORD dwRVA);
DWORD GetRVASection(IMAGE_DOS_HEADER * lpFileHead, DWORD dwRVA);
//参数1:PE文件地址;参数2:资源块起始地址;参数3:目录项地址:参数4:层级
void ProcessRes(PBYTE lpFile, PBYTE lpRes, IMAGE_RESOURCE_DIRECTORY * lpResDir, DWORD dwLevel)
{
const TCHAR szLevel1[] = TEXT("\r\n---------------------------------------------------------\r\n")
TEXT("资源类型:%s\r\n")
TEXT("---------------------------------------------------------\r\n");
const TCHAR szResData[] = TEXT(" 文件偏移:%08X (代码页=%04X, 长度%d字节)\r\n");
const TCHAR szLevel1byID[] = TEXT("%d (自定义编号)");
const TCHAR szLevel2byID[] = TEXT(" ID: %d\r\n");
const TCHAR szLevel2byName[] = TEXT(" Name: %s\r\n");
const TCHAR szType[][16] = {
TEXT("光标 "),//1
TEXT("位图 "),//2
TEXT("图标 "),//3
TEXT("菜单 "),//4
TEXT("对话框 "),//5
TEXT("字符串 "),//6
TEXT("字体目录 "),//7
TEXT("字体 "),//8
TEXT("加速键 "),//9
TEXT("未格式化资源 "),//10
TEXT("消息表 "),//11
TEXT("光标组 "),//12
TEXT("未知类型 "),//13
TEXT("图标组 "),//14
TEXT("未知类型 "),//15
TEXT("版本信息 ") //16
};
DWORD dwNextLevel;
static TCHAR szBuffer[256];
static TCHAR szResName[256];
IMAGE_RESOURCE_DIRECTORY * lpstRES_DIR;//资源目录
IMAGE_RESOURCE_DIRECTORY_ENTRY * lpstRES_DIR_ENT;
IMAGE_RESOURCE_DATA_ENTRY * lpstRES_DATA_ENT;
int number; //资源数量
DWORD lpAddr, address;
DWORD IDname, dwBytesWrite;
dwNextLevel = dwLevel + 1;
//检查资源目录表,得到资源目录项的数量
lpstRES_DIR = lpResDir;
number = lpstRES_DIR->NumberOfIdEntries + lpstRES_DIR->NumberOfNamedEntries;
//IMAGE_RESOURCE_DIRECTORY结构后面紧跟着是IMAGE_RESOURCE_DIRECTORY_ENTRY结构
lpstRES_DIR_ENT = (IMAGE_RESOURCE_DIRECTORY_ENTRY *)((PBYTE)lpstRES_DIR + sizeof(IMAGE_RESOURCE_DIRECTORY));
//循环处理每个资源目录项
while (number--)
{
RtlZeroMemory(szBuffer, sizeof(szBuffer));
//OffsetToData字段最高位为1,后七位指向下层目录块的起始地址IMAGE_RESOURCE_DIRECTORY结构
lpAddr = lpstRES_DIR_ENT->OffsetToData;
if (lpAddr & 0x80000000)
{
lpAddr &= 0x7fffffff;
lpAddr += (DWORD)lpRes;
//第一层:资源类型
if (dwLevel == 1)
{
IDname = lpstRES_DIR_ENT->Name;//目录项名称字符串或ID
//最高位为1时,低7位值作为指向UNICODE编码的资源名IAMGE_RESOURCE_STRING_U结构
if (IDname & 0x80000000)
{
IDname &= 0x7fffffff;
IDname += (DWORD)lpRes;
//复制UNICODE资源名
lstrcpy(szResName, (LPCWSTR)(IDname + 2));
address = (DWORD)szResName;
}
//高位为0时,表示字段的值作为ID使用
else
{
if (IDname <= 16) //为预定义资源
{
address = (DWORD)&szType[IDname - 1];
}
else //大于16,自定义资源
{
wsprintf(szResName, szLevel1byID, IDname);
address = (DWORD)szResName;
}
}
wsprintf(szBuffer, szLevel1, (PBYTE)address);
}
//第二层:资源(ID或名称)
else if (dwLevel == 2)
{
IDname = lpstRES_DIR_ENT->Name;//目录项名称字符串或ID
//资源以字符串方式命名
if (IDname & 0x80000000)
{
IDname &= 0x7fffffff;
IDname += (DWORD)lpRes;
//复制UNICODE资源名
lstrcpy(szResName, (LPCWSTR)(IDname + 2));
wsprintf(szBuffer, szLevel2byName, szResName);
}
//资源以 ID 命名
else
{
wsprintf(szBuffer, szLevel2byID, IDname);
}
}
else
break;
//_AppendInfo(szBuffer);
//写入Dump文件
dwBytesWrite = 0;
WriteFile(hFileDump, szBuffer,
sizeof(szBuffer), &dwBytesWrite, NULL);
ProcessRes(lpFile, lpRes,
(IMAGE_RESOURCE_DIRECTORY *)lpAddr, dwNextLevel); //递归调用
}
//OffsetToData字段最高位为0,不是资源目录则显示资源详细信息,
//后七位指向用来描述资源数据块的IMAGE_RESOURCE_DATA_ENTRY结构
else
{
lpAddr += (DWORD)lpRes;
lpstRES_DATA_ENT = (IMAGE_RESOURCE_DATA_ENTRY *)lpAddr;
address = RVAToOffset((IMAGE_DOS_HEADER *)lpFile,
lpstRES_DATA_ENT->OffsetToData);
if (!address)
return;
wsprintf(szBuffer, szResData, address,
lpstRES_DIR_ENT->Name, lpstRES_DATA_ENT->Size);
//_AppendInfo(szBuffer);
//写入Dump文件
dwBytesWrite = 0;
WriteFile(hFileDump, szBuffer, sizeof(szBuffer),
&dwBytesWrite, NULL);
}
lpstRES_DIR_ENT++;
}
}
void _getResourceInfo(PBYTE lpFile, IMAGE_NT_HEADERS * _lpPeHead, int _dwSize)
{
const TCHAR szMsg5[] = TEXT("\r\n\r\n文件名:%s\r\n")
TEXT("---------------------------------------------------------\r\n")
TEXT("资源所处的节:%s\r\n");
const TCHAR szErrNoRes[] = TEXT("\r\n这个文件中没有包含资源!");
static TCHAR szBuffer[256];
static TCHAR szSectionName[16];
static TCHAR szNameB[256];
static TCHAR szNameW[256];
IMAGE_NT_HEADERS32 * lpstNT32; //PE32文件头
IMAGE_NT_HEADERS64 * lpstNT64; //PE64文件头
IMAGE_RESOURCE_DIRECTORY * lpstRES_DIR;资源目录
DWORD rva, address, dwBytesWrite;
lpstNT32 = _lpPeHead;
lpstNT64 = (IMAGE_NT_HEADERS64 *)_lpPeHead;
//检测是否存在资源--数据目录第2项
if (lpstNT64->OptionalHeader.Magic == 0x020B) //64位PE文件
{//数据目录项的第3项
rva = lpstNT64->OptionalHeader.DataDirectory[2].VirtualAddress;
}
else
rva = lpstNT32->OptionalHeader.DataDirectory[2].VirtualAddress;
if (!rva)
{
MessageBox(hWinMain, szErrNoRes, NULL, MB_OK);
//写入Dump文件
dwBytesWrite = 0;
WriteFile(hFileDump, szErrNoRes,
sizeof(szErrNoRes), &dwBytesWrite, NULL);
return;
}
//将 RVA 转换成实际的数据位置
address = RVAToOffset((IMAGE_DOS_HEADER *)lpFile, rva);
if (!address)
return;
//资源目录的实际地址
lpstRES_DIR = (IMAGE_RESOURCE_DIRECTORY *)(address + (DWORD)lpFile);
//获取资源目录所在的节区名
address = GetRVASection((IMAGE_DOS_HEADER *)lpFile, rva);
if (!address)
return;
RtlCopyMemory(szNameB, (LPCWSTR)address, 8);//改用内存拷贝
//需要将节区名称ASCII码字符转为Unicode字符
MultiByteToWideChar(CP_ACP, 0, (LPCCH)szNameB, 8, szSectionName, 16);
//显示一些常用的信息
RtlZeroMemory(szBuffer, sizeof(szBuffer));
wsprintf(szBuffer, szMsg5, szFileName, szSectionName);
//SetWindowText(hWinEdit, szBuffer);
//写入Dump文件
dwBytesWrite = 0;
WriteFile(hFileDump, szBuffer, sizeof(szBuffer), &dwBytesWrite, NULL);
//显示所有资源目录块的信息
ProcessRes(lpFile, (PBYTE)lpstRES_DIR, lpstRES_DIR, 1);
}
运行:
图7-6 遍历资源表
提示
1.对于命名的资源类型,输出字符串名称。
资源类型:AFX_DIALOG_LAYOUT
RESULT_MODULE
---------------------------------------------------------
Name: RESULT_MODULE
文件偏移:00003AC0 (代码页=0804, 长度2字节)
ID: 1000
文件偏移:00003AB8 (代码页=0804, 长度2字节)
2.示例程序对于非资源内容(例如应用程序清单)归类于自定义资源类型。
资源类型:24 (自定义编号)
---------------------------------------------------------
ID: 1
文件偏移:00007DD0 (代码页=0409, 长度381字节)