C++ PE文件信息解析

news2025/1/18 20:17:40

尝试解析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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据库⑦_复合查询+内外链接(多表/子查询)

目录 1. 回顾基本查询 2. 多表查询 2.1 笛卡尔积初步过滤 3. 自连接 4. 子查询 4.1 单行子查询 4.2 多行子查询 4.2 多列子查询 4.2 from子句中使用子查询 5. 合并查询 6. 内外链接 6.1 内连接 6.2 左外链接 6.2 右外连接 本篇完。 1. 回顾基本查询 先回顾一下…

51单片机编程应用(C语言):串口通信

目录 通信的基本概念和种类 1.1串行通信与并行通信 ​编辑 1.2同步通信与异步通信 1.3单工&#xff0c;半双工&#xff0c;全双工 1.4通信速率 二、波特率和比特率的关系 串口通信简介&#xff1a; 1.接口标准 RS-232 2、D型9针接口定义 3.通信协议&#xff1a; …

OCP使用web console创建和构建应用

文章目录 环境登录创建project赋予查看权限部署第一个image检查pod扩展应用 部署一个Python应用连接数据库创建secret加载数据并显示国家公园地图 清理参考 环境 RHEL 9.3Red Hat OpenShift Local 2.32 登录 在 crc start 启动crc时&#xff0c;可以看到&#xff1a; .....…

2 月 7 日算法练习- 数据结构-树状数组上二分

问题引入 给出三种操作&#xff0c; 0在容器中插入一个数。 1在容器中删除一个数。 2求出容器中大于a的第k大元素。 树状数组的特点就是对点更新&#xff0c;成段求和&#xff0c;而且常数非常小。原始的树状数组只有两种操作&#xff0c;在某点插入一个数和求1到i的所有数的…

C++,stl,栈stack和队列queue详解

1.栈stack 1.stack基本概念 2.stack常用接口 代码示例&#xff1a; #include<bits/stdc.h> using namespace std;int main() {stack<int> stk;stk.push(7);stk.push(9);stk.push(5);cout << "栈的size为&#xff1a;" << stk.size() <…

“金龙送礼,昂首贺春”—— Anzo Capital给您送五粮液、茅台啦!

“迎龙年&#xff0c;贺新春”—— 值此龙年将至之际&#xff0c;为答谢新老客户一直以来对Anzo Capital昂首资本的信赖和支持&#xff0c;Anzo Capital昂首资本2月入金送礼活动重磅升级&#xff0c;除了京东卡、天猫超市卡、奔富红酒、SKG健康产品、白酒礼盒以外&#xff0c…

RocketMQ客户端实现多种功能

目录 RocketMQ客户端基本流程 消息确认机制 1、消息生产端采用消息确认加多次重试的机制保证消息正常发送到RocketMQ 单向发送 同步发送 异步发送 2、消息消费者端采用状态确认机制保证消费者一定能正常处理对应的消息 3、消费者也可以自行指定起始消费位点 广播消息 …

学习Pytorch深度学习运行AlexNet代码时关于在Pycharm中解决 “t >= 0 t < n_classes” 的断言错误方法

在学习深度学习的过程中&#xff0c;遇到了一个报错&#xff1a; 这跑的代码是AlexNet的代码实现。 运行时出现报错&#xff1a; C:\cb\pytorch_1000000000000\work\aten\src\ATen\native\cuda\Loss.cu:257: block: [0,0,0], thread: [4,0,0] Assertion t > 0 && t…

[职场] 公务员面试停顿磕巴常见吗 #学习方法#知识分享#知识分享

公务员面试停顿磕巴常见吗 面试时说话磕巴简直是太常见了&#xff0c;对于一个新问题&#xff0c;让人在短时间内&#xff0c;并且仅仅是三分钟内&#xff0c;就组织起一个答案&#xff0c;还无法全部打手稿&#xff0c;这对于连上个讲台都会脸发红的人来说&#xff0c;简直是一…

前端JavaScript篇之如何获得对象非原型链上的属性?

目录 如何获得对象非原型链上的属性&#xff1f; 如何获得对象非原型链上的属性&#xff1f; 要获取对象上非原型链上的属性&#xff0c;可以使用 hasOwnProperty() 方法。这个方法是 JavaScript 内置的对象方法&#xff0c;用于检查一个对象是否包含指定名称的属性&#xff0…

TP-LINK今年的年终奖。。

TP-LINK 年终奖 如果说昨天爆料的「浦发银行年终奖&#xff0c;一书抵万金」还稍有争议&#xff08;有些说没发&#xff0c;有些说 3/4/5 折&#xff09;&#xff0c;那今天的 TP-LINK 则是毫无悬念。 据在职的 TP-LINK 技术员工爆料&#xff1a;入职时说好的 16 薪&#xff0c…

day45_maven_tomcat

今日内容 0 复习昨日 1 maven 2 tomcat 3 创建项目 0 复习昨日 1 单词写5遍 argument 参数 parameter 参数 access 访问 field 字段 invoke 调用 illegal 非法 invalid 无效 column 列 property 属性 DataSource 数据源 2 数据库连接池有啥好处 3 获得字节码文件的方式 Class.f…

ChatGPT高效提问—prompt常见用法(续篇七)

ChatGPT高效提问—prompt常见用法&#xff08;续篇七&#xff09; 1.1 零样本、单样本和多样本 ​ ChatGPT拥有令人惊叹的功能和能力&#xff0c;允许用户自由向其提问&#xff0c;无须提供任何具体的示例样本&#xff0c;就可以获得精准的回答。这种特性被称为零样本&#x…

使用CHATGPT进行论文写作的缺点和风险

为了真正感受 ChatGPT 的写作潜力&#xff0c;让我们先将其与传统的论文写作方法进行一下比较分析 CHATGPT论文写作的缺点和风险 传统论文写作的考验和磨难很深&#xff1a;费力的研究、组织想法和精心设计的逻辑论证&#xff0c;往往以牺牲你的理智为代价。 进入ChatGPT&am…

Linux下的多用户管理和认证:从入门到精通(附实例)

Linux操作系统以其强大的多用户管理和认证机制而著称。这种机制不仅允许多个用户同时登录并执行各种任务&#xff0c;还能确保每个用户的数据安全和隐私。本文将通过一系列实例&#xff0c;带你逐步掌握Linux下的多用户管理和认证。 一、Linux多用户管理的基础知识 在Linux中&…

Bootstrap学习三

Bootstrap学习三 文章目录 前言四、Bootstrap插件4.1. 插件概览4.1.1. data属性4.1.2. 编程方式的API4.1.3. 避免命名空间冲突4.1.4. 事件 4.2. 模态框4.2.1. 引入4.2.2. 基本结构4.2.3. 基本使用4.2.4. 触发模态框的方法 4.3. 下拉菜单和滚动监听4.3.1. 下拉菜单4.3.2. 滚动监…

祝大家春节快乐

文章目录 祝福年俗交流 祝福 今天是大年三十&#xff0c;也就是除夕&#xff0c;这是全画人民欢庆春节的日子&#xff0c;在此辞旧迎新之际&#xff0c;我祝愿所有的粉丝们春节快乐&#xff0c;身体健康&#xff0c;万事如意。也祝愿我们伟大的祖国繁荣昌盛&#xff0c;龙腾虎…

《MySQL 简易速速上手小册》第9章:高级 MySQL 特性和技巧(2024 最新版)

文章目录 9.1 使用存储过程和触发器9.1.1 基础知识9.1.2 重点案例&#xff1a;使用 Python 调用存储过程实现用户注册9.1.3 拓展案例 1&#xff1a;利用触发器自动记录数据更改历史9.1.4 拓展案例 2&#xff1a;使用 Python 和触发器实现数据完整性检查 9.2 管理和查询 JSON 数…

基于LLM的数据漂移和异常检测

大型语言模型 (LLM) 的最新进展被证明是许多领域的颠覆性力量&#xff08;请参阅&#xff1a;通用人工智能的火花&#xff1a;GPT-4 的早期实验&#xff09;。 和许多人一样&#xff0c;我们非常感兴趣地关注这些发展&#xff0c;并探索LLM影响数据科学和机器学习领域的工作流程…

你的立身之本是什么?

去年发生的一切&#xff0c;大到疫情、政治经济形势、行业的萎靡和震荡&#xff0c;小到身边的跳槽、裁员、公司倒闭……似乎都在告诉我们&#xff1a; 当冲击到来的时候&#xff0c;它是不会提前跟你打招呼的。 接下来的10年&#xff0c;我们所面临的不确定性&#xff0c;比起…