C++ PE文件信息解析

尝试解析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>
#include <mutex>

#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 SectionOffset;        //数据偏移(在节数据中的偏移位置)
    DWORD Size;                 //数据大小

    _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
    // @ret: bool               操作成功与否
    bool LoadFile(const _tstring& strPath, bool fCheckSum = 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: 清空数据
    // @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             对齐后指针
    LPCVOID _GetAlignAddr(LPCVOID 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<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;                                                     //可选头魔数
    std::mutex  m_lock;
};

CPEHelper.cpp

#include "CPEHelper.h"
#include <tchar.h>
#include <imagehlp.h>
#include <thread>
#define STRING_BUFFER_MAX_LENGTH       (512)

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

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

    // 短字符串尽量减少分配内存导致的耗时
    if (str.size() < STRING_BUFFER_MAX_LENGTH)
    {
        //假设 宽字节 转 UTF8, 一个字符最多占用4字节
        CHAR strBuf[STRING_BUFFER_MAX_LENGTH * 4] = { 0 };
        int nConverted = ::WideCharToMultiByte(CodePage, 0, str.c_str(), -1, strBuf, sizeof(strBuf), NULL, NULL);
        if (0 != nConverted)
        {
            strResult = strBuf;
        }
    }
    else
    {
        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;

    // 短字符串尽量减少分配内存导致的耗时
    if (str.size() < STRING_BUFFER_MAX_LENGTH)
    {
        WCHAR strBuf[STRING_BUFFER_MAX_LENGTH] = { 0 };
        int nConverted = ::MultiByteToWideChar(CodePage, 0, str.c_str(), -1, strBuf, _countof(strBuf));
        if (0 != nConverted)
        {
            strResult = strBuf;
        }
    }
    else
    {
        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/* = false*/)
{
    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<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;
}

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*/
)
{
    std::lock_guard<std::mutex> lock(m_lock);

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

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

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

bool CPEHelper::_LoadAllInformation()
{
    std::vector<std::thread> vLoadTasks;

#if 1
    vLoadTasks.push_back(std::thread([this] {

        // 加载资源表
        _LoadResourceTable();
        }));

    vLoadTasks.push_back(std::thread([this] {

        // 加载导出表
        _LoadExportTable();
        }));

    vLoadTasks.push_back(std::thread([this] {

        // 加载导入表
        _LoadImportTable();
        }));

    //等待任务结束
    for (auto& item : vLoadTasks)
    {
        item.join();
    }

#else

    // 加载资源表
    _LoadResourceTable();

    // 加载导出表
    _LoadExportTable();

    // 加载导入表
    _LoadImportTable();

#endif

    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, &sectionHeader))
        {
            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<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, 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, &sectionHeader))
        {
            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)); i++)
            {
                LPCSTR lpDllName = (LPCSTR)_GetSectionDataAddr(lpSectionData, sectionHeader, pImportDesc[i].Name);
                if (NULL == lpDllName)
                {
                    break;
                }

                PIMAGE_THUNK_DATA32 pThunkData = (PIMAGE_THUNK_DATA32)_GetSectionDataAddr(lpSectionData, sectionHeader, 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, sectionHeader, pThunkData->u1.AddressOfData);
                        if (NULL == pImportName)
                        {
                            break;
                        }

                        FunList[nIndex].Hint = pImportName->Hint;
                        FunList[nIndex].Name = _AStrToTStr((LPCSTR)&pImportName->Name);
                    }
                    pThunkData++;
                }

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

        // 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, sectionHeader, pImportDesc[i].Name);
                if (NULL == lpDllName)
                {
                    break;
                }

                PIMAGE_THUNK_DATA64 pThunkData = (PIMAGE_THUNK_DATA64)_GetSectionDataAddr(lpSectionData, sectionHeader, 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, sectionHeader, pThunkData->u1.AddressOfData);
                        if (NULL == pImportName)
                        {
                            break;
                        }

                        FunList[nIndex].Hint = pImportName->Hint;
                        FunList[nIndex].Name = _AStrToTStr((LPCSTR)&pImportName->Name);
                    }
                    nIndex++;
                    pThunkData++;
                }

                m_ImportTable.insert(std::make_pair(_AStrToTStr(lpDllName), 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, &sectionHeader))
        {
            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;
        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.SectionOffset = pDataEntry->OffsetToData - sectionHeader.VirtualAddress;
                    info.Size = pDataEntry->Size;

                    GroupInfo.Items[k] = info;
                }
            }

            m_ResourceInfo.ResourceTable[i] = 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
)
{
    for (const auto& str : info.Items)
    {
        std::vector<STRING_TEXT> 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)
            {
                STRING_TEXT strText;
                strText.StrText = _WStrToTStr(std::wstring((LPCWSTR)lpLength + 1, *lpLength));
                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());
        }
    }

    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(std::string(lpStr, str.Size));
    }

    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));
        {
            STRING_FILE_INFO strFileInfo;
            StringTable* pStringTable = (StringTable*)(pStringFileInfo->Children);
            strFileInfo.StringCode = _WStrToTStr(std::wstring(pStringTable->szKey, _countof(pStringTable->szKey)));

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

            while (lpBegin < lpEnd)
            {
                const String* pString = (String*)lpBegin;
                DWORD dwValueSize = pString->wValueLength;
                STRING_FILE_ITEM item;

                std::wstring strKey = pString->szKey;
                item.Key = _WStrToTStr(strKey);

                //值名
                LPCWSTR lpStrValue = pString->szKey;
                lpStrValue += strKey.size() + 1;

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

                //字符串类型
                if (1 == pString->wType)
                {
                    item.Value = _WStrToTStr(std::wstring(lpStrValue, dwValueSize));
                }
                //字节数据类型
                else
                {
                    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);

                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());

        VS_FIXEDFILEINFO& FixedFileInfo = m_ResourceInfo.VersionInfo.FixedFileInfo;
        _tprintf(_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)
            {
                _tprintf(_T("            %s: %s\n"), info.Key.c_str(), info.Value.c_str());
            }
            else
            {
                _tprintf(_T("            %s: %s\n"), info.Key.c_str(), info.Value.c_str());
                //_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::PrintStringTable()
{
    _tprintf(_T("StringTable: count: %d\n"), (int)m_ResourceInfo.StringTable.size());
    for (const auto& item : m_ResourceInfo.StringTable)
    {
        _tprintf(_T("    ID: %d, count: %d\n"), item.first, (int)item.second.size());

        for (const auto& info : item.second)
        {
            _tprintf(_T("        %04d: %s\n"), info.ID, info.StrText.c_str());
        }
    }
}

void CPEHelper::PrintManifest()
{
    _tprintf(_T("Manifest:\n"));
    _tprintf(_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;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/375788.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux------命令行参数

目录 前言 一、main函数的参数 二、命令行控制实现计算器 三、实现touch指令 前言 当我们在命令行输入 ls -al &#xff0c;可以查看当前文件夹下所有文件的信息&#xff0c;还有其他的如rm&#xff0c;touch等指令&#xff0c;都可以帮我们完成相应的操作。 其实运行这些…

2024-02-06 TCP/UDP work

1. 画出TCP三次握手和四次挥手的示意图&#xff0c;并且总结TCP和UDP的区别 三次握手&#xff1a; 4次挥手&#xff1a; tcp/udp区别 TCP 1. 稳定&#xff0c;提供面向连接的&#xff0c;可靠的数据传输服务 2. 传输过程中&#xff0c;数据无误、数据无丢失、数据无失序、…

python+PyQt5实现指示灯检查

UI: 源代码&#xff1a; # -*- coding: utf-8 -*-# Form implementation generated from reading ui file CheckImageWinFrm.ui # # Created by: PyQt5 UI code generator 5.15.2 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again…

企业邮箱是什么?企业邮箱百科

本文将为大家讲解&#xff1a;1、企业邮箱的定义&#xff1b;2、企业邮箱的主要功能特点&#xff1b;3、企业邮箱如何选择和部署&#xff1b;4、企业邮箱的运营与维护&#xff1b;5、企业邮箱在实际工作中的应用与挑战&#xff1b;6、2024年最新五大企业邮箱盘点   下面提到的…

基础面试题整理6之Redis

1.Redis的应用场景 Redis支持类型&#xff1a;String、hash、set、zset、list String类型 hash类型 set类型 zset类型 list类型 一般用作缓存&#xff0c;例如 如何同时操作同一功能 2.redis是单线程 Redis服务端(数据操作)是单线程&#xff0c;所以Redis是并发安全的,因…

C语言的起源

1940年代&#xff0c;最早的开始&#xff0c;编程语言是机器语言&#xff0c;用0/1表示的、计算机能直接识别和执行的一种机器指令的集合。最早的编程方式&#xff0c;就是给纸带打孔或者卡片机打孔。机器语言直接与硬件沟通&#xff0c;极具针对性&#xff0c;但是非常难于理解…

解密 ARMS 持续剖析:如何用一个全新视角洞察应用的性能瓶颈?

作者&#xff1a;饶子昊、杨龙 应用复杂度提升&#xff0c;根因定位困难重重 随着软件技术发展迭代&#xff0c;很多企业软件系统也逐步从单体应用向云原生微服务架构演进&#xff0c;一方面让应用实现高并发、易扩展、开发敏捷度高等效果&#xff0c;但另外一方面也让软件应…

【分享】如何运用数字I/O来保护继电器

1.简述 在开关系统中&#xff0c;短路或者是开路的情况下&#xff0c;由于存在着额外的电流或者是电压&#xff0c;继电器往往会过载。所有的继电器都有一个最大的承载电流和热切换功率&#xff0c;如果超出了这个范围&#xff0c;会增加继电器焊接在一起的风险&#xff0c;从…

7.electron之渲染线程发送事件,主进程监听事件

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 Electron 将 Chromium 和 Node.js 嵌入到了一个二进制文件中&#xff0c;因此它允许你仅需一个代码仓库&#xff0c;就可以撰写支持 Windows、…

蓝桥杯省赛无忧 课件92 行列式

01 什么是行列式 02 行列式的性质 03 高斯消元求行列式

vue+vite项目,动态导入静态资源的几种方式

博主的桌面工具软件已经正式开发&#xff0c;获取方式&#xff1a; 可以关注我的小程序【中二少年工具箱】获取。&#xff08;若小程序更新有延迟&#xff0c;可先收藏小程序&#xff09; 通过下载链接 百度网盘: 链接&#xff1a;https://pan.baidu.com/s/15zDnSoEzJGSZLjpD…

kafka 文件存储机制

文章目录 1. 思考四个问题&#xff1a;1.1 topic中partition存储分布&#xff1a;1.2 partiton中文件存储方式&#xff1a;1.3 partiton中segment文件存储结构&#xff1a;1.4 在partition中如何通过offset查找message: 2. kafka日志存储参数配置 Topic是逻辑上的概念&#xff…

Damn Small Linux 停更16年后,2024 回归更新

Damn Small Linux(DSL) 发行版释出了最新的 2024 版本&#xff0c;并重新定义了什么叫“Damn Small”。 DSL 诞生于 2005 年&#xff0c;原本是尝试提供一个 50MB 大小的 LiveCD&#xff0c;2008 年开发停滞。 2024 年原作者 John Andrews 宣布 DSL 复活&#xff0c;在几乎所…

微软AD域替代方案,助力企业摆脱护网期间被攻击的窘境

在红蓝攻防演练&#xff08;护网行动&#xff09;中&#xff0c;AD域若被攻击成功&#xff0c;是其中一个扣分最多的一项内容。每年&#xff0c;宁盾都会接到大量AD在护网期间被攻击&#xff0c;甚至是被打穿的企业客户。过去&#xff0c;企业还会借助2FA双因子认证加强OA、Exc…

【C#】MVVM架构

示例结果展示 前提了解 MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发。 Model——可以理解为带有字段,属性的类。例如学校类,教师类,学生类等 View——可以理解为我们所看到的UI。前端界面。 View Model在View和Model之间,起到连接的作用,…

awd总结

总结&#xff1a; 由于是第一次参加AWD比赛&#xff0c;各方面经验都不足&#xff0c;在参赛的前几天也是疯狂搜集各种脚本、框架、工具等&#xff0c;同时也参考b站的视频进行学习&#xff0c;我发现就是还是实操才能更快的学习 我觉得就是我前期的准备工作不足&#xff0c;…

挑战杯 python+大数据校园卡数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于yolov5的深度学习车牌识别系统实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;3分 该项目较为新颖&am…

#免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程

Mac电脑苹果芯片读写NTFS硬盘bash脚本 &#xff08;ntfs.sh脚本内容在本文最后面&#xff09; ntfs.sh脚本可以将Mac系统(苹果M系芯片)上的NTFS硬盘改成可读写的挂载方式&#xff0c;从而可以直接往NTFS硬盘写入数据。此脚本免费&#xff0c;使用过程中无需下载任何收费软件。…

Linux操作系统运维-Docker的基础知识梳理总结

Linux操作系统运维-Docker的基础知识梳理总结 docker用来解决不同开发人员软件调试时环境不统一的问题&#xff0c;保证了程序调试时运行环境的一致性。docker的设计理念便是一处镜像&#xff0c;处处运行&#xff0c;即通过产生用户软件&#xff0c;运行环境及其运行配置的统一…

复旦微 zynq amp cpu0 唤醒启动cpu1

1 配置多核amp工程&#xff0c;参考上一篇文章 https://blog.csdn.net/yangchenglin927/article/details/136057534 2 在cpu0的main函数中增加唤醒代码 active_cpu1(); /** helloworld.c: simple test application** This application configures UART 16550 to baud rate 96…