C++ PE文件信息解析

尝试解析PE文件结构, 于是编写了此PE信息助手类, 暂时完成如下信息解析

1.导入表信息

2.导入表信息

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 
#include 
#include 
#include 
#include 
#include 

#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 {
    WORD ID;                    //资源ID
    WORD LangID;                //语言ID
    DWORD SectionOffset;        //数据偏移(在节数据中的偏移位置)
    DWORD Size;                 //数据大小
}RESOURCE_ITEM;

typedef struct {
    _tstring StrText;           //文本内容
    WORD ID;                    //字符串ID
}STRING_TEXT;

typedef struct {
    _tstring TypeName;                  //类型名
    WORD TypeID;                        //类型ID
    std::vector Items;   //资源信息
}RESOURCE_GROUP_INFO;

typedef struct
{
    _tstring Text;
    WORD  wType;
}STRING_INFO;

typedef struct
{
    _tstring Key;                   // 值名字符串
    _tstring Value;                 // 值数据字符串
    WORD wType;                     // 版本资源中的数据类型, 1: 资源包含文本数据 0: 版本资源包含二进制数据
    std::vector Data;      // 二进制数据
}STRING_FILE_ITEM;

// 版本资源中的数据, 包含可为特定语言和代码页显示的版本信息
typedef struct
{
    _tstring StringCode;
    std::vector StringInfos;
}STRING_FILE_INFO;

typedef struct
{
    WORD LanguageID;
    WORD CodePageID;
}TRANSLATION_INFO;

// 版本信息
typedef struct
{
    VS_FIXEDFILEINFO FixedFileInfo;
    std::vector StringFileInfo;   // 版本资源中的数据的组织
    std::vector TranslationList;  // 应用程序或 DLL 支持的语言列表
}VERSION_INFO;

typedef struct
{
    _tstring Name;          //函数名
    WORD Hint;              //索引, 可能为0
}IMPORT_FUNCTION_INFO;

typedef struct
{
    _tstring Name;          //函数名
    _tstring ForwarderName; //转发函数名
    DWORD Addr;             //函数相对偏移地址
    DWORD Ordinal;          //函数顺序
}EXPORT_FUNCTION_INFO;

typedef struct _RESOURCE_INFO
{
    std::vector ResourceTable;             //资源表信息
    std::map> 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
    // @ret: bool               操作成功与否
    bool LoadFile(const _tstring& strPath, bool fCheckSum = true);

    //
    // @brief: 关闭文件占用
    // @ret: bool               操作成功与否
    void Close();

    //
    // @brief: 获取导入表信息
    // @ret: std::map<_tstring, std::vector>  导入表信息
    const std::map<_tstring, std::vector>& GetImportTable() const;

    //
    // @brief: 获取导出表信息
    // @ret: std::map<_tstring, std::vector>  导出表信息
    const std::map<_tstring, std::vector>& 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 PrintManifest();

private:

    //
    // @brief: 清空数据
    // @ret: void
    void _Clear();

    //
    // @brief: 输出字节信息
    // @ret: void
    void _PrintfByte(LPVOID lpData, size_t size);

    //
    // @brief: 获取文件大小
    // @param: ullSize          文件大小
    // @ret: bool               操作成功与否
    bool _GetFileSize(unsigned long long& ullSize) const;

    //
    // @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: sectionHeader    节信息头
    // @param: ullVirtualAddr   虚拟地址
    // @ret: LONGLONG           虚拟地址在节数据中的相对偏移
    LONGLONG _GetSectionDataOffset(
        const IMAGE_SECTION_HEADER& sectionHeader, 
        ULONGLONG ullVirtualAddr
    );

    //
    // @brief: 获取虚拟地址在节数据内存中的位置
    // @param: lpBase           节数据内存位置
    // @param: sectionHeader    节信息头
    // @param: ullVirtualAddr   虚拟地址
    // @ret: LONGLONG           虚拟地址在节数据内存中的位置
    LPVOID _GetSectionDataAddr(
        LPVOID lpBase, 
        const IMAGE_SECTION_HEADER& sectionHeader, 
        ULONGLONG ullVirtualAddr
    );

    //
    // @brief: 获取虚拟地址在节数据内存中的位置
    // @param: ullVirtualAddr   虚拟地址
    // @param: pSectinHeader    节信息头输出缓冲
    // @ret: bool               操作成功与否
    bool _GetSectionHeader(
        ULONGLONG ullVirtualAddr, 
        PIMAGE_SECTION_HEADER pSectinHeader
    );

    //
    // @brief: 获取地址对齐后的地址
    // @param: lpAddr           地址
    // @param: dwAlign          对齐粒度
    // @ret: LPVOID             对齐后指针
    LPVOID _GetAlignAddr(const LPVOID lpAddr, DWORD dwAlign = sizeof(DWORD));

    //
    // @brief: 加载所有信息
    // @ret: bool               操作成功与否
    bool _LoadAllInformation();

    //
    // @brief: 加载导出表
    // @ret: bool               操作成功与否
    bool _LoadExportTable();

    //
    // @brief: 加载导入表
    // @ret: bool               操作成功与否
    bool _LoadImportTable();

    //
    // @brief: 加载资源表
    // @ret: bool               操作成功与否
    bool _LoadResourceTable();

    //
    // @brief: 加载资源信息
    // @param: lpSection        节数据内存指针
    // @ret: bool               操作成功与否
    bool _LoadResourceInformation(const LPBYTE lpSectionData);

    //
    // @brief: 加载资源字符串表
    // @param: lpSection        节数据内存指针
    // @ret: info               资源组信息
    // @ret: bool               操作成功与否
    bool _LoadResourceStringTable(
        const LPBYTE lpSection,
        const RESOURCE_GROUP_INFO& info
    );

    //
    // @brief: 加载资源Manifest
    // @param: lpSection        节数据内存指针
    // @ret: info               资源组信息
    // @ret: bool               操作成功与否
    bool _LoadResourceManifest(
        const LPBYTE lpSection,
        const RESOURCE_GROUP_INFO& info
    );

    //
    // @brief: 加载资源版本信息
    // @param: lpSection        节数据内存指针
    // @ret: info               资源组信息
    // @ret: bool               操作成功与否
    bool _LoadResourceVersion(
        const LPBYTE lpSection,
        const RESOURCE_GROUP_INFO& info
    );

    // 宽字符转多字节字符
    static std::string _WStrToMultiStr(UINT CodePage, const std::wstring& str);

    // 多字节字符转宽字符
    static std::wstring _MultiStrToWStr(UINT CodePage, const std::string& str);

    // 宽字符串转换
    static std::string _WStrToU8Str(const std::wstring& str);
    static std::string _WStrToAStr(const std::wstring& str);
    static _tstring _WStrToTStr(const std::wstring& str);

    // ANSI字符转换
    static std::wstring _AStrToWStr(const std::string& str);
    static std::string _AStrToU8Str(const std::string& str);
    static _tstring _AStrToTStr(const std::string& str);

    // UTF-8字符串转换
    static std::wstring _U8StrToWStr(const std::string& str);
    static std::string _U8StrToAStr(const std::string& str);
    static _tstring _U8StrToTStr(const std::string& str);

    // 中立字符串转换
    static std::string _TStrToAStr(const _tstring& str);
    static std::wstring _TStrToWStr(const _tstring& str);
    static std::string _TStrToU8Str(const _tstring& str);

private:

    HANDLE m_hFile;                                                                 //PE文件句柄

    //基础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 m_SectionHeader;                              //节头信息
    std::vector m_ResourceDirectoryEntrys;          //资源目录信息

    //数据目录解析相关成员
    std::map<_tstring, std::vector> m_ImportTable;            //导入表信息
    std::map<_tstring, std::vector> m_ExportTable;            //导出表信息
    RESOURCE_INFO m_ResourceInfo;                                                   //资源信息
    WORD m_OptionalHeaderMagic;                                                     //可选头魔数
};

CPEHelper.cpp

#include "CPEHelper.h"
#include 
#include 

#pragma comment(lib, "Imagehlp.lib") 

// Directory Entries

/*
#define IMAGE_DIRECTORY_ENTRY_EXPORT          0   // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT          1   // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE        2   // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION       3   // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY        4   // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC       5   // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG           6   // Debug Directory
//      IMAGE_DIRECTORY_ENTRY_COPYRIGHT       7   // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE    7   // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR       8   // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS             9   // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG    10   // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT   11   // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT            12   // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT   13   // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14   // COM Runtime descriptor
*/

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")
};

std::string CPEHelper::_WStrToMultiStr(UINT CodePage, const std::wstring& str)
{
    std::string strResult;
    LPSTR lpMultiByteStr = NULL;

    do
    {
        //计算缓冲区所需的字节大小
        int nConverted = 0;
        nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, NULL, 0, NULL, NULL);
        if (0 == nConverted)
        {
            break;
        }

        //分配内存
        lpMultiByteStr = (LPSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nConverted);
        if (NULL == lpMultiByteStr)
        {
            break;
        }

        //转换
        nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, lpMultiByteStr, nConverted, NULL, NULL);
        if (0 == nConverted)
        {
            break;
        }

        strResult = lpMultiByteStr;

    } while (false);

    //释放字符串缓冲
    if (NULL != lpMultiByteStr)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpMultiByteStr);
        lpMultiByteStr = NULL;
    }

    return strResult;
}

std::wstring CPEHelper::_MultiStrToWStr(UINT CodePage, const std::string& str)
{
    std::wstring strResult;
    LPWSTR lpWideStr = NULL;

    do
    {
        //计算缓冲区所需的字节大小
        int nConverted = 0;
        nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, NULL, 0);
        if (0 == nConverted)
        {
            break;
        }

        //分配缓冲内存
        lpWideStr = (LPWSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, nConverted * sizeof(WCHAR));
        if (NULL == lpWideStr)
        {
            break;
        }

        //转换字符串
        nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, lpWideStr, nConverted);
        if (0 == nConverted)
        {
            break;
        }

        strResult = lpWideStr;

    } while (false);

    //释放字符串缓冲
    if (NULL != lpWideStr)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpWideStr);
        lpWideStr = NULL;
    }

    return strResult;
}

std::string CPEHelper::_WStrToAStr(const std::wstring& str)
{
    return _WStrToMultiStr(CP_ACP, str);
}

std::string CPEHelper::_WStrToU8Str(const std::wstring& str)
{
    return _WStrToMultiStr(CP_UTF8, str);
}

_tstring CPEHelper::_WStrToTStr(const std::wstring& str)
{
#ifdef _UNICODE
    return str;
#else
    return _WStrToMultiStr(CP_ACP, str);
#endif
}

std::wstring CPEHelper::_AStrToWStr(const std::string& str)
{
    return _MultiStrToWStr(CP_ACP, str);
}

std::string CPEHelper::_AStrToU8Str(const std::string& str)
{
    return _WStrToU8Str(_AStrToWStr(str));
}

_tstring CPEHelper::_AStrToTStr(const std::string& str)
{
#ifdef _UNICODE
    return _MultiStrToWStr(CP_ACP, str);
#else
    return str;
#endif
}

std::wstring CPEHelper::_U8StrToWStr(const std::string& str)
{
    return _MultiStrToWStr(CP_UTF8, str);
}

std::string CPEHelper::_U8StrToAStr(const std::string& str)
{
    return _WStrToAStr(_U8StrToWStr(str));
}

_tstring CPEHelper::_U8StrToTStr(const std::string& str)
{
#ifdef _UNICODE
    return _MultiStrToWStr(CP_UTF8, str);
#else
    return _WStrToAStr(_U8StrToWStr(str));
#endif
}

std::string CPEHelper::_TStrToAStr(const _tstring& str)
{
#ifdef _UNICODE
    return _WStrToMultiStr(CP_ACP, str);
#else
    return str;
#endif
}

std::wstring CPEHelper::_TStrToWStr(const _tstring& str)
{
#ifdef _UNICODE
    return str;
#else
    return _AStrToWStr(str);
#endif
}

std::string CPEHelper::_TStrToU8Str(const _tstring& str)
{
#ifdef _UNICODE
    return _WStrToU8Str(str);
#else
    return _WStrToU8Str(_AStrToWStr(str));
#endif
}

CPEHelper::CPEHelper()
    :
    m_hFile(INVALID_HANDLE_VALUE)
{
    _Clear();
}

CPEHelper::~CPEHelper()
{
    Close();
}

bool CPEHelper::LoadFile(const _tstring& strPath, bool fCheckSum/* = true*/)
{
    bool bResult = false;
    Close();
    _Clear();

    m_hFile = ::CreateFile(
        strPath.c_str(),
        GENERIC_READ,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        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;
        }

        // 读取 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)
        {
            if (fCheckSum && (CheckSum != ntHeader32.OptionalHeader.CheckSum))
            {
                break;
            }
            m_OptionalHeaderMagic = IMAGE_NT_OPTIONAL_HDR32_MAGIC;
            memcpy_s(&m_NtHeader32, sizeof(m_NtHeader32), &ntHeader32, sizeof(ntHeader32));
            memcpy(&m_DataDirectory, &m_NtHeader32.OptionalHeader.DataDirectory, sizeof(m_DataDirectory));
        }
        // 检查 是否为 64位程序可选头
        else if (IMAGE_NT_OPTIONAL_HDR64_MAGIC == ntHeader64.OptionalHeader.Magic)
        {
            if (fCheckSum && (CheckSum != ntHeader64.OptionalHeader.CheckSum))
            {
                break;
            }

            m_OptionalHeaderMagic = IMAGE_NT_OPTIONAL_HDR64_MAGIC;
            memcpy_s(&m_NtHeader64, sizeof(m_NtHeader64), &ntHeader64, sizeof(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头
        memcpy(&m_DosHeader, &dosHeader, sizeof(m_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);

    if (bResult)
    {
        _LoadAllInformation();
    }
    else
    {
        Close();
    }

    return bResult;
}

void CPEHelper::Close()
{
    if (INVALID_HANDLE_VALUE != m_hFile)
    {
        ::CloseHandle(m_hFile);
        m_hFile = INVALID_HANDLE_VALUE;
    }
}

const std::map<_tstring, std::vector>& CPEHelper::GetImportTable() const
{
    return m_ImportTable;
}

const std::map<_tstring, std::vector>& 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;
}

void CPEHelper::_PrintfByte(LPVOID lpData, size_t size)
{
    if (NULL == lpData)
    {
        return;
    }

    for (int i = 0; i < size; i++)
    {
        if (i != size - 1)
        {
            _tprintf(_T("%02X "), ((LPBYTE)lpData)[i]);
        }
        else
        {
            _tprintf(_T("%02X"), ((LPBYTE)lpData)[i]);
        }
    }
}

bool CPEHelper::_GetFileSize(unsigned long long& ullSize) const
{
    if (INVALID_HANDLE_VALUE == m_hFile)
    {
        return false;
    }

    LARGE_INTEGER ullFileSize = { 0 };
    if (!::GetFileSizeEx(m_hFile, &ullFileSize))
    {
        return false;
    }

    ullSize = ullFileSize.QuadPart;
    return true;
}

bool CPEHelper::_ReadFile(
    LPVOID lpBuffer,
    DWORD dwSize,
    LPDWORD lpBytesRead/* = nullptr*/,
    LONGLONG llPos/* = 0*/,
    DWORD dwFlag/* = FILE_CURRENT*/,
    bool fCheckResdSize/* = false*/
)
{
    if (INVALID_HANDLE_VALUE == m_hFile)
    {
        return false;
    }

    LARGE_INTEGER  liDistanceToMove = { 0 };
    BOOL bResult = FALSE;
    liDistanceToMove.QuadPart = llPos;
    ::SetFilePointerEx(m_hFile, liDistanceToMove, NULL, dwFlag);
    DWORD dwBytesRead = 0;

    bResult = ::ReadFile(m_hFile, lpBuffer, dwSize, &dwBytesRead, NULL);
    if (!bResult)
    {
        return bResult;
    }

    if (nullptr != lpBytesRead)
    {
        *lpBytesRead = dwBytesRead;
    }

    //设置了大区大小检查, 则当实际读取数据量与指定读取数据量相同才认为读取成功
    if (fCheckResdSize)
    {
        bResult = (dwBytesRead == dwSize);
    }

    return bResult;
}

LONGLONG CPEHelper::_GetSectionDataOffset(
    const IMAGE_SECTION_HEADER& sectionHeader,
    ULONGLONG ullVirtualAddr
)
{
    ULONGLONG VirtualAddrBegin = sectionHeader.VirtualAddress;
    ULONGLONG VirtualAddrEnd = VirtualAddrBegin + sectionHeader.SizeOfRawData;
    ULONGLONG ullOffset = ullVirtualAddr;
    if (ullOffset < VirtualAddrBegin || ullOffset >= VirtualAddrEnd)
    {
        return -1;
    }

    ullOffset -= VirtualAddrBegin;

    return ullOffset;
}

LPVOID CPEHelper::_GetSectionDataAddr(
    LPVOID lpBase,
    const IMAGE_SECTION_HEADER& sectionHeader,
    ULONGLONG ullVirtualAddr
)
{
    ULONGLONG VirtualAddrBegin = sectionHeader.VirtualAddress;
    ULONGLONG VirtualAddrEnd = VirtualAddrBegin + sectionHeader.SizeOfRawData;
    ULONGLONG ullOffset = VirtualAddrBegin + ullVirtualAddr - sectionHeader.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;
}

LPVOID CPEHelper::_GetAlignAddr(
    const LPVOID lpAddr,
    DWORD dwAlign
)
{
    DWORD_PTR dwPadding = ((DWORD_PTR)lpAddr % dwAlign);
    if (0 == dwPadding)
    {
        return lpAddr;
    }

    return (LPBYTE)lpAddr + (dwAlign - dwPadding);
}

bool CPEHelper::_LoadAllInformation()
{
    // 加载资源表
    _LoadResourceTable();

    // 加载导出表
    _LoadExportTable();

    // 加载导入表
    _LoadImportTable();

    return true;
}

bool CPEHelper::_LoadExportTable()
{
    bool fResult = false;

    DWORD VirtualAddress = m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
    if (0 == VirtualAddress)
    {
        return false;
    }

    LPBYTE lpSectionData = nullptr;

    do
    {
        // 获取虚拟地址所在节信息
        IMAGE_SECTION_HEADER sectionHeader = { 0 };
        if (!_GetSectionHeader(VirtualAddress, §ionHeader))
        {
            break;
        }

        //分配节数据缓冲
        lpSectionData = (LPBYTE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sectionHeader.SizeOfRawData);
        if (NULL == lpSectionData)
        {
            break;
        }

        // 读取整个节数据
        DWORD dwReadBytes = 0;
        if (!_ReadFile(lpSectionData, sectionHeader.SizeOfRawData, &dwReadBytes, sectionHeader.PointerToRawData, FILE_BEGIN, true))
        {
            break;
        }

        // 获取导出描述信息所在偏移
        ULONGLONG VirtualAddrBegin = m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
        ULONGLONG VirtualAddrEnd = VirtualAddrBegin + m_DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
        LONGLONG ullExportDescOffset = _GetSectionDataOffset(sectionHeader, VirtualAddress);
        if (-1 == ullExportDescOffset)
        {
            break;
        }

        // 获取导出目录信息位置
        PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((LPBYTE)lpSectionData + ullExportDescOffset);
        LPCSTR lpDllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, sectionHeader, pExportDirectory->Name);
        if (NULL == lpDllName)
        {
            break;
        }

        // 遍历导出表
        LPDWORD pFuncAddr = (LPDWORD)_GetSectionDataAddr(lpSectionData, sectionHeader, pExportDirectory->AddressOfFunctions);
        LPDWORD pNameAddr = (LPDWORD)_GetSectionDataAddr(lpSectionData, sectionHeader, pExportDirectory->AddressOfNames);
        LPWORD pNameOrdinalsName = (LPWORD)_GetSectionDataAddr(lpSectionData, sectionHeader, pExportDirectory->AddressOfNameOrdinals);
        if ((NULL == pFuncAddr) || (NULL == pNameAddr) || (NULL == pNameOrdinalsName))
        {
            break;
        }

        std::vector FunList;
        for (DWORD i = 0; i < pExportDirectory->NumberOfFunctions; i++)
        {
            EXPORT_FUNCTION_INFO info;
            info.Addr = pFuncAddr[i];
            info.Ordinal = pExportDirectory->Base + i;
            FunList.push_back(info);
        }

        for (DWORD i = 0; i < pExportDirectory->NumberOfNames; i++)
        {
            LPCSTR lpFnName = (LPCSTR)_GetSectionDataAddr(lpSectionData, sectionHeader, 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, sectionHeader, FunList[dwOrdinal].Addr);
                if (NULL == lpForwarderName)
                {
                    break;
                }

                FunList[dwOrdinal].ForwarderName = _AStrToTStr(lpForwarderName);
            }
        }

        m_ExportTable.insert(std::make_pair(_AStrToTStr(lpDllName), FunList));

        fResult = true;

    } while (false);

    // 释放节数据缓冲
    if (lpSectionData)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpSectionData);
    }

    return fResult;
}

bool CPEHelper::_LoadImportTable()
{
    IMAGE_IMPORT_DESCRIPTOR emptyImportDesc = { 0 };
    bool fResult = false;

    DWORD VirtualAddress = m_DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    if (0 == VirtualAddress)
    {
        return false;
    }

    LPBYTE lpSectionData = nullptr;

    do
    {
        // 获取虚拟地址所在节信息
        IMAGE_SECTION_HEADER sectionHeader = { 0 };
        if (!_GetSectionHeader(VirtualAddress, §ionHeader))
        {
            break;
        }

        //分配节数据缓冲
        lpSectionData = (LPBYTE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sectionHeader.SizeOfRawData);
        if (NULL == lpSectionData)
        {
            break;
        }

        // 读取整个节数据
        DWORD dwReadBytes = 0;
        if (!_ReadFile(lpSectionData, sectionHeader.SizeOfRawData, &dwReadBytes, sectionHeader.PointerToRawData, FILE_BEGIN, true))
        {
            break;
        }

        // 获取导入描述信息所在偏移
        LONGLONG ullImportDescOffset = _GetSectionDataOffset(sectionHeader, VirtualAddress);
        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)) != 0; i++)
            {
                LPCSTR DllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, sectionHeader, pImportDesc[i].Name);
                if (NULL == DllName)
                {
                    break;
                }

                PIMAGE_THUNK_DATA32 pThunkData = (PIMAGE_THUNK_DATA32)_GetSectionDataAddr(lpSectionData, sectionHeader, pImportDesc[i].FirstThunk);
                if (NULL == pThunkData)
                {
                    break;
                }

                std::vector FunList;
                while (pThunkData->u1.AddressOfData)
                {
                    if (IMAGE_SNAP_BY_ORDINAL32(pThunkData->u1.AddressOfData))
                    {
                        IMPORT_FUNCTION_INFO info;
                        info.Hint = IMAGE_ORDINAL32(pThunkData->u1.AddressOfData);
                        FunList.push_back(info);
                    }
                    else
                    {
                        PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)_GetSectionDataAddr(lpSectionData, sectionHeader, pThunkData->u1.AddressOfData);
                        if (NULL == pImportName)
                        {
                            break;
                        }

                        IMPORT_FUNCTION_INFO info;
                        info.Hint = pImportName->Hint;
                        info.Name = _AStrToTStr((LPCSTR)&pImportName->Name);
                        FunList.push_back(info);
                    }
                    pThunkData++;
                }

                m_ImportTable.insert(std::make_pair(_AStrToTStr(DllName), FunList));
            }
        }

        // 64位导入表
        if (IMAGE_NT_OPTIONAL_HDR64_MAGIC == m_OptionalHeaderMagic)
        {
            // 遍历导入表
            for (DWORD i = 0; 0 != ::memcmp(pImportDesc + i, &emptyImportDesc, sizeof(emptyImportDesc)) != 0; i++)
            {
                LPCSTR DllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, sectionHeader, pImportDesc[i].Name);
                if (NULL == DllName)
                {
                    break;
                }

                PIMAGE_THUNK_DATA64 pThunkData = (PIMAGE_THUNK_DATA64)_GetSectionDataAddr(lpSectionData, sectionHeader, pImportDesc[i].FirstThunk);
                if (NULL == pThunkData)
                {
                    break;
                }

                std::vector FunList;
                while (pThunkData->u1.AddressOfData)
                {
                    if (IMAGE_SNAP_BY_ORDINAL64(pThunkData->u1.AddressOfData))
                    {
                        IMPORT_FUNCTION_INFO info;
                        info.Hint = IMAGE_ORDINAL64(pThunkData->u1.AddressOfData);
                        FunList.push_back(info);
                    }
                    else
                    {
                        PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)_GetSectionDataAddr(lpSectionData, sectionHeader, pThunkData->u1.AddressOfData);
                        if (NULL == pImportName)
                        {
                            break;
                        }

                        IMPORT_FUNCTION_INFO info;
                        info.Hint = pImportName->Hint;
                        info.Name = _AStrToTStr((LPCSTR)&pImportName->Name);
                        FunList.push_back(info);
                    }
                    pThunkData++;
                }

                m_ImportTable.insert(std::make_pair(_AStrToTStr(DllName), FunList));
            }
        }

        fResult = true;

    } while (false);

    // 释放节数据缓冲
    if (lpSectionData)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpSectionData);
    }

    return fResult;
}

bool CPEHelper::_LoadResourceTable()
{
    bool fResult = false;

    DWORD VirtualAddress = m_DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
    if (0 == VirtualAddress)
    {
        return false;
    }

    LPBYTE lpSectionData = nullptr;

    do
    {
        // 获取虚拟地址所在节信息
        IMAGE_SECTION_HEADER sectionHeader = { 0 };
        if (!_GetSectionHeader(VirtualAddress, §ionHeader))
        {
            break;
        }

        //分配节数据缓冲
        lpSectionData = (LPBYTE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sectionHeader.SizeOfRawData);
        if (NULL == lpSectionData)
        {
            break;
        }

        // 读取整个节数据
        DWORD dwReadBytes = 0;
        if (!_ReadFile(lpSectionData, sectionHeader.SizeOfRawData, &dwReadBytes, sectionHeader.PointerToRawData, FILE_BEGIN, true))
        {
            break;
        }

        PIMAGE_RESOURCE_DIRECTORY pDirectoryRoot = (PIMAGE_RESOURCE_DIRECTORY)lpSectionData;
        DWORD dwTpyeCount = pDirectoryRoot->NumberOfNamedEntries + pDirectoryRoot->NumberOfIdEntries;
        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);
                WCHAR szName[MAX_PATH] = { 0 };
                memcpy_s(szName, sizeof(szName), pStrName->NameString, pStrName->Length * sizeof(WCHAR));
                GroupInfo.TypeName = _WStrToTStr(szName);
                GroupInfo.TypeID = 0;
            }
            else
            {
                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;
                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.SectionOffset = pDataEntry->OffsetToData - sectionHeader.VirtualAddress;
                    info.Size = pDataEntry->Size;

                    GroupInfo.Items.push_back(info);
                }
            }

            m_ResourceInfo.ResourceTable.push_back(GroupInfo);
        }

        fResult = true;

        _LoadResourceInformation(lpSectionData);

    } while (false);

    // 释放节数据缓冲
    if (lpSectionData)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpSectionData);
    }

    return fResult;
}

bool CPEHelper::_LoadResourceInformation(const LPBYTE lpSectionData)
{
    // 解析各种资源类型
    for (const auto& item : m_ResourceInfo.ResourceTable)
    {
        if (MAKEINTRESOURCE(item.TypeID) == RT_STRING)
        {
            // 加载 字符串表 资源
            _LoadResourceStringTable(lpSectionData, item);
        }
        else if (MAKEINTRESOURCE(item.TypeID) == RT_MANIFEST)
        {
            // 加载 Manifest 资源
            _LoadResourceManifest(lpSectionData, item);
        }
        else if (MAKEINTRESOURCE(item.TypeID) == RT_VERSION)
        {
            // 加载 版本 资源
            _LoadResourceVersion(lpSectionData, item);
        }
    }

    return true;
}

bool CPEHelper::_LoadResourceStringTable(
    const LPBYTE lpSection,
    const RESOURCE_GROUP_INFO& info
)
{
    LPWSTR lpStrBuf = nullptr;
    DWORD dwStringBufSize = UINT16_MAX;
    lpStrBuf = (LPWSTR)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, dwStringBufSize);
    if (NULL == lpStrBuf)
    {
        return false;
    }

    for (const auto& str : info.Items)
    {
        std::vector strList;
        WORD wIdBegin = (str.ID - 1) * 16;
        LPWORD lpLength = (LPWORD)((LPBYTE)lpSection + str.SectionOffset);
        for (WORD i = wIdBegin; i < wIdBegin + 16; i++)
        {
            if (0 != *lpLength)
            {
                memcpy_s(lpStrBuf, dwStringBufSize, lpLength + 1, *lpLength * sizeof(WCHAR));
                lpStrBuf[*lpLength] = L'\0';
                STRING_TEXT strText;
                strText.StrText = _WStrToTStr(lpStrBuf);
                strText.ID = i;
                strList.push_back(strText);
                lpLength += *lpLength;
            }
            lpLength++;
        }

        auto itFind = m_ResourceInfo.StringTable.find(str.LangID);
        if (m_ResourceInfo.StringTable.end() == itFind)
        {
            m_ResourceInfo.StringTable.insert(std::make_pair(str.LangID, strList));
        }
        else
        {
            itFind->second.insert(itFind->second.end(), strList.begin(), strList.end());
        }
    }

    if (NULL != lpStrBuf)
    {
        ::HeapFree(::GetProcessHeap(), 0, lpStrBuf);
    }

    return true;
}

bool CPEHelper::_LoadResourceManifest(
    const LPBYTE lpSection,
    const RESOURCE_GROUP_INFO& info
)
{
    for (const auto& str : info.Items)
    {
        LPCSTR lpStr = (LPCSTR)((LPBYTE)lpSection + str.SectionOffset);
        m_ResourceInfo.Manifest = _U8StrToTStr(lpStr);
    }

    return true;
}

bool CPEHelper::_LoadResourceVersion(
    const LPBYTE lpSection,
    const RESOURCE_GROUP_INFO& info
)
{
    for (const auto& str : info.Items)
    {
        PVS_VERSIONINFO lpVersion = (PVS_VERSIONINFO)((LPBYTE)lpSection + str.SectionOffset);

        // 存在 VS_FIXEDFILEINFO 信息
        if (0 != lpVersion->wValueLength)
        {
            VS_FIXEDFILEINFO* pFixedFileInfo = (VS_FIXEDFILEINFO*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO));
            pFixedFileInfo = (VS_FIXEDFILEINFO*)_GetAlignAddr(pFixedFileInfo, sizeof(DWORD));
            m_ResourceInfo.VersionInfo.FixedFileInfo = *pFixedFileInfo;
        }

        // 解析 StringFileInfo
        StringFileInfo* pStringFileInfo = (StringFileInfo*)((LPBYTE)lpVersion + sizeof(VS_VERSIONINFO) + lpVersion->wValueLength);
        pStringFileInfo = (StringFileInfo*)_GetAlignAddr(pStringFileInfo, sizeof(DWORD));
        {
            StringTable* pStringTable = (StringTable*)(pStringFileInfo->Children);

            STRING_FILE_INFO strFileInfo;
            WCHAR szCodeBuf[MAX_PATH] = { 0 };
            memcpy_s(szCodeBuf, sizeof(szCodeBuf), pStringTable->szKey, sizeof(pStringTable->szKey));
            strFileInfo.StringCode = _WStrToTStr(szCodeBuf);

            LPBYTE lpBegin = (LPBYTE)pStringTable->Children;
            LPBYTE lpEnd = lpBegin + pStringTable->wLength - sizeof(STRING_FILE_INFO);

            while (lpBegin < lpEnd)
            {
                String* pString = (String*)lpBegin;
                WCHAR szBufKey[MAX_PATH] = { 0 };
                WCHAR szBufValue[MAX_PATH] = { 0 };
                DWORD dwValueSize = pString->wValueLength;
                STRING_FILE_ITEM item;

                //字符串类型
                if (1 == pString->wType)
                {
                    dwValueSize *= sizeof(WORD);
                }

                //值名
                LPCWSTR lpKey = pString->szKey;
                for (DWORD i = 0; 0 != *lpKey; i++, lpKey++)
                {
                    szBufKey[i] = *lpKey;
                }
                lpKey++;
                item.Key = _WStrToTStr(szBufKey);

                //值数据位置
                lpKey = (LPCWSTR)_GetAlignAddr((LPVOID)lpKey, sizeof(DWORD));
                item.wType = pString->wType;

                //字符串类型
                if (1 == pString->wType)
                {
                    memcpy_s(szBufValue, sizeof(szBufValue), lpKey, dwValueSize);
                    item.Value = _WStrToTStr(szBufValue);
                }
                //字节数据类型
                else
                {
                    memcpy_s(szBufValue, sizeof(szBufValue), lpKey, dwValueSize);
                    item.Value = _WStrToTStr(szBufValue);

                    item.Data.resize(dwValueSize);
                    LPBYTE lpData = (LPBYTE)lpKey;
                    for (DWORD i = 0; i < dwValueSize; i++)
                    {
                        item.Data[i] = lpData[i];
                    }
                }

                strFileInfo.StringInfos.push_back(item);

                lpBegin += pString->wLength;
                lpBegin = (LPBYTE)_GetAlignAddr(lpBegin, sizeof(DWORD));
            }

            m_ResourceInfo.VersionInfo.StringFileInfo.push_back(strFileInfo);
        }

        // 解析 VarFileInfo
        VarFileInfo* pVerFileInfo = (VarFileInfo*)((LPBYTE)pStringFileInfo + pStringFileInfo->wLength);
        pVerFileInfo = (VarFileInfo*)_GetAlignAddr(pVerFileInfo, sizeof(DWORD));
        if ((LPBYTE)pVerFileInfo < ((LPBYTE)lpVersion + lpVersion->wLength))
        {
            Var* pVar = pVerFileInfo->Children;
            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);
            }
        }
    }

    return true;
}

void CPEHelper::PrintResourceTable(bool fShowDetail/* = true*/)
{
    _tprintf(_T("资源类型数量: %4d\n"), (int)m_ResourceInfo.ResourceTable.size());
    int i = 0;
    for (const auto& ResourceInfo : m_ResourceInfo.ResourceTable)
    {
        if (0 != ResourceInfo.TypeID)
        {
            _tprintf(_T("    %4d 类型ID: %4d 类型名: %s 数量: %d\n"), i++, ResourceInfo.TypeID, ResourceInfo.TypeName.c_str(), (int)ResourceInfo.Items.size());
        }
        else
        {
            _tprintf(_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)
        {
            _tprintf(_T("        %4d 资源ID: %4d  节数据偏移: %08X 大小: %d 语言: %d\n"), j++, item.ID, item.SectionOffset, 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();
    }

    _tprintf(_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)
        {
            _tprintf(_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())
                {
                    _tprintf(_T("        %4d Ordinal: %4d(%04x) Addr: %08x %s \n"), j++, fun.Ordinal, fun.Ordinal, fun.Addr, fun.Name.c_str());
                }
                else
                {
                    _tprintf(_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
            {
                _tprintf(_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();
    }

    _tprintf(_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)
        {
            _tprintf(_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())
            {
                _tprintf(_T("        %4d %4d(%04x) %s\n"), j++, fun.Hint, fun.Hint, fun.Name.c_str());
            }
            else
            {
                _tprintf(_T("        %4d %4d(%04x)\n"), j++, fun.Hint, fun.Hint);
            }
        }
    }
}

void CPEHelper::PrintVersion()
{
    _tprintf(_T("Version:\n"));
    _tprintf(_T("    StringFileInfo conut: %d\n"), (int)m_ResourceInfo.VersionInfo.StringFileInfo.size());
    for (const auto& item : m_ResourceInfo.VersionInfo.StringFileInfo)
    {
        _tprintf(_T("        %s\n"), item.StringCode.c_str());

        for (const auto& info : item.StringInfos)
        {
            if (info.wType)
            {
                _tprintf(_T("            %s: %s\n"), info.Key.c_str(), info.Value.c_str());
            }
            else
            {
                _tprintf(_T("            %s: "), info.Key.c_str());
                _PrintfByte((LPVOID)info.Data.data(), info.Data.size());
                _tprintf(_T("\n"));
            }
        }
    }
    _tprintf(_T("    Translation conut: %d\n"), (int)m_ResourceInfo.VersionInfo.TranslationList.size());
    for (const auto& item : m_ResourceInfo.VersionInfo.TranslationList)
    {
        _tprintf(_T("        0x%04X 0x%04X\n"), item.LanguageID, item.CodePageID);
    }
}

void CPEHelper::PrintManifest()
{
    _tprintf(_T("Manifest:\n"));
    _tprintf(_T("%s\n"), m_ResourceInfo.Manifest.c_str());
}

测试

main.c

#include 
#include 
#include 
#include "CPEHelper.h"
#include "CTimeUtils.h"

int main()
{
    setlocale(LC_ALL, "");

    CPEHelper obj;

    
    //obj.LoadFile(_T(R"(qt-opensource-windows-x86-5.14.2.exe)"));
    //obj.LoadFile(_T(R"(gfx_win_101.3790_101.2114.exe)"));

    int64_t timeBegin = CTimeUtils::GetCurrentTickCount();
    int64_t timeEnd = CTimeUtils::GetCurrentTickCount();
    int nRepeatCount = 100;

    while (true)
    {
        timeBegin = CTimeUtils::GetCurrentTickCount();

        for (int i = 0; i < nRepeatCount; i++)
        {
            //obj.LoadFile(_T(R"(qt-opensource-windows-x86-5.14.2.exe)"), true);
            //obj.LoadFile(_T(R"(shell32.dll)"));
            obj.LoadFile(_T(R"(user32.dll)"), true);
            //obj.LoadFile(_T(R"(CPEUtils.exe)"));
            //obj.LoadFile(_T(R"(shell32.dll)"));
            //obj.LoadFile(_T(R"(KernelBase.dll)"));
            //obj.LoadFile(_T(R"(ntdll.dll)"));
        }
        
        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.PrintVersion();
        _tprintf(_T("\n"));
#else
        obj.PrintExportTable(true, true);
        _tprintf(_T("\n"));
        obj.PrintImportTable(true, true);
        _tprintf(_T("\n"));
        obj.PrintResourceTable(false);
        _tprintf(_T("\n"));
        obj.PrintVersion();
        _tprintf(_T("\n"));
#endif
        //obj.PrintManifest();

        _tprintf(_T("repeat count: %d cost time: %llu ms\n"), nRepeatCount, timeEnd - timeBegin);

        system("pause");
    }

    return 0;
}

C++ PE文件信息解析_第1张图片

C++ PE文件信息解析_第2张图片

C++ PE文件信息解析_第3张图片

C++ PE文件信息解析_第4张图片

C++ PE文件信息解析_第5张图片

你可能感兴趣的:(C++,Win32,Windows,开发语言,C++,Win32,PE文件解析,PE文件信息)