libpe:PE32/PE32+ 二进制解析库

PE32/PE32 +二进制文件内部结构解析库

介绍

libpe是一个用于解析PE32(x86)PE32+(x64)二进制文件的轻量级且非常快速的库,实现为C++20模块。

目录

特点

  • 适用于PE32(x86)PE32+(x64)二进制文件
  • 获取所有PE32/PE32+数据结构:
    • MSDOS头
    • «Rich»头
    • NT/文件/可选头
    • 数据目录
    • 导出表
    • 导入表
    • 资源表
    • 异常表
    • 安全表
    • 重定位表
    • 调试表
    • TLS表
    • 加载配置目录
    • 绑定导入表
    • 延迟导入表
    • COM表

Pepper是构建在libpe之上的应用程序之一。

用法

import libpe;

int main() {
    libpe::Clibpe pe(L"C:\\myFile.exe"); //或者 pe.OpenFile(L"C:\\myFile.exe");
    const auto peImp = pe.GetImport();
    if(peImp) {
    ...
    }
    ...
}

方法

OpenFile

auto OpenFile(const wchar_t* pwszFile)->int;

打开文件以进行后续处理,直到调用CloseFileClibpe对象超出范围并自动在析构函数中关闭文件为止。

libpe::Clibpe pe;
if(pe.OpenFile(L"C:\\MyFile.exe") == PEOK) {
    ...
}

CloseFile();

void CloseFile();

显式关闭先前使用OpenFile(const wchar_t*)打开的文件。此方法将在Clibpe析构函数中自动调用。

GetDOSHeader

[[nodiscard]] auto GetDOSHeader()const->std::optional<IMAGE_DOS_HEADER>;

返回文件的标准MSDOS头部。

GetRichHeader

[[nodiscard]] auto GetRichHeader()const->std::optional<PERICHHDR_VEC>;

返回非官方且未公开的所谓«Rich»结构的数组。

struct PERICHHDR {
    DWORD dwOffset; //文件入口的原始偏移量。
    WORD  wId;      //入口ID。
    WORD  wVersion; //入口版本。
    DWORD dwCount;  //出现次数。
};
using PERICHHDR_VEC = std::vector<PERICHHDR>;

GetNTHeader

[[nodiscard]] auto GetNTHeader()const->std::optional<PENTHDR>;

返回文件的NT头部。

struct PENTHDR {
    DWORD dwOffset;   //文件头的原始偏移量。
    union UNPENTHDR { //x86或x64 NT头部的联合体。
        IMAGE_NT_HEADERS32 stNTHdr32; //x86头部。
        IMAGE_NT_HEADERS64 stNTHdr64; //x64头部。
    } unHdr;
};

GetDataDirs

[[nodiscard]] auto GetDataDirs()const->std::optional<PEDATADIR_VEC>;

返回文件的数据目录结构体数组。

struct PEDATADIR {
    IMAGE_DATA_DIRECTORY stDataDir;  //标准头部。
    std::string          strSection; //此目录所在的段的名称(指向)。
};
using PEDATADIR_VEC = std::vector<PEDATADIR>;

GetSecHeaders

[[nodiscard]] auto GetSecHeaders()const->std::optional<PESECHDR_VEC>;

返回文件的节头结构体数组。

struct PESECHDR {
    DWORD                dwOffset;   //此节头描述符的文件原始偏移量。
    IMAGE_SECTION_HEADER stSecHdr;   //标准节头。
    std::string          strSecName; //节全名。
};
using PESECHDR_VEC = std::vector<PESECHDR>;

GetExport

[[nodiscard]] auto GetExport()const->std::optional<PEEXPORT>;

返回文件的导出信息。

struct PEEXPORTFUNC {
    DWORD       dwFuncRVA;        //函数的RVA地址。
    DWORD       dwOrdinal;        //函数的序数。
    DWORD       dwNameRVA;        //名称的RVA地址。
    std::string strFuncName;      //函数名称。
    std::string strForwarderName; //函数的转发名称。
};
struct PEEXPORT {
    DWORD                     dwOffset;      //导出头描述符的文件原始偏移量。
    IMAGE_EXPORT_DIRECTORY    stExportDesc;  //标准导出头描述符。
    std::string               strModuleName; //实际模块名称。
    std::vector<PEEXPORTFUNC> vecFuncs;      //导出函数结构的数组。	
};

示例:

libpe::Clibpe pe(L"PATH_TO_PE_FILE");
const auto peExport = pe.GetExport();
if (!peExport) {
    return;
}

peExport->stExportDesc;  //IMAGE_EXPORT_DIRECTORY 结构。
peExport->strModuleName; //导出模块名称。
peExport->vecFuncs;      //导出函数的向量。

for (const auto& itFuncs : peExport->vecFuncs) {
    itFuncs.strFuncName;      //函数名称。
    itFuncs.dwOrdinal;        //序数。
    itFuncs.dwFuncRVA;        //函数的RVA地址。
    itFuncs.strForwarderName; //转发名称。
}

GetImport

[[nodiscard]] auto GetImport()const->std::optional<PEIMPORT_VEC>;

返回文件的导入表条目数组。

struct PEIMPORTFUNC {
    union UNPEIMPORTTHUNK {
    	IMAGE_THUNK_DATA32 stThunk32; //x86标准thunk。
	    IMAGE_THUNK_DATA64 stThunk64; //x64标准thunk。
	} unThunk;
    IMAGE_IMPORT_BY_NAME stImpByName; //标准IMAGE_IMPORT_BY_NAME结构。
    std::string          strFuncName; //函数名称。
};
struct PEIMPORT {
    DWORD                     dwOffset;      //导入描述符的文件原始偏移量。
    IMAGE_IMPORT_DESCRIPTOR   stImportDesc;  //标准导入描述符。
    std::string               strModuleName; //导入的模块名称。
    std::vector<PEIMPORTFUNC> vecImportFunc; //导入函数的数组。
};
using PEIMPORT_VEC = std::vector<PEIMPORT>;

示例:

libpe::Clibpe pe(L"C:\\Windows\\notepad.exe");
const auto peImp = pe.GetImport();
if (!peImp) {
    return -1;
}

for (const auto& itModule : *peImp) { //循环遍历PE文件中包含的所有导入。
    std::cout << std::format("{}, Imported funcs: {}\r\n", 
                   itModule.strModuleName, itModule.vecImportFunc.size());
    for (const auto& itFuncs : itModule.vecImportFunc) { //循环遍历从itModule模块导入的所有函数。
        itFuncs.strFuncName;       //导入的函数名称。
        itFuncs.stImpByName;       //该函数的IMAGE_IMPORT_BY_NAME结构。
        itFuncs.unThunk.stThunk32; //IMAGE_THUNK_DATA32或IMAGE_THUNK_DATA64的联合(取决于PE类型)。
    }
}

GetResources

[[nodiscard]] auto GetResources()const->std::optional<PERESROOT>;

返回文件的所有资源。

示例:

下面的代码段用资源的类型和名称填充std::wstring,并将其输出到标准std::wcout

#include <format>
#include <iostream>
#include <string>

import libpe;
using namespace libpe;

int main()
{
    libpe::Clibpe pe;
    if (pe.OpenFile(L"C:\\Windows\\notepad.exe") != PEOK) {
        return -1;
    }

    const auto peResRoot = pe.GetResources();
    if (!peResRoot) {
        return -1;
    }

    std::wstring wstrResData; //此wstring将包含所有资源的名称。
    for (const auto& iterRoot : peResRoot->vecResData) { //提取资源的主循环。
        auto ilvlRoot = 0;
        auto pResDirEntry = &iterRoot.stResDirEntry; //根IMAGE_RESOURCE_DIRECTORY_ENTRY
        if (pResDirEntry->NameIsString) {
            wstrResData += std::format(L"Entry: {} 
                           [Name: {}]\r\n", ilvlRoot, iterRoot.wstrResName);
        }
        else {
            if (const auto iter = MapResID.find(pResDirEntry->Id); 
                                  iter != MapResID.end()) {
                wstrResData += std::format(L"Entry: {} 
                    [Id: {}, {}]\r\n", ilvlRoot, pResDirEntry->Id, iter->second);
            }
            else {
                wstrResData += std::format(L"Entry: {} 
                               [Id: {}]\r\n", ilvlRoot, pResDirEntry->Id);
            }
        }

        if (pResDirEntry->DataIsDirectory) {
            auto ilvl2 = 0;
            auto pstResLvL2 = &iterRoot.stResLvL2;
            for (const auto& iterLvL2 : pstResLvL2->vecResData) {
                pResDirEntry = &iterLvL2.stResDirEntry; //第2级IMAGE_RESOURCE_DIRECTORY_ENTRY
                if (pResDirEntry->NameIsString) {
                    wstrResData += std::format
                    (L"    Entry: {}, Name: {}\r\n", ilvl2, iterLvL2.wstrResName);
                }
                else {
                    wstrResData += std::format
                    (L"    Entry: {}, Id: {}\r\n", ilvl2, pResDirEntry->Id);
                }

                if (pResDirEntry->DataIsDirectory) {
                    auto ilvl3 = 0;
                    auto pstResLvL3 = &iterLvL2.stResLvL3;
                    for (const auto& iterLvL3 : pstResLvL3->vecResData) {
                        pResDirEntry = &iterLvL3.stResDirEntry; //第3级IMAGE_RESOURCE_DIRECTORY_ENTRY
                        if (pResDirEntry->NameIsString) {
                            wstrResData += std::format
                            (L"        Entry: {}, Name: {}\r\n", ilvl3, iterLvL3.wstrResName);
                        }
                        else {
                            wstrResData += std::format
                            (L"        Entry: {}, lang: {}\r\n", ilvl3, pResDirEntry->Id);
                        }
                        ++ilvl3;
                    }
                }
                ++ilvl2;
            }
        }
        ++ilvlRoot;
    }
    std::wcout << wstrResData;

GetExceptions

[[nodiscard]] auto GetExceptions()const->std::optional<PEEXCEPTION_VEC>;

返回文件的异常条目数组。

struct PEEXCEPTION {
    DWORD                         dwOffset;           //文件的异常描述符的原始偏移
    _IMAGE_RUNTIME_FUNCTION_ENTRY stRuntimeFuncEntry; //标准_IMAGE_RUNTIME_FUNCTION_ENTRY头。
};
using PEEXCEPTION_VEC = std::vector<PEEXCEPTION>;

GetSecurity

[[nodiscard]] auto GetSecurity()const->std::optional<PESECURITY_VEC>;

返回文件的安全条目数组。

struct PEWIN_CERTIFICATE { //完全复制自<WinTrust.h>中的WIN_CERTIFICATE结构体。
    DWORD dwLength;
    WORD  wRevision;
    WORD  wCertificateType;
    BYTE  bCertificate[1];
};
struct PESECURITY {
    DWORD             dwOffset;  //文件的安全描述符的原始偏移。
    PEWIN_CERTIFICATE stWinSert; //标准WIN_CERTIFICATE结构体。
};
using PESECURITY_VEC = std::vector<PESECURITY>;

GetRelocations

[[nodiscard]] auto GetRelocations()const->std::optional<PERELOC_VEC>;

返回文件的重定位信息数组。

struct PERELOCDATA {
    DWORD dwOffset;     //文件的重定位数据描述符的原始偏移。
    WORD  wRelocType;   //重定位类型。
    WORD  wRelocOffset; //重定位偏移(必须应用的重定位的偏移)。
};
struct PERELOC {
    DWORD                    dwOffset;     //文件的重定位描述符的原始偏移。
    IMAGE_BASE_RELOCATION    stBaseReloc;  //标准IMAGE_BASE_RELOCATION头。
    std::vector<PERELOCDATA> vecRelocData; //重定位数据结构的数组。
};
using PERELOC_VEC = std::vector<PERELOC>;

GetDebug

[[nodiscard]] auto GetDebug()const->std::optional<PEDEBUG_VEC>;

返回文件的调试条目数组。

struct PEDEBUGDBGHDR {
    //dwHdr[6]是IMAGE_DEBUG_DIRECTORY::PointerToRawData数据(调试信息头)的前六个DWORD的数组。
    //它们的含义取决于dwHdr[0](Signature)的值。
    //如果dwHdr[0] == 0x53445352(Ascii "RSDS"),它是PDB 7.0文件:
    //然后dwHdr[1]-dwHdr[4]是GUID(*((GUID*)&dwHdr[1]))。dwHdr[5]是Counter/Age。
    //如果dwHdr[0] == 0x3031424E(Ascii "NB10"),它是PDB 2.0文件:
    //然后dwHdr[1]是Offset。dwHdr[2]是Time/Signature。dwHdr[3]是Counter/Age。
    DWORD       dwHdr[6];
    std::string strPDBName; //PDB文件名/路径。
};
struct PEDEBUG {
    DWORD                 dwOffset;       //文件的调试描述符的原始偏移。
    IMAGE_DEBUG_DIRECTORY stDebugDir;     //标准IMAGE_DEBUG_DIRECTORY头。
    PEDEBUGDBGHDR         stDebugHdrInfo; //调试信息头。
};
using PEDEBUG_VEC = std::vector<PEDEBUG>;

GetTLS

[[nodiscard]] auto GetTLS()const->std::optional<PETLS>;

返回文件的线程局部存储信息。

struct PETLS {
    DWORD              dwOffset;          //文件的TLS头描述符的原始偏移。
    union UNPETLS {
    	IMAGE_TLS_DIRECTORY32 stTLSDir32; //x86标准TLS头。
    	IMAGE_TLS_DIRECTORY64 stTLSDir64; //x64 TLS头。
    } unTLS;
    std::vector<DWORD> vecTLSCallbacks;   //TLS回调函数的数组。
};

GetLoadConfig

[[nodiscard]] auto GetLoadConfig()const->std::optional;

返回文件的加载配置目录信息。

struct PELOADCONFIG {
    DWORD dwOffset;                            //文件的LCD描述符的原始偏移量。
    union UNPELOADCONFIG {
        IMAGE_LOAD_CONFIG_DIRECTORY32 stLCD32; //x86 LCD描述符。
        IMAGE_LOAD_CONFIG_DIRECTORY64 stLCD64; //x64 LCD描述符。
    } unLCD;
};

GetBoundImport

[[nodiscard]] auto GetBoundImport()const->std::optional;

返回文件的绑定导入条目的数组。

struct PEBOUNDFORWARDER {
    DWORD                     dwOffset;              //文件的BF描述符的原始偏移量。
    IMAGE_BOUND_FORWARDER_REF stBoundForwarder;      //标准IMAGE_BOUND_FORWARDER_REF结构。
    std::string               strBoundForwarderName; //绑定导入名称。
};
struct PEBOUNDIMPORT {
    DWORD                         dwOffset;          //文件的BI描述符的原始偏移量。
    IMAGE_BOUND_IMPORT_DESCRIPTOR stBoundImpDesc;    //标准IMAGE_BOUND_IMPORT_DESCRIPTOR结构。
    std::string                   strBoundName;      //绑定导入名称。
    std::vector vecBoundForwarder; //绑定导入结构的数组。
};
using PEBOUNDIMPORT_VEC = std::vector;

GetDelayImport

[[nodiscard]] auto GetDelayImport()const->std::optional;

返回文件的延迟导入条目的数组。

struct PEDELAYIMPORTFUNC {
    union UNPEDELAYIMPORTTHUNK {
        struct x32 {
            IMAGE_THUNK_DATA32 stImportAddressTable;      //x86导入地址表结构。
            IMAGE_THUNK_DATA32 stImportNameTable;         //x86导入名称表结构。
            IMAGE_THUNK_DATA32 stBoundImportAddressTable; //x86绑定导入地址表结构。
            IMAGE_THUNK_DATA32 stUnloadInformationTable;  //x86卸载信息表结构。
        } st32;
        struct x64 {
            IMAGE_THUNK_DATA64 stImportAddressTable;      //x64导入地址表结构。
            IMAGE_THUNK_DATA64 stImportNameTable;         //x64导入名称表结构。
            IMAGE_THUNK_DATA64 stBoundImportAddressTable; //x64绑定导入地址表结构。
            IMAGE_THUNK_DATA64 stUnloadInformationTable;  //x64卸载信息表结构。
        } st64;
    } unThunk;
    IMAGE_IMPORT_BY_NAME stImpByName; //标准IMAGE_IMPORT_BY_NAME结构。
    std::string          strFuncName; //函数名称。
};
struct PEDELAYIMPORT {
    DWORD                          dwOffset;        //文件的延迟导入描述符的原始偏移量。
    IMAGE_DELAYLOAD_DESCRIPTOR     stDelayImpDesc;  //标准IMAGE_DELAYLOAD_DESCRIPTOR结构。
    std::string                    strModuleName;   //导入模块名称。
    std::vector vecDelayImpFunc; //延迟导入模块函数的数组。
};
using PEDELAYIMPORT_VEC = std::vector;

GetCOMDescriptor

[[nodiscard]] auto GetCOMDescriptor()const->std::optional;

获取文件的.NET信息。

struct PECOMDESCRIPTOR {
    DWORD              dwOffset; //文件的IMAGE_COR20_HEADER描述符的原始偏移量。
    IMAGE_COR20_HEADER stCorHdr; //标准IMAGE_COR20_HEADER结构。
};

辅助方法

这些独立的方法不需要一个具有打开文件的活动Clibpe对象,而是采用对先前获得的结构的引用。

GetFileType

[[nodiscard]] inline constexpr auto GetFileType(const PENTHDR& stNTHdr)->EFileType

返回以EFileType枚举形式表示的PE文件类型。

enum class EFileType : std::uint8_t {
    UNKNOWN = 0, PE32, PE64, PEROM
};

GetImageBase

[[nodiscard]] inline constexpr auto GetImageBase(const PENTHDR& stNTHdr)->ULONGLONG

返回文件的Image Base

GetOffsetFromRVA

[[nodiscard]] inline constexpr auto GetOffsetFromRVA
              (ULONGLONG ullRVA, const PESECHDR_VEC& vecSecHdr)->DWORD

将文件的RVA转换为文件在磁盘上的物理原始偏移。

FlatResources

[[nodiscard]] inline constexpr auto FlatResources(const PERESROOT& stResRoot)

此函数是GetResources方法的简化版本。它接收由GetResources返回的PERESROOT结构,并返回PERESFLAT结构的std::vectorPERESFLAT是一个轻量级结构,只包含指向实际资源数据的指针,不像PERESROOT那样重。 FlatResources会将所有资源转换成一维数组,以便更方便地访问。

struct PERESFLAT {
    std::span<const std::byte> spnData { };    //资源数据。
    std::wstring_view          wsvTypeStr { }; //资源类型名称。
    std::wstring_view          wsvNameStr { }; //资源名称(资源本身的名称)。
    std::wstring_view          wsvLangStr { }; //资源语言名称。
    WORD                       wTypeID { };    //资源类型ID(RT_CURSOR, RT_BITMAP等)。
    WORD                       wNameID { };    //资源名称ID(资源本身的ID)。
    WORD                       wLangID { };    //资源语言ID。
};
using PERESFLAT_VEC = std::vector<PERESFLAT>;

Maps

PE文件由许多结构组成,这些结构又包含许多字段,其中一些字段具有预定义的值。
这些映射旨在将这些字段转换为可读的格式。它们是简单的std::unordered_map<DWORD, std::wstring_view>映射。

请注意,某些字段只能具有一个值,而其他字段可以使用按位或 |运算符组合多个值。

MapFileHdrMachine

此映射形成IMAGE_NT_HEADERS::IMAGE_FILE_HEADER::Machine字段中的一个值。

MapFileHdrCharact

此映射形成IMAGE_NT_HEADERS::IMAGE_FILE_HEADER::Characteristics字段中的一个或多个值。

const auto pNTHdr = m_pLibpe->GetNTHeader();
const auto pDescr = &pNTHdr->unHdr.stNTHdr32.FileHeader; //同x86/x64一样。
std::wstring  wstrCharact;
for (const auto& flags : MapFileHdrCharact) {
    if (flags.first & pDescr->Characteristics) {
        wstrCharact += flags.second;
        wstrCharact += L"\n";
    }
}

MapOptHdrMagic

此映射形成IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Magic字段中的一个值。

MapOptHdrSubsystem

此映射形成IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Subsystem字段中的一个值。

MapOptHdrDllCharact

此映射形成IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::DllCharacteristics字段中的一个或多个值。

const auto pNTHdr = m_pLibpe->GetNTHeader();
const auto pOptHdr = &pNTHdr->unHdr.stNTHdr32.OptionalHeader //对于 x64: 
                                    //pNTHdr->unHdr.stNTHdr64.OptionalHeader
std::wstring wstrCharact;
for (const auto& flags : MapOptHdrDllCharact) {
    if (flags.first & pOptHdr->DllCharacteristics) {
        wstrCharact += flags.second;
        wstrCharact += L"\n";
    }
}

MapSecHdrCharact

这个map的形成一个或多个值从IMAGE_SECTION_HEADER :: Characteristics 字段。

const auto pSecHeaders = m_pLibpe->GetSecHeaders();
std::wstring wstrCharact;
auto IdOfSection = 0; // 期望节的ID。
for (const auto& flags : MapSecHdrCharact) {
    if (flags.first & pSecHeaders->at(IdOfSection).stSecHdr.Characteristics) {
        wstrCharact += flags.second;
        wstrCharact += L"\n";
    }
}

MapResID

这个map的形成一个值从IMAGE_RESOURCE_DIRECTORY_ENTRY :: Id 字段。

MapWinCertRevision

这个map的形成一个值从 WIN_CERTIFICATE :: wRevision 字段。

MapWinCertType

这个map的形成一个值从 WIN_CERTIFICATE :: wCertificateType 字段。

MapRelocType

这个map的形成一个值从 PERELOCDATA :: wRelocType 字段。

MapDbgType

这个map的形成一个值从 IMAGE_DEBUG_DIRECTORY :: Type 字段。

MapTLSCharact

这个map的形成一个值从 IMAGE_TLS_DIRECTORY :: Characteristics 字段。

MapLCDGuardFlags

这个map的形成一个或多个值从 IMAGE_LOAD_CONFIG_DIRECTORY :: GuardFlags 字段。

const auto pLCD = m_pLibpe->GetLoadConfig();
const auto pPELCD = &pLCD->unLCD.stLCD32; // 对于x64:pLCD->unLCD.stLCD64
std::wstring wstrGFlags;
for (const auto& flags : MapLCDGuardFlags) {
    if (flags.first & pPELCD->GuardFlags) {
        wstrGFlags += flags.second;
        wstrGFlags += L"\n";
    }
}

MapCOR20Flags

这个map的形成一个或多个值从 IMAGE_COR20_HEADER :: Flags 字段。

const auto pCOMDesc = m_pLibpe->GetCOMDescriptor();
std::wstring wstrFlags;
for (const auto& flags : MapCOR20Flags) {
    if (flags.first & pCOMDesc->stCorHdr.Flags) {
        wstrFlags += flags.second;
        wstrFlags += L"\n";
    }
}

Leave a Reply

Your email address will not be published. Required fields are marked *