尝试解析PE文件结构, 于是编写了此PE信息助手类, 暂时完成如下信息解析
1.导出表信息(Dll模块, 函数)
2.导入表信息(Dll模块, 函数)
3.资源表信息(字符串表, 版本信息, 清单信息)
CPEHelper.h
#pragma once
//
// @brief: PE文件解析助手类
// @copyright: Copyright 2024 FlameCyclone
// @license:
// @birth: Created by Visual Studio 2022 on 2024-02-04
// @version: V1.0.0
// @revision: last revised by FlameCyclone on 2024-02-04
//
#include <stdint.h>
#include <wtypesbase.h>
#include <windows.h>
#include <string>
#include <vector>
#include <map>
#ifdef _UNICODE
using _tstring = std::wstring;
#else
using _tstring = std::string;
#endif
#pragma pack(push)
#pragma pack(1)
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/newheader
typedef struct {
WORD Reserved; //保留;必须为零
WORD ResType; //资源类型 1: RES_ICON 2: RES_CURSOR
WORD ResCount; //资源组中的图标或游标组件数
} ICON_GROUP_HEADER, * LPICON_GROUP_HEADER;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/resdir
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/iconresdir
typedef struct {
BYTE Width; //图标的宽度(以像素为单位)。 可接受的值为 16、32 和 64
BYTE Height; //图标的高度(以像素为单位)。 可接受的值为 16、32 和 64
BYTE ColorCount; //图标中的颜色数。 可接受的值为 2、8 和 16。
BYTE reserved; //保留;必须设置为与图标文件标头中保留字段的值相同的值
WORD Planes; //图标或光标位图中的颜色平面数
WORD BitCount; //图标或光标位图中每像素的位数
DWORD BytesInRes; //资源的大小(以字节为单位)
WORD IconId; //具有唯一序号标识符的图标或光标
} ICON_ENTRY, * LPICON_ENTRY;
typedef struct {
ICON_GROUP_HEADER Header; //图标组头部
ICON_ENTRY IconEntry[1]; //单个图标信息
}ICON_GROUP_DIR, * LPICON_GROUP_DIR;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/var-str
typedef struct {
WORD wLength; // Var 结构的长度(以字节为单位)
WORD wValueLength; // Value 成员的长度(以字节为单位)
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[12]; // Unicode 字符串 L“Translation”
WORD Padding; // 在 32 位边界上对齐 Value 成员所需的任意或零个 WORD
struct {
WORD LanguageID; //低序字: Microsoft 语言标识符
WORD CodePageID; //高序字: IBM 代码页码
}Value[1];
} Var;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/varfileinfo
typedef struct {
WORD wLength; // 整个 VarFileInfo 块(包括 Children 成员指示的所有结构)的长度(以字节为单位)
WORD wValueLength; // 此成员始终等于零
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[12]; // Unicode 字符串 L“VarFileInfo”
WORD Padding; // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD
Var Children[1]; // 通常包含应用程序或 DLL 支持的语言列表
} VarFileInfo;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/string-str
typedef struct {
WORD wLength; // 此 字符串 结构的长度(以字节为单位)
WORD wValueLength; // Value 成员的大小(以字为单位)
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[1]; // 任意长度的 Unicode 字符串
WORD Padding; // 在 32 位边界上对齐 Value 成员所需的任意或零个 WORD
WORD Value[1]; // 以零结尾的字符串
} String;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/stringtable
typedef struct {
WORD wLength; // 此 StringTable 结构的长度(以字节为单位),包括 Children 成员指示的所有结构
WORD wValueLength; // 此成员始终等于零
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[8]; // 存储为 Unicode 字符串的 8 位十六进制数
WORD Padding; // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD
String Children[1]; // 一个或多个 String 结构的数组
} StringTable;
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/stringfileinfo
typedef struct {
WORD wLength; // 整个 StringFileInfo 块的长度(以字节为单位)
WORD wValueLength; // 此成员始终等于零
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[14]; // Unicode 字符串 L“StringFileInfo”
WORD Padding; // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD
StringTable Children[1]; // 一个或多个 StringTable 结构的数组
} StringFileInfo;
// VS 文件版本信息
// https://learn.microsoft.com/zh-cn/windows/win32/menurc/vs-versioninfo
typedef struct {
WORD wLength; // VS_VERSIONINFO 结构的长度(以字节为单位),此长度不包括在 32 位边界上对齐任何后续版本资源数据的填充
WORD wValueLength; // Value 成员的长度(以字节为单位)
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
WCHAR szKey[15]; // Unicode 字符串 L“VS_VERSION_INFO”
WORD Padding1; // 在 32 位边界上对齐 Children 成员所需的任意或零个 WORD
//VS_FIXEDFILEINFO Value
//WORD Padding2
//WORD Children
} VS_VERSIONINFO, *PVS_VERSIONINFO;
#pragma pack(pop)
typedef struct _RESOURCE_ITEM
{
WORD ID; //资源ID
WORD LangID; //语言ID
DWORD OffsetToData; //数据偏移
DWORD Size; //数据大小
DWORD CodePage; //代码页
_RESOURCE_ITEM()
{
memset(this, 0, sizeof(this));
}
}RESOURCE_ITEM;
typedef struct _STRING_TEXT
{
_tstring StrText; //文本内容
WORD ID; //字符串ID
_STRING_TEXT()
:
ID(0)
{
}
}STRING_TEXT;
typedef struct _RESOURCE_GROUP_INFO
{
_tstring TypeName; //类型名
WORD TypeID; //类型ID
std::vector<RESOURCE_ITEM> Items; //资源信息
_RESOURCE_GROUP_INFO()
:
TypeID(0)
{
}
}RESOURCE_GROUP_INFO;
typedef struct _STRING_INFO
{
_tstring Text;
WORD wType;
_STRING_INFO()
:
wType(0)
{
}
}STRING_INFO;
typedef struct _STRING_FILE_ITEM
{
_tstring Key; // 值名字符串
_tstring Value; // 值数据字符串
WORD wType; // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
std::vector<uint8_t> Data; // 二进制数据
_STRING_FILE_ITEM()
:
wType(0)
{
}
}STRING_FILE_ITEM;
// 版本资源中的数据, 包含可为特定语言和代码页显示的版本信息
typedef struct
{
_tstring StringCode;
std::vector<STRING_FILE_ITEM> StringInfos;
}STRING_FILE_INFO;
typedef struct _TRANSLATION_INFO
{
WORD LanguageID;
WORD CodePageID;
_TRANSLATION_INFO()
:
LanguageID(0),
CodePageID(0)
{
}
}TRANSLATION_INFO;
// 版本信息
typedef struct
{
VS_FIXEDFILEINFO FixedFileInfo;
std::vector<STRING_FILE_INFO> StringFileInfo; // 版本资源中的数据的组织
std::vector<TRANSLATION_INFO> TranslationList; // 应用程序或 DLL 支持的语言列表
}VERSION_INFO;
typedef struct _IMPORT_FUNCTION_INFO
{
_tstring Name; //函数名
WORD Hint; //索引, 可能为0
_IMPORT_FUNCTION_INFO()
:
Hint(0)
{
}
}IMPORT_FUNCTION_INFO;
typedef struct _EXPORT_FUNCTION_INFO
{
_tstring Name; //函数名
_tstring ForwarderName; //转发函数名
DWORD Addr; //函数相对偏移地址
DWORD Ordinal; //函数顺序
_EXPORT_FUNCTION_INFO()
:
Addr(0),
Ordinal(0)
{
}
}EXPORT_FUNCTION_INFO;
typedef struct _RESOURCE_INFO
{
std::vector<RESOURCE_GROUP_INFO> ResourceTable; //资源表信息
std::map<WORD, std::vector<STRING_TEXT>> StringTable; //资源字符串表
VERSION_INFO VersionInfo; //资源版本信息
_tstring Manifest; //资源清单
void clear()
{
ResourceTable.clear();
StringTable.clear();
VersionInfo.StringFileInfo.clear();
VersionInfo.TranslationList.clear();
memset(&VersionInfo.FixedFileInfo, 0, sizeof(VersionInfo.FixedFileInfo));
}
}RESOURCE_INFO;
class CPEHelper
{
public:
CPEHelper();
~CPEHelper();
//
// @brief: 加载PE文件信息
// @param: strPath 文件路径
// @param: fCheckSum 检查映像文件校验和, 校验失败此函数将直接返回false
// @param: fByModule 是否通过模块加载
// @ret: bool 操作成功与否
bool LoadFile(
const _tstring& strPath,
bool fCheckSum = false,
bool fByModule = false
);
//
// @brief: 关闭文件占用
// @ret: bool 操作成功与否
void Close();
//
// @brief: 获取导入表信息
// @ret: std::map<_tstring, std::vector<IMPORT_FUNCTION_INFO>> 导入表信息
const std::map<_tstring, std::vector<IMPORT_FUNCTION_INFO>>& GetImportTable() const;
//
// @brief: 获取导出表信息
// @ret: std::map<_tstring, std::vector<IMPORT_FUNCTION_INFO>> 导出表信息
const std::map<_tstring, std::vector<EXPORT_FUNCTION_INFO>>& GetExportTable() const;
//
// @brief: 获取资源表信息
// @ret: RESOURCE_INFO 资源信息
const RESOURCE_INFO& GetResourceInfo() const;
//
// @brief: 打印导出表信息
// @param: fShowModule 显示模块信息
// @param: fShowFunList 显示函数信息
// @ret: void
void PrintExportTable(bool fShowModule = true, bool fShowFunList = true);
//
// @brief: 打印导入表信息
// @param: fShowModule 显示模块信息
// @param: fShowFunList 显示函数信息
// @ret: void
void PrintImportTable(bool fShowModule = true, bool fShowFunList = true);
//
// @brief: 打印资源表信息
// @param: fShowDetail 显示详情
// @ret: void
void PrintResourceTable(bool fShowDetail = true);
//
// @brief: 打印版本信息
// @ret: void
void PrintVersion();
//
// @brief: 打印字符串表
// @ret: void
void PrintStringTable();
//
// @brief: 打印清单信息
// @ret: void
void PrintManifest();
private:
//
// @brief: 加载PE文件信息
// @param: strPath 文件路径
// @param: fCheckSum 检查映像文件校验和, 校验失败此函数将直接返回false
// @ret: bool 操作成功与否
bool _LoadByFile(
const _tstring& strPath,
bool fCheckSum = false
);
//
// @brief: 加载PE文件信息
// @param: strPath 文件路径
// @param: fCheckSum 检查映像文件校验和, 校验失败此函数将直接返回false
// @ret: bool 操作成功与否
bool _LoadByModule(
const _tstring& strPath,
bool fCheckSum = false
);
//
// @brief: 清空数据
// @ret: void
void _Clear();
//
// @brief: 输出字节信息
// @ret: void
void _PrintfByte(LPVOID lpData, size_t size);
//
// @brief: 读取文件
// @param: lpBuffer 读取数据存放缓冲
// @param: dwSize 缓冲大小(字节)
// @param: lpBytesRead 实际读取大小(字节)
// @param: llPos 读取文件数据位置
// @param: dwFlag 设置位置标志 FILE_BEGIN: 文件开头 FILE_CURRENT: 当前文件指针 FILE_END: 文件结束位置
// @param: fCheckResdSize 是否检查读取长度
// @ret: bool 操作成功与否
bool _ReadFile(
LPVOID lpBuffer,
DWORD dwSize,
LPDWORD lpBytesRead = nullptr,
LONGLONG llPos = 0,
DWORD dwFlag = FILE_CURRENT,
bool fCheckResdSize = false
);
//
// @brief: 获取虚拟地址在节数据中的相对偏移
// @param: pSectionHeader 节信息头
// @param: ullVirtualAddr 虚拟地址
// @ret: LONGLONG 虚拟地址在节数据中的相对偏移
LONGLONG _GetSectionDataOffset(
const PIMAGE_SECTION_HEADER lpSectionHeader,
ULONGLONG ullVirtualAddr
);
//
// @brief: 获取虚拟地址在节数据内存中的位置
// @param: lpBase 节数据内存位置
// @param: pSectionHeader 节信息头
// @param: ullVirtualAddr 虚拟地址
// @ret: LONGLONG 虚拟地址在节数据内存中的位置
LPVOID _GetSectionDataAddr(
LPCVOID lpBase,
const PIMAGE_SECTION_HEADER pSectionHeader,
ULONGLONG ullVirtualAddr
);
//
// @brief: 获取虚拟地址在节数据内存中的位置
// @param: ullVirtualAddr 虚拟地址
// @param: pSectinHeader 节信息头输出缓冲
// @ret: bool 操作成功与否
bool _GetSectionHeader(
ULONGLONG ullVirtualAddr,
PIMAGE_SECTION_HEADER pSectinHeader
);
//
// @brief: 获取地址对齐后的地址
// @param: lpBase 起始地址
// @param: lpAddr 地址
// @param: dwAlign 对齐粒度
// @ret: LPBYTE 对齐后指针
LPBYTE _GetAlignAddr(
LPVOID lpBase,
LPCVOID lpAddr,
DWORD dwAlign = sizeof(DWORD)
);
//
// @brief: 加载所有信息
// @ret: bool 操作成功与否
bool _LoadAllInformation();
//
// @brief: 获取目录节数据与节标头信息
// @param: nDirectortIndex 目录索引
// @param: lppSection 节数据指针输出指针
// @param: lpSectionHeader 节标头数据输出指针
// @ret: bool 操作成功与否
bool _GetDirectorySection(
int nDirectortIndex,
LPBYTE* lppSection,
PIMAGE_SECTION_HEADER lpSectionHeader
);
//
// @brief: 加载导出表
// @param: lpSection 节数据
// @param: ullVirtualAddr 目录虚拟地址
// @param: lpSectionHeader 节标头数据输出指针
// @ret: bool 操作成功与否
bool _LoadExportTable(
LPCBYTE lpSection,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
);
//
// @brief: 加载导入表
// @param: lpSection 节数据
// @param: ullVirtualAddr 目录虚拟地址
// @param: lpSectionHeader 节标头
// @ret: bool 操作成功与否
bool _LoadImportTable(
LPCBYTE lpSection,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
);
//
// @brief: 加载资源表
// @param: lpSection 节数据
// @param: ullVirtualAddr 目录虚拟地址
// @param: lpSectionHeader 节标头
// @ret: bool 操作成功与否
bool _LoadResourceTable(
LPCBYTE lpSection,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
);
//
// @brief: 加载资源信息
// @param: lpSection 节数据内存指针
// @ret: bool 操作成功与否
bool _LoadResourceInformation(
LPCBYTE lpSectionData,
const PIMAGE_SECTION_HEADER lpSectionHeader
);
//
// @brief: 加载资源字符串表
// @param: lpSection 节数据内存指针
// @ret: info 资源组信息
// @ret: bool 操作成功与否
bool _LoadResourceStringTable(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
);
//
// @brief: 加载资源Manifest
// @param: lpSection 节数据内存指针
// @ret: info 资源组信息
// @ret: bool 操作成功与否
bool _LoadResourceManifest(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
);
//
// @brief: 加载资源版本信息
// @param: lpSection 节数据内存指针
// @ret: info 资源组信息
// @ret: bool 操作成功与否
bool _LoadResourceVersion(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
);
private:
HANDLE m_hFile; //PE文件句柄
HMODULE m_hModule; //PE文件模块句柄
LPBYTE m_pImageData; //数据地址
//基础PE信息
IMAGE_DOS_HEADER m_DosHeader; //Dos头
IMAGE_NT_HEADERS32 m_NtHeader32; //NT头(32位)
IMAGE_NT_HEADERS64 m_NtHeader64; //NT头(64位)
IMAGE_DATA_DIRECTORY m_DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; //数据目录
std::vector<IMAGE_SECTION_HEADER> m_SectionHeader; //节头信息
std::vector<IMAGE_RESOURCE_DIRECTORY_ENTRY> m_ResourceDirectoryEntrys; //资源目录信息
//数据目录解析相关成员
std::map<_tstring, std::vector<IMPORT_FUNCTION_INFO>> m_ImportTable; //导入表信息
std::map<_tstring, std::vector<EXPORT_FUNCTION_INFO>> m_ExportTable; //导出表信息
RESOURCE_INFO m_ResourceInfo; //资源信息
WORD m_OptionalHeaderMagic; //可选头魔数
};
CPEHelper.cpp
#include "CPEHelper.h"
#include <tchar.h>
#include <imagehlp.h>
#define LDR_IS_DATAFILE(_handle) (((ULONG_PTR)(_handle)) & (ULONG_PTR)1)
#define LDR_IS_IMAGEMAPPING(_handle) (((ULONG_PTR)(_handle)) & (ULONG_PTR)2)
#define LDR_IS_RESOURCE(_handle) (LDR_IS_IMAGEMAPPING(_handle) || LDR_IS_DATAFILE(_handle))
#pragma comment(lib, "Imagehlp.lib")
static LPCTSTR g_ResourctTypeName[] = {
_T("RT_NONE"),
_T("Cursor"),
_T("Bitmap"),
_T("Icon"),
_T("Menu"),
_T("Dialog"),
_T("String Table"),
_T("Font dir"),
_T("Font"),
_T("Accelerator"),
_T("Application-defined resource (raw data"),
_T("Message-table entry"),
_T("Cursor Group"),
_T("RT_NONE"),
_T("Icon Group"),
_T("RT_NONE"),
_T("Version Info"),
_T("RT_DLGINCLUDE"),
_T("RT_NONE"),
_T("Plug and Play resource"),
_T("VXD"),
_T("Animated cursor"),
_T("Animated icon"),
_T("Html"),
_T("Manifest")
};
static std::string _WStrToMultiStr(UINT CodePage, const std::wstring& str)
{
//计算缓冲区所需的字节长度
int cbMultiByte = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, 0);
std::string strResult(cbMultiByte, 0);
//成功则返回写入到指示的缓冲区的字节数
size_t nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size(), NULL, NULL);
//调整内容长度
strResult.resize(nConverted);
return strResult;
}
static std::wstring _MultiStrToWStr(UINT CodePage, const std::string& str)
{
//计算缓冲区所需的字符长度
int cchWideChar = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, 0);
std::wstring strResult(cchWideChar, 0);
//成功则返回写入到指示的缓冲区的字符数
size_t nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), (int)str.size(), &strResult[0], (int)strResult.size());
//调整内容长度
strResult.resize(nConverted);
return strResult;
}
static std::string _WStrToAStr(const std::wstring& str)
{
return _WStrToMultiStr(CP_ACP, str);
}
static std::string _WStrToU8Str(const std::wstring& str)
{
return _WStrToMultiStr(CP_UTF8, str);
}
static _tstring _WStrToTStr(const std::wstring& str)
{
#ifdef _UNICODE
return str;
#else
return _WStrToMultiStr(CP_ACP, str);
#endif
}
static std::wstring _AStrToWStr(const std::string& str)
{
return _MultiStrToWStr(CP_ACP, str);
}
static std::string _AStrToU8Str(const std::string& str)
{
return _WStrToU8Str(_AStrToWStr(str));
}
static _tstring _AStrToTStr(const std::string& str)
{
#ifdef _UNICODE
return _MultiStrToWStr(CP_ACP, str);
#else
return str;
#endif
}
static std::wstring _U8StrToWStr(const std::string& str)
{
return _MultiStrToWStr(CP_UTF8, str);
}
static std::string _U8StrToAStr(const std::string& str)
{
return _WStrToAStr(_U8StrToWStr(str));
}
static _tstring _U8StrToTStr(const std::string& str)
{
#ifdef _UNICODE
return _MultiStrToWStr(CP_UTF8, str);
#else
return _WStrToAStr(_U8StrToWStr(str));
#endif
}
static std::string _TStrToAStr(const _tstring& str)
{
#ifdef _UNICODE
return _WStrToMultiStr(CP_ACP, str);
#else
return str;
#endif
}
static std::wstring _TStrToWStr(const _tstring& str)
{
#ifdef _UNICODE
return str;
#else
return _AStrToWStr(str);
#endif
}
static std::string _TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODE
return _WStrToU8Str(str);
#else
return _WStrToU8Str(_AStrToWStr(str));
#endif
}
const int FORMAT_COUNT_MAX = (1024 * 1024 * 64);
static void ConsoleOutputA(LPCSTR pFormat, ...)
{
size_t nCchCount = MAX_PATH;
std::string strResult(nCchCount, 0);
va_list args;
va_start(args, pFormat);
do
{
//格式化输出字符串
int nSize = _vsnprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
if (-1 != nSize)
{
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
::WriteConsoleA(console, strResult.c_str(), nSize, NULL, NULL);
break;
}
//缓冲大小超限终止
if (nCchCount >= FORMAT_COUNT_MAX)
{
break;
}
//重新分配缓冲
nCchCount *= 2;
strResult.resize(nCchCount);
} while (true);
va_end(args);
}
static void ConsoleOutputW(LPCWSTR pFormat, ...)
{
size_t nCchCount = MAX_PATH;
std::wstring strResult(nCchCount, 0);
va_list args;
va_start(args, pFormat);
do
{
//格式化输出字符串
int nSize = _vsnwprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
if (-1 != nSize)
{
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
::WriteConsoleW(console, strResult.c_str(), nSize, NULL, NULL);
break;
}
//缓冲大小超限终止
if (nCchCount >= FORMAT_COUNT_MAX)
{
break;
}
//重新分配缓冲
nCchCount *= 2;
strResult.resize(nCchCount);
} while (true);
va_end(args);
}
static void ConsoleOutput(LPCTSTR pFormat, ...)
{
size_t nCchCount = MAX_PATH;
_tstring strResult(nCchCount, 0);
va_list args;
va_start(args, pFormat);
do
{
//格式化输出字符串
int nSize = _vsntprintf_s(&strResult[0], nCchCount, _TRUNCATE, pFormat, args);
if (-1 != nSize)
{
HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE);
::WriteConsole(console, strResult.c_str(), nSize, NULL, NULL);
break;
}
//缓冲大小超限终止
if (nCchCount >= FORMAT_COUNT_MAX)
{
break;
}
//重新分配缓冲
nCchCount *= 2;
strResult.resize(nCchCount);
} while (true);
va_end(args);
}
CPEHelper::CPEHelper()
:
m_hFile(INVALID_HANDLE_VALUE),
m_hModule(NULL)
{
_Clear();
}
CPEHelper::~CPEHelper()
{
Close();
}
bool CPEHelper::LoadFile(
const _tstring& strPath,
bool fCheckSum/* = false*/,
bool fByModule/* = false*/
)
{
bool bResult = false;
Close();
_Clear();
if (fByModule)
{
bResult = _LoadByModule(strPath, fCheckSum);
}
else
{
bResult = _LoadByFile(strPath, fCheckSum);
}
if (bResult)
{
_LoadAllInformation();
}
else
{
Close();
}
return bResult;
}
bool CPEHelper::_LoadByFile(
const _tstring& strPath,
bool fCheckSum/* = false*/
)
{
bool bResult = false;
// 读取文件方式解析
m_hFile = ::CreateFile(
strPath.c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL
);
if (INVALID_HANDLE_VALUE == m_hFile)
{
return false;
}
do
{
DWORD HeaderSum = 0;
DWORD CheckSum = 0;
// 计算校验和
if (fCheckSum && (CHECKSUM_SUCCESS != ::MapFileAndCheckSum(strPath.c_str(), &HeaderSum, &CheckSum)))
{
break;
}
// 文件头中存在校验和才进行检查
if (0 != HeaderSum && HeaderSum != CheckSum)
{
break;
}
// 读取 DOS 头 64字节
IMAGE_DOS_HEADER dosHeader = { 0 };
DWORD dwBytesRead = 0;
if (!_ReadFile(&dosHeader, sizeof(dosHeader), &dwBytesRead, 0, FILE_BEGIN, true) || IMAGE_DOS_SIGNATURE != dosHeader.e_magic)
{
break;
}
// 读取 NT头(32位版本) 248字节
IMAGE_NT_HEADERS32 ntHeader32 = { 0 };
if (!_ReadFile(&ntHeader32, sizeof(ntHeader32), &dwBytesRead, dosHeader.e_lfanew, FILE_BEGIN, true))
{
break;
}
// 读取 NT头(64位版本) 264字节
IMAGE_NT_HEADERS64 ntHeader64 = { 0 };
if (!_ReadFile(&ntHeader64, sizeof(ntHeader64), &dwBytesRead, dosHeader.e_lfanew, FILE_BEGIN, true))
{
break;
}
// 检查 NT头 签名
if (IMAGE_NT_SIGNATURE != ntHeader32.Signature)
{
break;
}
// 检查 是否为 32位程序可选头
if (IMAGE_NT_OPTIONAL_HDR32_MAGIC == ntHeader32.OptionalHeader.Magic)
{
m_OptionalHeaderMagic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
m_NtHeader32 = ntHeader32;
memcpy(&m_DataDirectory, &m_NtHeader32.OptionalHeader.DataDirectory, sizeof(m_DataDirectory));
}
// 检查 是否为 64位程序可选头
else if (IMAGE_NT_OPTIONAL_HDR64_MAGIC == ntHeader64.OptionalHeader.Magic)
{
m_OptionalHeaderMagic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
m_NtHeader64 = ntHeader64;
memcpy(&m_DataDirectory, &m_NtHeader64.OptionalHeader.DataDirectory, sizeof(m_DataDirectory));
}
// ROM可选头
else if (IMAGE_ROM_OPTIONAL_HDR_MAGIC == ntHeader32.OptionalHeader.Magic)
{
m_OptionalHeaderMagic = IMAGE_ROM_OPTIONAL_HDR_MAGIC;
}
else
{
break;
}
// 保存Dos头
m_DosHeader = dosHeader;
// 节标头偏移
DWORD dwSectionOffset = (DWORD)(dosHeader.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + ntHeader32.FileHeader.SizeOfOptionalHeader);
WORD wNumberOfSections = ntHeader32.FileHeader.NumberOfSections;
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMAGE_SECTION_HEADER) * wNumberOfSections);
if (nullptr == pSectionHeader)
{
break;
}
//保存标头信息
do
{
if (!_ReadFile(pSectionHeader, sizeof(IMAGE_SECTION_HEADER) * wNumberOfSections, &dwBytesRead, dwSectionOffset, FILE_BEGIN, true))
{
break;
}
for (int i = 0; i < wNumberOfSections; i++)
{
m_SectionHeader.push_back(pSectionHeader[i]);
}
} while (false);
// 释放节标头缓冲
if (pSectionHeader)
{
::HeapFree(::GetProcessHeap(), 0, pSectionHeader);
}
bResult = true;
} while (false);
return bResult;
}
bool CPEHelper::_LoadByModule(
const _tstring& strPath,
bool fCheckSum/* = false*/
)
{
bool bResult = false;
// 资源模块加载方式解析
DWORD dwFlags = LOAD_LIBRARY_AS_DATAFILE;
// 先尝试 数据文件 模式加载
m_hModule = ::LoadLibraryEx(strPath.c_str(), 0, dwFlags);
if (NULL == m_hModule)
{
// 加载失败则尝试 数据文件 + 映像资源 模式加载
dwFlags |= LOAD_LIBRARY_AS_IMAGE_RESOURCE;
m_hModule = ::LoadLibraryEx(strPath.c_str(), 0, dwFlags);
}
if (NULL == m_hModule)
{
return false;
}
do
{
DWORD HeaderSum = 0;
DWORD CheckSum = 0;
// 计算校验和
if (fCheckSum && (CHECKSUM_SUCCESS != ::MapFileAndCheckSum(strPath.c_str(), &HeaderSum, &CheckSum)))
{
break;
}
// 文件头中存在校验和才进行检查
if (0 != HeaderSum && HeaderSum != CheckSum)
{
break;
}
LPBYTE pHeader = (BYTE*)m_hModule;
m_pImageData = (BYTE*)((ULONG_PTR)pHeader & ~((ULONG_PTR)0x03));
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)m_pImageData;
// 读取 DOS 头 64字节
if (IMAGE_DOS_SIGNATURE != pDosHeader->e_magic)
{
break;
}
PIMAGE_NT_HEADERS pNtHeader = (IMAGE_NT_HEADERS*)((BYTE*)(pDosHeader)+(DWORD)(pDosHeader->e_lfanew));
// 检查 NT头 签名
if (IMAGE_NT_SIGNATURE != pNtHeader->Signature)
{
break;
}
// 检查 是否为 32位程序可选头
if (IMAGE_NT_OPTIONAL_HDR32_MAGIC == pNtHeader->OptionalHeader.Magic)
{
m_NtHeader32 = *((PIMAGE_NT_HEADERS32)pNtHeader);
memcpy(&m_DataDirectory, &m_NtHeader32.OptionalHeader.DataDirectory, sizeof(m_DataDirectory));
}
// 检查 是否为 64位程序可选头
else if (IMAGE_NT_OPTIONAL_HDR64_MAGIC == pNtHeader->OptionalHeader.Magic)
{
m_OptionalHeaderMagic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
m_NtHeader64 = *((PIMAGE_NT_HEADERS64)pNtHeader);
memcpy(&m_DataDirectory, &m_NtHeader64.OptionalHeader.DataDirectory, sizeof(m_DataDirectory));
}
// ROM可选头
else if (IMAGE_ROM_OPTIONAL_HDR_MAGIC == pNtHeader->OptionalHeader.Magic)
{
m_OptionalHeaderMagic = IMAGE_ROM_OPTIONAL_HDR_MAGIC;
}
else
{
break;
}
m_OptionalHeaderMagic = pNtHeader->OptionalHeader.Magic;
// 保存Dos头
m_DosHeader = *pDosHeader;
// 节标头偏移
DWORD dwSectionOffset = (DWORD)(m_DosHeader.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader);
WORD wNumberOfSections = pNtHeader->FileHeader.NumberOfSections;
PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(m_pImageData + dwSectionOffset);
//保存标头信息
do
{
for (int i = 0; i < wNumberOfSections; i++)
{
m_SectionHeader.push_back(pSectionHeader[i]);
}
} while (false);
bResult = true;
} while (false);
return bResult;
}
void CPEHelper::Close()
{
if (INVALID_HANDLE_VALUE != m_hFile)
{
::CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
if (NULL != m_hModule)
{
::FreeLibrary(m_hModule);
m_hModule = NULL;
}
}
const std::map<_tstring, std::vector<IMPORT_FUNCTION_INFO>>& CPEHelper::GetImportTable() const
{
return m_ImportTable;
}
const std::map<_tstring, std::vector<EXPORT_FUNCTION_INFO>>& CPEHelper::GetExportTable() const
{
return m_ExportTable;
}
const RESOURCE_INFO& CPEHelper::GetResourceInfo() const
{
return m_ResourceInfo;
}
void CPEHelper::_Clear()
{
memset(&m_DosHeader, 0, sizeof(m_DosHeader));
memset(&m_NtHeader32, 0, sizeof(m_NtHeader32));
memset(&m_NtHeader64, 0, sizeof(m_NtHeader64));
memset(&m_DataDirectory, 0, sizeof(m_DataDirectory));
m_ResourceDirectoryEntrys.clear();
m_SectionHeader.clear();
m_ImportTable.clear();
m_ExportTable.clear();
m_ResourceInfo.clear();
m_OptionalHeaderMagic = 0;
m_pImageData = NULL;
}
void CPEHelper::_PrintfByte(LPVOID lpData, size_t size)
{
if (NULL == lpData)
{
return;
}
for (size_t i = 0; i < size; i++)
{
if (i != size - 1)
{
ConsoleOutput(_T("%02X "), ((LPBYTE)lpData)[i]);
}
else
{
ConsoleOutput(_T("%02X"), ((LPBYTE)lpData)[i]);
}
}
}
bool CPEHelper::_ReadFile(
LPVOID lpBuffer,
DWORD dwSize,
LPDWORD lpBytesRead/* = nullptr*/,
LONGLONG llPos/* = 0*/,
DWORD dwFlag/* = FILE_CURRENT*/,
bool fCheckResdSize/* = false*/
)
{
LARGE_INTEGER liDistanceToMove = { 0 };
DWORD dwBytesRead = 0;
bool bResult = FALSE;
if (INVALID_HANDLE_VALUE == m_hFile)
{
return false;
}
liDistanceToMove.QuadPart = llPos;
::SetFilePointerEx(m_hFile, liDistanceToMove, NULL, dwFlag);
bResult = ::ReadFile(m_hFile, lpBuffer, dwSize, &dwBytesRead, NULL);
if (nullptr != lpBytesRead)
{
*lpBytesRead = dwBytesRead;
}
//设置了读取大小检查, 则当实际读取数据量与指定读取数据量相同才认为读取成功
if (fCheckResdSize)
{
bResult = (dwBytesRead == dwSize);
}
return bResult;
}
LONGLONG CPEHelper::_GetSectionDataOffset(
const PIMAGE_SECTION_HEADER lpSectionHeader,
ULONGLONG ullVirtualAddr
)
{
ULONGLONG VirtualAddrBegin = lpSectionHeader->VirtualAddress;
ULONGLONG VirtualAddrEnd = VirtualAddrBegin + lpSectionHeader->SizeOfRawData;
ULONGLONG ullOffset = ullVirtualAddr;
if (ullOffset < VirtualAddrBegin || ullOffset >= VirtualAddrEnd)
{
return -1;
}
ullOffset -= VirtualAddrBegin;
return ullOffset;
}
LPVOID CPEHelper::_GetSectionDataAddr(
LPCVOID lpBase,
const PIMAGE_SECTION_HEADER pSectionHeader,
ULONGLONG ullVirtualAddr
)
{
ULONGLONG VirtualAddrBegin = pSectionHeader->VirtualAddress;
ULONGLONG VirtualAddrEnd = VirtualAddrBegin + pSectionHeader->SizeOfRawData;
ULONGLONG ullOffset = VirtualAddrBegin + ullVirtualAddr - pSectionHeader->VirtualAddress;
if (ullOffset < VirtualAddrBegin || ullOffset >= VirtualAddrEnd)
{
return NULL;
}
ullOffset -= VirtualAddrBegin;
return (LPBYTE)lpBase + ullOffset;
}
bool CPEHelper::_GetSectionHeader(
ULONGLONG ullVirtualAddr,
PIMAGE_SECTION_HEADER pSectinHeader
)
{
for (const auto& item : m_SectionHeader)
{
ULONGLONG VirtualAddrBegin = item.VirtualAddress;
ULONGLONG VirtualAddrEnd = item.VirtualAddress + item.SizeOfRawData;
if ((ullVirtualAddr >= VirtualAddrBegin) && (ullVirtualAddr < VirtualAddrEnd))
{
if (pSectinHeader)
{
*pSectinHeader = item;
}
return true;
}
}
return false;
}
inline LPBYTE CPEHelper::_GetAlignAddr(
LPVOID lpBase,
LPCVOID lpAddr,
DWORD dwAlign
)
{
DWORD_PTR dwOffset = 0;
if (dwAlign)
{
dwOffset = (DWORD_PTR)lpBase & (dwAlign - 1);
}
DWORD_PTR dwPadding = ((DWORD_PTR)lpAddr - dwOffset) % dwAlign;
return dwPadding ? (LPBYTE)lpAddr + (dwAlign - dwPadding) : (LPBYTE)lpAddr;
}
bool CPEHelper::_LoadAllInformation()
{
// 加载导入表
auto _FunImportTable = [this]() {
IMAGE_SECTION_HEADER sectionHeaderImportTable = { 0 };
LPBYTE lpSectionImportTable = NULL;
if (_GetDirectorySection(IMAGE_DIRECTORY_ENTRY_IMPORT, &lpSectionImportTable, §ionHeaderImportTable))
{
_LoadImportTable(lpSectionImportTable, m_DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, §ionHeaderImportTable);
if (INVALID_HANDLE_VALUE != m_hFile)
{
::HeapFree(::GetProcessHeap(), 0, lpSectionImportTable);
}
}
};
// 加载导出表
auto _FunLoadExportTable = [this]() {
IMAGE_SECTION_HEADER sectionHeaderExportTable = { 0 };
LPBYTE lpSectionExportTable = NULL;
if (_GetDirectorySection(IMAGE_DIRECTORY_ENTRY_EXPORT, &lpSectionExportTable, §ionHeaderExportTable))
{
_LoadExportTable(lpSectionExportTable, m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, §ionHeaderExportTable);
if (INVALID_HANDLE_VALUE != m_hFile)
{
::HeapFree(::GetProcessHeap(), 0, lpSectionExportTable);
}
}
};
// 加载资源表
auto _FunLoadResourceTable = [this]() {
IMAGE_SECTION_HEADER sectionHeaderResourceTable = { 0 };
LPBYTE lpSectionResourceTable = NULL;
if (_GetDirectorySection(IMAGE_DIRECTORY_ENTRY_RESOURCE, &lpSectionResourceTable, §ionHeaderResourceTable))
{
_LoadResourceTable(lpSectionResourceTable, m_DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress, §ionHeaderResourceTable);
if (INVALID_HANDLE_VALUE != m_hFile)
{
::HeapFree(::GetProcessHeap(), 0, lpSectionResourceTable);
}
}
};
_FunImportTable();
_FunLoadExportTable();
_FunLoadResourceTable();
return true;
}
bool CPEHelper::_GetDirectorySection(
int nDirectortIndex,
LPBYTE* lppSection,
PIMAGE_SECTION_HEADER lpSectionHeader
)
{
if (NULL == lppSection)
{
return false;
}
DWORD VirtualAddress = m_DataDirectory[nDirectortIndex].VirtualAddress;
if (0 == VirtualAddress)
{
return false;
}
if (INVALID_HANDLE_VALUE != m_hFile)
{
LPBYTE lpSectionData = nullptr;
bool fResult = false;
do
{
// 获取虚拟地址所在节信息
if (!_GetSectionHeader(VirtualAddress, lpSectionHeader))
{
break;
}
//分配节数据缓冲
lpSectionData = (LPBYTE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, lpSectionHeader->SizeOfRawData);
if (NULL == lpSectionData)
{
break;
}
// 读取整个节数据
DWORD dwReadBytes = 0;
if (!_ReadFile(lpSectionData, lpSectionHeader->SizeOfRawData, &dwReadBytes, lpSectionHeader->PointerToRawData, FILE_BEGIN, true))
{
break;
}
fResult = true;
} while (false);
if (!fResult && lpSectionData)
{
::HeapFree(::GetProcessHeap(), 0, lpSectionData);
}
if (fResult)
{
*lppSection = lpSectionData;
}
}
if (NULL != m_hModule)
{
do
{
// 获取虚拟地址所在节信息
if (!_GetSectionHeader(VirtualAddress, lpSectionHeader))
{
break;
}
if (LDR_IS_DATAFILE(m_hModule))
{
*lppSection = m_pImageData + lpSectionHeader->PointerToRawData;
}
else if (LDR_IS_IMAGEMAPPING(m_hModule))
{
*lppSection = m_pImageData + lpSectionHeader->VirtualAddress;
}
else
{
break;
}
} while (false);
}
return NULL != *lppSection;
}
bool CPEHelper::_LoadExportTable(
LPCBYTE lpSectionData,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
)
{
bool fResult = false;
if (!lpSectionData || !lpSectionHeader)
{
return false;
}
do
{
// 获取导出描述信息所在偏移
ULONGLONG VirtualAddrBegin = m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
ULONGLONG VirtualAddrEnd = VirtualAddrBegin + m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
LONGLONG ullExportDescOffset = _GetSectionDataOffset(lpSectionHeader, ullVirtualAddr);
if (-1 == ullExportDescOffset)
{
break;
}
// 获取导出目录信息位置
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)lpSectionData + ullExportDescOffset);
LPCSTR lpDllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pExportDirectory->Name);
if (NULL == lpDllName)
{
break;
}
// 遍历导出表
LPDWORD pFuncAddr = (LPDWORD)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pExportDirectory->AddressOfFunctions);
LPDWORD pNameAddr = (LPDWORD)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pExportDirectory->AddressOfNames);
LPWORD pNameOrdinalsName = (LPWORD)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pExportDirectory->AddressOfNameOrdinals);
if ((NULL == pFuncAddr) || (NULL == pNameAddr) || (NULL == pNameOrdinalsName))
{
break;
}
std::vector<EXPORT_FUNCTION_INFO> FunList;
//预先分配空间, 速度比每次 push_back 一个信息快
FunList.resize(pExportDirectory->NumberOfFunctions);
for (DWORD i = 0; i < pExportDirectory->NumberOfFunctions; i++)
{
FunList[i].Addr = pFuncAddr[i];
FunList[i].Ordinal = pExportDirectory->Base + i;
}
for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++)
{
LPCSTR lpFnName = (LPCSTR)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pNameAddr[i]);
if (NULL == lpFnName)
{
break;
}
DWORD dwOrdinal = pNameOrdinalsName[i];
FunList[dwOrdinal].Name = _AStrToTStr(lpFnName);
//转发函数
if ((FunList[dwOrdinal].Addr >= VirtualAddrBegin && FunList[dwOrdinal].Addr < VirtualAddrEnd))
{
LPCSTR lpForwarderName = (LPCSTR)_GetSectionDataAddr(lpSectionData, lpSectionHeader, FunList[dwOrdinal].Addr);
if (NULL == lpForwarderName)
{
break;
}
FunList[dwOrdinal].ForwarderName = _AStrToTStr(lpForwarderName);
}
}
m_ExportTable.emplace(_AStrToTStr(lpDllName), FunList);
fResult = true;
} while (false);
return fResult;
}
bool CPEHelper::_LoadImportTable(
LPCBYTE lpSectionData,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
)
{
IMAGE_IMPORT_DESCRIPTOR emptyImportDesc = { 0 };
bool fResult = false;
if (!lpSectionData || !lpSectionHeader)
{
return false;
}
do
{
// 获取导入描述信息所在偏移
LONGLONG ullImportDescOffset = _GetSectionDataOffset(lpSectionHeader, ullVirtualAddr);
if (-1 == ullImportDescOffset)
{
break;
}
// 导入描述
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)(LPBYTE)(lpSectionData + ullImportDescOffset);
// 32位导入表
if (IMAGE_NT_OPTIONAL_HDR32_MAGIC == m_OptionalHeaderMagic)
{
// 遍历导入表
for (DWORD i = 0; 0 != ::memcmp(pImportDesc + i, &emptyImportDesc, sizeof(emptyImportDesc)); i++)
{
LPCSTR lpDllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pImportDesc[i].Name);
if (NULL == lpDllName)
{
break;
}
PIMAGE_THUNK_DATA32 pThunkData = (PIMAGE_THUNK_DATA32)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pImportDesc[i].FirstThunk);
if (NULL == pThunkData)
{
break;
}
//先统计一下导入函数数量
PIMAGE_THUNK_DATA32 pThunkDataTmp = pThunkData;
size_t nThunkCount = 0;
while (pThunkDataTmp->u1.AddressOfData)
{
nThunkCount++;
pThunkDataTmp++;
}
std::vector<IMPORT_FUNCTION_INFO> FunList;
//预先分配空间, 速度比每次 push_back 一个信息快
FunList.resize(nThunkCount);
size_t nIndex = 0;
while (pThunkData->u1.AddressOfData)
{
if (IMAGE_SNAP_BY_ORDINAL32(pThunkData->u1.AddressOfData))
{
FunList[nIndex].Hint = IMAGE_ORDINAL32(pThunkData->u1.AddressOfData);
}
else
{
PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pThunkData->u1.AddressOfData);
if (NULL == pImportName)
{
break;
}
FunList[nIndex].Hint = pImportName->Hint;
FunList[nIndex].Name = _AStrToTStr((LPCSTR)&pImportName->Name);
}
nIndex++;
pThunkData++;
}
auto result = m_ImportTable.emplace(_AStrToTStr(lpDllName), FunList);
if (!result.second)
{
result.first->second.insert(result.first->second.end(), FunList.begin(), FunList.end());
}
}
}
// 64位导入表
if (IMAGE_NT_OPTIONAL_HDR64_MAGIC == m_OptionalHeaderMagic)
{
// 遍历导入表
for (DWORD i = 0; 0 != ::memcmp(pImportDesc + i, &emptyImportDesc, sizeof(emptyImportDesc)); i++)
{
LPCSTR lpDllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pImportDesc[i].Name);
if (NULL == lpDllName)
{
break;
}
PIMAGE_THUNK_DATA64 pThunkData = (PIMAGE_THUNK_DATA64)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pImportDesc[i].FirstThunk);
if (NULL == pThunkData)
{
break;
}
//先统计一下导入函数数量
PIMAGE_THUNK_DATA64 pThunkDataTmp = pThunkData;
size_t nThunkCount = 0;
while (pThunkDataTmp->u1.AddressOfData)
{
nThunkCount++;
pThunkDataTmp++;
}
std::vector<IMPORT_FUNCTION_INFO> FunList;
//预先分配空间, 速度比每次 push_back 一个信息快
FunList.resize(nThunkCount);
size_t nIndex = 0;
while (pThunkData->u1.AddressOfData)
{
if (IMAGE_SNAP_BY_ORDINAL64(pThunkData->u1.AddressOfData))
{
FunList[nIndex].Hint = IMAGE_ORDINAL64(pThunkData->u1.AddressOfData);
}
else
{
PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)_GetSectionDataAddr(lpSectionData, lpSectionHeader, pThunkData->u1.AddressOfData);
if (NULL == pImportName)
{
break;
}
FunList[nIndex].Hint = pImportName->Hint;
FunList[nIndex].Name = _AStrToTStr((LPCSTR)&pImportName->Name);
}
nIndex++;
pThunkData++;
}
auto result = m_ImportTable.emplace(_AStrToTStr(lpDllName), FunList);
if (!result.second)
{
result.first->second.insert(result.first->second.end(), FunList.begin(), FunList.end());
}
}
}
fResult = true;
} while (false);
return fResult;
}
bool CPEHelper::_LoadResourceTable(
LPCBYTE lpSectionData,
ULONGLONG ullVirtualAddr,
const PIMAGE_SECTION_HEADER lpSectionHeader
)
{
bool fResult = false;
UNREFERENCED_PARAMETER(ullVirtualAddr);
if (!lpSectionData || !lpSectionHeader)
{
return false;
}
do
{
PIMAGE_RESOURCE_DIRECTORY pDirectoryRoot = (PIMAGE_RESOURCE_DIRECTORY)lpSectionData;
DWORD dwTpyeCount = pDirectoryRoot->NumberOfNamedEntries + pDirectoryRoot->NumberOfIdEntries;
m_ResourceInfo.ResourceTable.resize(dwTpyeCount);
for (DWORD i = 0; i < dwTpyeCount; i++)
{
//资源类型目录子项
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntryType = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pDirectoryRoot + 1) + i;
m_ResourceDirectoryEntrys.push_back(*pEntryType);
RESOURCE_GROUP_INFO GroupInfo;
// 资源类型ID为字符串
if (pEntryType->NameIsString)
{
PIMAGE_RESOURCE_DIR_STRING_U pStrName = (PIMAGE_RESOURCE_DIR_STRING_U)((LPBYTE)pDirectoryRoot + pEntryType->NameOffset);
GroupInfo.TypeName = _WStrToTStr(std::wstring(pStrName->NameString, pStrName->Length));
GroupInfo.TypeID = 0;
}
else
{
// 预定义资源类型名字符串
if (pEntryType->Id < _countof(g_ResourctTypeName))
{
GroupInfo.TypeName = g_ResourctTypeName[pEntryType->Id];
}
GroupInfo.TypeID = pEntryType->Id;
}
// 资源类型ID子项
PIMAGE_RESOURCE_DIRECTORY pDirectoryDataID = (PIMAGE_RESOURCE_DIRECTORY)((LPBYTE)pDirectoryRoot + pEntryType->OffsetToDirectory);
DWORD dwCount = pDirectoryDataID->NumberOfNamedEntries + pDirectoryDataID->NumberOfIdEntries;
for (DWORD j = 0; j < dwCount; j++)
{
RESOURCE_ITEM info;
// 资源类型ID子项
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntryDataID = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pDirectoryDataID + 1) + j;
info.ID = pEntryDataID->Id;
//各种语言版本的资源数据
PIMAGE_RESOURCE_DIRECTORY pDirectoryLanguage = (PIMAGE_RESOURCE_DIRECTORY)((LPBYTE)pDirectoryRoot + pEntryDataID->OffsetToDirectory);
DWORD dwLanguageCount = pDirectoryLanguage->NumberOfNamedEntries + pDirectoryLanguage->NumberOfIdEntries;
GroupInfo.Items.resize(dwLanguageCount);
for (DWORD k = 0; k < dwLanguageCount; k++)
{
//资源ID与数据偏移, 数据大小
PIMAGE_RESOURCE_DIRECTORY_ENTRY pEntryLanguage = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(pDirectoryLanguage + 1) + k;
PIMAGE_RESOURCE_DATA_ENTRY pDataEntry = (PIMAGE_RESOURCE_DATA_ENTRY)((LPBYTE)pDirectoryRoot + pEntryLanguage->OffsetToDirectory);
info.LangID = pEntryLanguage->Id;
info.OffsetToData = pDataEntry->OffsetToData;
info.Size = pDataEntry->Size;
info.CodePage = pDataEntry->CodePage;
GroupInfo.Items[k] = info;
}
}
m_ResourceInfo.ResourceTable[i] = GroupInfo;
}
fResult = true;
_LoadResourceInformation(lpSectionData, lpSectionHeader);
} while (false);
return fResult;
}
bool CPEHelper::_LoadResourceInformation(
LPCBYTE lpSectionData,
const PIMAGE_SECTION_HEADER lpSectionHeader
)
{
// 解析各种资源类型
for (const auto& item : m_ResourceInfo.ResourceTable)
{
if (MAKEINTRESOURCE(item.TypeID) == RT_STRING)
{
// 加载 字符串表 资源
_LoadResourceStringTable(lpSectionData, lpSectionHeader, item);
}
else if (MAKEINTRESOURCE(item.TypeID) == RT_MANIFEST)
{
// 加载 Manifest 资源
_LoadResourceManifest(lpSectionData, lpSectionHeader, item);
}
else if (MAKEINTRESOURCE(item.TypeID) == RT_VERSION)
{
// 加载 版本 资源
_LoadResourceVersion(lpSectionData, lpSectionHeader, item);
}
}
return true;
}
bool CPEHelper::_LoadResourceStringTable(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
)
{
for (const auto& str : info.Items)
{
// 数据范围检查
if (str.OffsetToData < lpSectionHeader->VirtualAddress ||
str.OffsetToData >= (lpSectionHeader->VirtualAddress + lpSectionHeader->SizeOfRawData))
{
continue;
}
std::vector<STRING_TEXT> strList;
DWORD SectionOffset = str.OffsetToData - lpSectionHeader->VirtualAddress;
LPWORD lpLengthBegin = (LPWORD)((LPBYTE)lpSection + SectionOffset);
LPWORD lpLengthEnd = (LPWORD)((LPBYTE)lpLengthBegin + str.Size);
DWORD wIdBegin = (str.ID - 1) * 16;
DWORD wIdEnd = wIdBegin + 16;
LPWORD lpStrLength = lpLengthBegin;
for (DWORD i = wIdBegin; i < wIdEnd; i++)
{
// 数据范围检查
if (lpStrLength >= lpLengthEnd || 0 == *lpStrLength)
{
break;
}
STRING_TEXT strText;
LPCWSTR lpStrAddr = (LPCWSTR)lpStrLength + 1;
if ((LPBYTE)lpStrAddr < ((LPBYTE)lpLengthEnd))
{
strText.StrText = _WStrToTStr(std::wstring(lpStrAddr, *lpStrLength));
strText.ID = i;
strList.push_back(strText);
}
lpStrLength = (LPWORD)lpStrAddr + *lpStrLength;
}
auto itFind = m_ResourceInfo.StringTable.find(str.LangID);
if (m_ResourceInfo.StringTable.end() == itFind)
{
m_ResourceInfo.StringTable.emplace(str.LangID, strList);
}
else
{
itFind->second.insert(itFind->second.end(), strList.begin(), strList.end());
}
}
return true;
}
bool CPEHelper::_LoadResourceManifest(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
)
{
for (const auto& str : info.Items)
{
// 数据范围检查
if (str.OffsetToData < lpSectionHeader->VirtualAddress ||
str.OffsetToData >= (lpSectionHeader->VirtualAddress + lpSectionHeader->SizeOfRawData))
{
continue;
}
DWORD SectionOffset = str.OffsetToData - lpSectionHeader->VirtualAddress;
LPCSTR lpStr = (LPCSTR)((LPBYTE)lpSection + SectionOffset);
m_ResourceInfo.Manifest = _U8StrToTStr(std::string(lpStr, str.Size));
}
return true;
}
bool CPEHelper::_LoadResourceVersion(
LPCBYTE lpSection,
const PIMAGE_SECTION_HEADER lpSectionHeader,
const RESOURCE_GROUP_INFO& info
)
{
for (const auto& str : info.Items)
{
// 数据范围检查
if (str.OffsetToData < lpSectionHeader->VirtualAddress ||
str.OffsetToData >= (lpSectionHeader->VirtualAddress + lpSectionHeader->SizeOfRawData))
{
continue;
}
DWORD SectionOffset = str.OffsetToData - lpSectionHeader->VirtualAddress;
PVS_VERSIONINFO lpVersion = (PVS_VERSIONINFO)((LPBYTE)lpSection + SectionOffset);
// 存在 VS_FIXEDFILEINFO 信息
if (0 != lpVersion->wValueLength)
{
VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO));
pFixedFileInfo = (VS_FIXEDFILEINFO*)_GetAlignAddr(lpVersion, (LPVOID)pFixedFileInfo, sizeof(DWORD));
m_ResourceInfo.VersionInfo.FixedFileInfo = *pFixedFileInfo;
}
{
VS_FIXEDFILEINFO FixedFileInfo = { 0 };
LPBYTE lpVerBeg = (LPBYTE)lpVersion;
// 存在 VS_FIXEDFILEINFO 信息
if (0 != lpVersion->wValueLength)
{
VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO));
pFixedFileInfo = (VS_FIXEDFILEINFO*)_GetAlignAddr(lpVersion, pFixedFileInfo, sizeof(DWORD));
FixedFileInfo = *pFixedFileInfo;
lpVerBeg = (LPBYTE)lpVerBeg + sizeof(VS_VERSIONINFO) + lpVersion->wValueLength;
}
lpVerBeg = _GetAlignAddr(lpVersion, lpVerBeg, sizeof(DWORD));
LPBYTE lpVerEnd = (LPBYTE)lpVersion + lpVersion->wLength;
while (lpVerBeg < lpVerEnd)
{
StringFileInfo* pStringFileInfo = (StringFileInfo*)_GetAlignAddr(lpVersion, lpVerBeg, sizeof(DWORD));
if (0 == pStringFileInfo->wLength)
{
break;
}
// 解析 "StringFileInfo" 块
if (0 == _wcsnicmp(L"StringFileInfo", pStringFileInfo->szKey, 14))
{
LPBYTE lpStringFileInfoBeg = (LPBYTE)pStringFileInfo;
LPBYTE lpStringFileInfoEnd = (LPBYTE)pStringFileInfo + pStringFileInfo->wLength;
lpStringFileInfoBeg = (LPBYTE)(pStringFileInfo->Children);
while (lpStringFileInfoBeg < lpStringFileInfoEnd)
{
STRING_FILE_INFO strFileInfo;
StringTable* pStringTable = (StringTable*)lpStringFileInfoBeg;
if (0 == pStringTable->wLength)
{
break;
}
strFileInfo.StringCode = _WStrToTStr(std::wstring(pStringTable->szKey, _countof(pStringTable->szKey)));
LPBYTE lpStringBeg = (LPBYTE)pStringTable->Children;
LPBYTE lpStringEnd = (LPBYTE)pStringTable + pStringTable->wLength;
while (lpStringBeg < lpStringEnd)
{
const String* pString = (String*)lpStringBeg;
if (0 == pString->wLength)
{
break;
}
//值名
std::wstring strKey = pString->szKey;
STRING_FILE_ITEM item;
//值数据
LPCWSTR lpStrValue = pString->szKey + strKey.size() + 1;
//值数据位置
lpStrValue = (LPCWSTR)_GetAlignAddr(lpVersion, lpStrValue, sizeof(DWORD));
item.wType = pString->wType;
item.Key = _WStrToTStr(strKey);
//字符串类型
if (1 == pString->wType)
{
WORD wLength = pString->wValueLength;
if (wLength > 0 && L'\0' == lpStrValue[wLength - 1])
{
wLength--;
}
std::wstring strValue = std::wstring(lpStrValue, wLength);
item.Value = _WStrToTStr(strValue);
}
//字节数据类型
else
{
DWORD dwValueSize = pString->wValueLength;
item.Value = _WStrToTStr(std::wstring(lpStrValue, dwValueSize));
item.Data.resize(dwValueSize);
for (DWORD i = 0; i < dwValueSize; i++)
{
item.Data[i] = ((LPBYTE)lpStrValue)[i];
}
}
strFileInfo.StringInfos.push_back(item);
lpStringBeg = _GetAlignAddr(lpVersion, lpStringBeg + pString->wLength, sizeof(DWORD));
}
m_ResourceInfo.VersionInfo.StringFileInfo.push_back(strFileInfo);
lpStringFileInfoBeg = _GetAlignAddr(lpVersion, (LPBYTE)pStringTable + pStringTable->wLength, sizeof(DWORD));
}
}
// 解析 "VarFileInfo" 块
else if (0 == _wcsnicmp(L"VarFileInfo", pStringFileInfo->szKey, 11))
{
VarFileInfo* pVerFileInfo = (VarFileInfo*)_GetAlignAddr(lpVersion, pStringFileInfo, sizeof(DWORD));
Var* pVar = (Var*)pVerFileInfo->Children;
if (0 == _wcsnicmp(L"Translation", pVar->szKey, 11))
{
for (DWORD i = 0; i < pVar->wValueLength / sizeof(DWORD); i++)
{
TRANSLATION_INFO transInfo;
transInfo.CodePageID = pVar->Value[i].CodePageID;
transInfo.LanguageID = pVar->Value[i].LanguageID;
m_ResourceInfo.VersionInfo.TranslationList.push_back(transInfo);
}
}
}
lpVerBeg = _GetAlignAddr(lpVersion, lpVerBeg + pStringFileInfo->wLength, sizeof(DWORD));
}
}
}
return true;
}
void CPEHelper::PrintResourceTable(bool fShowDetail/* = true*/)
{
ConsoleOutput(_T("资源类型数量: %4d\n"), (int)m_ResourceInfo.ResourceTable.size());
int i = 0;
for (const auto& ResourceInfo : m_ResourceInfo.ResourceTable)
{
if (0 != ResourceInfo.TypeID)
{
ConsoleOutput(_T(" %4d 类型ID: %4d 类型名: %s 数量: %d\n"), i++, ResourceInfo.TypeID, ResourceInfo.TypeName.c_str(), (int)ResourceInfo.Items.size());
}
else
{
ConsoleOutput(_T(" %4d 类型ID: \"%s\" 数量: %d\n"), i++, ResourceInfo.TypeName.c_str(), (int)ResourceInfo.Items.size());
}
if (!fShowDetail)
{
continue;
}
int j = 0;
for (const auto& item : ResourceInfo.Items)
{
ConsoleOutput(_T(" %4d 资源ID: %4d 节数据偏移: %08X 大小: %d 语言: %d\n"), j++, item.ID, item.OffsetToData, item.Size, item.LangID);
}
}
}
void CPEHelper::PrintExportTable(bool fShowModule/* = true*/, bool fShowFunList/* = true*/)
{
int nFunctionCount = 0;
for (const auto& item : m_ExportTable)
{
nFunctionCount += (int)item.second.size();
}
ConsoleOutput(_T("Export module count: %d function count: %d\n"), (int)m_ExportTable.size(), nFunctionCount);
int i = 0;
for (const auto& item : m_ExportTable)
{
if (fShowModule)
{
ConsoleOutput(_T(" %4d %s function count: %4d\n"), i++, item.first.c_str(), (int)item.second.size());
}
if (!fShowFunList)
{
continue;
}
int j = 0;
for (const auto& fun : item.second)
{
if (!fun.Name.empty())
{
if (fun.ForwarderName.empty())
{
ConsoleOutput(_T(" %4d Ordinal: %4d(%04x) Addr: %08x %s \n"), j++, fun.Ordinal, fun.Ordinal, fun.Addr, fun.Name.c_str());
}
else
{
ConsoleOutput(_T(" %4d Ordinal: %4d(%04x) Addr: %08x %s -> %s \n"), j++, fun.Ordinal, fun.Ordinal, fun.Addr, fun.Name.c_str(), fun.ForwarderName.c_str());
}
}
else
{
ConsoleOutput(_T(" %4d Ordinal: %4d(%04x) Addr: %08x \n"), j++, fun.Ordinal, fun.Ordinal, fun.Addr);
}
}
}
}
void CPEHelper::PrintImportTable(bool fShowModule/* = true*/, bool fShowFunList/* = true*/)
{
int nFunctionCount = 0;
for (const auto& item : m_ImportTable)
{
nFunctionCount += (int)item.second.size();
}
ConsoleOutput(_T("Import module count: %d function count: %d\n"), (int)m_ImportTable.size(), nFunctionCount);
int i = 0;
for (const auto& item : m_ImportTable)
{
if (fShowModule)
{
ConsoleOutput(_T(" %d %s count: %d\n"), i++, item.first.c_str(), (int)item.second.size());
}
if (!fShowFunList)
{
continue;
}
int j = 0;
for (const auto& fun : item.second)
{
if (!fun.Name.empty())
{
ConsoleOutput(_T(" %4d %4d(%04x) %s\n"), j++, fun.Hint, fun.Hint, fun.Name.c_str());
}
else
{
ConsoleOutput(_T(" %4d %4d(%04x)\n"), j++, fun.Hint, fun.Hint);
}
}
}
}
void CPEHelper::PrintVersion()
{
ConsoleOutput(_T("Version:\n"));
ConsoleOutput(_T(" StringFileInfo conut: %d\n"), (int)m_ResourceInfo.VersionInfo.StringFileInfo.size());
for (const auto& item : m_ResourceInfo.VersionInfo.StringFileInfo)
{
ConsoleOutput(_T(" %s\n"), item.StringCode.c_str());
VS_FIXEDFILEINFO& FixedFileInfo = m_ResourceInfo.VersionInfo.FixedFileInfo;
ConsoleOutput(_T(" FileVersion: %d.%d.%d.%d\n"),
HIWORD(FixedFileInfo.dwFileVersionMS),
LOWORD(FixedFileInfo.dwFileVersionMS),
HIWORD(FixedFileInfo.dwFileVersionLS),
LOWORD(FixedFileInfo.dwFileVersionLS));
for (const auto& info : item.StringInfos)
{
if (info.wType)
{
ConsoleOutput(_T(" %s: %s\n"), info.Key.c_str(), info.Value.c_str());
}
else
{
ConsoleOutput(_T(" %s: %s\n"), info.Key.c_str(), info.Value.c_str());
//ConsoleOutput(_T(" %s: "), info.Key.c_str());
//_PrintfByte((LPVOID)info.Data.data(), info.Data.size());
//ConsoleOutput(_T("\n"));
}
}
}
ConsoleOutput(_T(" Translation conut: %d\n"), (int)m_ResourceInfo.VersionInfo.TranslationList.size());
for (const auto& item : m_ResourceInfo.VersionInfo.TranslationList)
{
ConsoleOutput(_T(" 0x%04X 0x%04X\n"), item.LanguageID, item.CodePageID);
}
}
void CPEHelper::PrintStringTable()
{
ConsoleOutput(_T("StringTable: count: %d\n"), (int)m_ResourceInfo.StringTable.size());
for (const auto& item : m_ResourceInfo.StringTable)
{
ConsoleOutput(_T(" ID: %d, count: %d\n"), item.first, (int)item.second.size());
for (const auto& info : item.second)
{
ConsoleOutput(_T(" %04d: %s\n"), info.ID, info.StrText.c_str());
}
}
}
void CPEHelper::PrintManifest()
{
ConsoleOutput(_T("Manifest:\n"));
ConsoleOutput(_T("%s\n"), m_ResourceInfo.Manifest.c_str());
}
测试
main.c
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include "CPEHelper.h"
#include "CTimeUtils.h"
int main()
{
setlocale(LC_ALL, "");
int64_t timeBegin = CTimeUtils::GetCurrentTickCount();
int64_t timeEnd = CTimeUtils::GetCurrentTickCount();
int nRepeatCount = 1000;
while (true)
{
timeBegin = CTimeUtils::GetCurrentTickCount();
{
CPEHelper obj;
for (int i = 0; i < nRepeatCount; i++)
{
//obj.LoadFile(_T(R"(gfx_win_101.3790_101.2114.exe)"), false);
//obj.LoadFile(_T(R"(CPEUtils.exe)"), false);
//obj.LoadFile(_T(R"(qt-opensource-windows-x86-5.14.2.exe)"), false);
obj.LoadFile(_T(R"(user32.dll)"), false);
//obj.LoadFile(_T(R"(ndis.sys)"), true);
//obj.LoadFile(_T(R"(shell32.dll)"), true);
//obj.LoadFile(_T(R"(KernelBase.dll)"), true);
//obj.LoadFile(_T(R"(ntdll.dll)"), true);
}
RESOURCE_INFO info = obj.GetResourceInfo();
timeEnd = CTimeUtils::GetCurrentTickCount();
#if 1
obj.PrintExportTable(false, false);
_tprintf(_T("\n"));
obj.PrintImportTable(false, false);
_tprintf(_T("\n"));
obj.PrintResourceTable(false);
_tprintf(_T("\n"));
obj.PrintStringTable();
_tprintf(_T("\n"));
obj.PrintVersion();
_tprintf(_T("\n"));
obj.PrintManifest();
#else
obj.PrintExportTable(true, true);
_tprintf(_T("\n"));
obj.PrintImportTable(true, true);
_tprintf(_T("\n"));
obj.PrintResourceTable(true);
_tprintf(_T("\n"));
obj.PrintStringTable();
_tprintf(_T("\n"));
obj.PrintVersion();
_tprintf(_T("\n"));
obj.PrintManifest();
#endif
_tprintf(_T("Repeat count: %d Cost time: %llu ms, Speed: %0.3lf/S\n"), nRepeatCount, timeEnd - timeBegin, (double)nRepeatCount * 1000 / ((double)(timeEnd - timeBegin)));
}
system("pause");
}
return 0;
}