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;
打开文件以进行后续处理,直到调用CloseFile
或Clibpe
对象超出范围并自动在析构函数中关闭文件为止。
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::vector
。PERESFLAT
是一个轻量级结构,只包含指向实际资源数据的指针,不像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