libpe: PE32/PE32+ Binaries Parsing Library
Library for parsing internal structures of PE32/PE32+ binary files.
libpe is a lightweight and very fast library for parsing PE32(x86) and PE32+(x64) binaries, implemented as a C++20 module.
- Works with both PE32(x86) and PE32+(x64) binaries
- Obtains all PE32/PE32+ data structures:
- MSDOS Header
- «Rich» Header
- NT/File/Optional Headers
- Data Directories
- Sections
- Export Table
- Import Table
- Resource Table
- Exceptions Table
- Security Table
- Relocations Table
- Debug Table
- TLS Table
- Load Config Directory
- Bound Import Table
- Delay Import Table
- COM Table
Pepper is one of the apps that is built on top of the libpe.
import libpe; int main() { libpe::Clibpe pe(L"C:\\myFile.exe"); //or pe.OpenFile(L"C:\\myFile.exe"); const auto peImp = pe.GetImport(); if(peImp) { ... } ... }
auto OpenFile(const wchar_t* pwszFile)->int;
Opens a file for further processing, until CloseFile
is called or Clibpe
object goes out of scope and file closes automatically in destructor.
libpe::Clibpe pe; if(pe.OpenFile(L"C:\\MyFile.exe") == PEOK) { ... }
void CloseFile();
Explicitly closes file that was previously opened with the OpenFile(const wchar_t*)
. This method is invoked automatically in Clibpe
destructor.
[[nodiscard]] auto GetDOSHeader()const->std::optional<IMAGE_DOS_HEADER>;
Returns a file's standard MSDOS header.
[[nodiscard]] auto GetRichHeader()const->std::optional<PERICHHDR_VEC>;
Returns an array of the unofficial and undocumented so called «Rich» structures.
struct PERICHHDR { DWORD dwOffset; //File's raw offset of the entry. WORD wId; //Entry Id. WORD wVersion; //Entry version. DWORD dwCount; //Amount of occurrences. }; using PERICHHDR_VEC = std::vector<PERICHHDR>;
[[nodiscard]] auto GetNTHeader()const->std::optional<PENTHDR>;
Returns a file's NT header.
struct PENTHDR { DWORD dwOffset; //File's raw offset of the header. union UNPENTHDR { //Union of either x86 or x64 NT header. IMAGE_NT_HEADERS32 stNTHdr32; //x86 Header. IMAGE_NT_HEADERS64 stNTHdr64; //x64 Header. } unHdr; };
[[nodiscard]] auto GetDataDirs()const->std::optional<PEDATADIR_VEC>;
Returns an array of file's Data directories structs.
struct PEDATADIR { IMAGE_DATA_DIRECTORY stDataDir; //Standard header. std::string strSection; //Name of the section this directory resides in (points to). }; using PEDATADIR_VEC = std::vector<PEDATADIR>;
[[nodiscard]] auto GetSecHeaders()const->std::optional<PESECHDR_VEC>;
Returns an array of file's Sections headers structs.
struct PESECHDR { DWORD dwOffset; //File's raw offset of this section header descriptor. IMAGE_SECTION_HEADER stSecHdr; //Standard section header. std::string strSecName; //Section full name. }; using PESECHDR_VEC = std::vector<PESECHDR>;
[[nodiscard]] auto GetExport()const->std::optional<PEEXPORT>;
Returns a file's Export information.
struct PEEXPORTFUNC { DWORD dwFuncRVA; //Function RVA. DWORD dwOrdinal; //Function ordinal. DWORD dwNameRVA; //Name RVA. std::string strFuncName; //Function name. std::string strForwarderName; //Function forwarder name. }; struct PEEXPORT { DWORD dwOffset; //File's raw offset of the Export header descriptor. IMAGE_EXPORT_DIRECTORY stExportDesc; //Standard export header descriptor. std::string strModuleName; //Actual module name. std::vector<PEEXPORTFUNC> vecFuncs; //Array of the exported functions struct. };
Example:
libpe::Clibpe pe(L"PATH_TO_PE_FILE"); const auto peExport = pe.GetExport(); if (!peExport) { return; } peExport->stExportDesc; //IMAGE_EXPORT_DIRECTORY struct. peExport->strModuleName; //Export module name. peExport->vecFuncs; //Vector of exported functions. for (const auto& itFuncs : peExport->vecFuncs) { itFuncs.strFuncName; //Function name. itFuncs.dwOrdinal; //Ordinal. itFuncs.dwFuncRVA; //Function RVA. itFuncs.strForwarderName; //Forwarder name. }
[[nodiscard]] auto GetImport()const->std::optional<PEIMPORT_VEC>;
Returns an array of file's Import table entries.
struct PEIMPORTFUNC { union UNPEIMPORTTHUNK { IMAGE_THUNK_DATA32 stThunk32; //x86 standard thunk. IMAGE_THUNK_DATA64 stThunk64; //x64 standard thunk. } unThunk; IMAGE_IMPORT_BY_NAME stImpByName; //Standard IMAGE_IMPORT_BY_NAME struct std::string strFuncName; //Function name. }; struct PEIMPORT { DWORD dwOffset; //File's raw offset of the Import descriptor. IMAGE_IMPORT_DESCRIPTOR stImportDesc; //Standard Import descriptor. std::string strModuleName; //Imported module name. std::vector<PEIMPORTFUNC> vecImportFunc; //Array of imported functions. }; using PEIMPORT_VEC = std::vector<PEIMPORT>;
Example
libpe::Clibpe pe(L"C:\\Windows\\notepad.exe"); const auto peImp = pe.GetImport(); if (!peImp) { return -1; } for (const auto& itModule : *peImp) { //Cycle through all imports that this PE file contains. std::cout << std::format("{}, Imported funcs: {}\r\n", itModule.strModuleName, itModule.vecImportFunc.size()); for (const auto& itFuncs : itModule.vecImportFunc) { //Cycle through all the functions imported from itModule module. itFuncs.strFuncName; //Imported function name. itFuncs.stImpByName; //IMAGE_IMPORT_BY_NAME struct for this function. itFuncs.unThunk.stThunk32; //Union of IMAGE_THUNK_DATA32 or IMAGE_THUNK_DATA64 (depending on the PE type). } }
[[nodiscard]] auto GetResources()const->std::optional<PERESROOT>;
Returns all file's resources.
The next code snippet populates std::wstring
with all resources' types and names that PE binary possesses, and prints it to the standard 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; //This wstring will contain all resources by name. for (const auto& iterRoot : peResRoot->vecResData) { //Main loop to extract Resources. auto ilvlRoot = 0; auto pResDirEntry = &iterRoot.stResDirEntry; //ROOT 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; //Level 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; //Level 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;
[[nodiscard]] auto GetExceptions()const->std::optional<PEEXCEPTION_VEC>;
Returns an array of file's Exception entries.
struct PEEXCEPTION { DWORD dwOffset; //File's raw offset of the exceptions descriptor. _IMAGE_RUNTIME_FUNCTION_ENTRY stRuntimeFuncEntry; //Standard _IMAGE_RUNTIME_FUNCTION_ENTRY header. }; using PEEXCEPTION_VEC = std::vector<PEEXCEPTION>;
[[nodiscard]] auto GetSecurity()const->std::optional<PESECURITY_VEC>;
Returns an array of file's Security entries.
struct PEWIN_CERTIFICATE { //Full replica of the WIN_CERTIFICATE struct from the <WinTrust.h>. DWORD dwLength; WORD wRevision; WORD wCertificateType; BYTE bCertificate[1]; }; struct PESECURITY { DWORD dwOffset; //File's raw offset of this security descriptor. PEWIN_CERTIFICATE stWinSert; //Standard WIN_CERTIFICATE struct. }; using PESECURITY_VEC = std::vector<PESECURITY>;
[[nodiscard]] auto GetRelocations()const->std::optional<PERELOC_VEC>;
Returns an array of file's relocation information.
struct PERELOCDATA { DWORD dwOffset; //File's raw offset of the Relocation data descriptor. WORD wRelocType; //Relocation type. WORD wRelocOffset; //Relocation offset (Offset the relocation must be applied to.) }; struct PERELOC { DWORD dwOffset; //File's raw offset of the Relocation descriptor. IMAGE_BASE_RELOCATION stBaseReloc; //Standard IMAGE_BASE_RELOCATION header. std::vector<PERELOCDATA> vecRelocData; //Array of the Relocation data struct. }; using PERELOC_VEC = std::vector<PERELOC>;
[[nodiscard]] auto GetDebug()const->std::optional<PEDEBUG_VEC>;
Returns an array of file's Debug entries.
struct PEDEBUGDBGHDR { //dwHdr[6] is an array of the first six DWORDs of IMAGE_DEBUG_DIRECTORY::PointerToRawData data (Debug info header). //Their meaning varies depending on dwHdr[0] (Signature) value. //If dwHdr[0] == 0x53445352 (Ascii "RSDS") it's PDB 7.0 file: // Then dwHdr[1]-dwHdr[4] is GUID (*((GUID*)&dwHdr[1])). dwHdr[5] is Counter/Age. //If dwHdr[0] == 0x3031424E (Ascii "NB10") it's PDB 2.0 file: // Then dwHdr[1] is Offset. dwHdr[2] is Time/Signature. dwHdr[3] is Counter/Age. DWORD dwHdr[6]; std::string strPDBName; //PDB file name/path. }; struct PEDEBUG { DWORD dwOffset; //File's raw offset of the Debug descriptor. IMAGE_DEBUG_DIRECTORY stDebugDir; //Standard IMAGE_DEBUG_DIRECTORY header. PEDEBUGDBGHDR stDebugHdrInfo; //Debug info header. }; using PEDEBUG_VEC = std::vector<PEDEBUG>;
[[nodiscard]] auto GetTLS()const->std::optional<PETLS>;
Returns file's Thread Local Storage information.
struct PETLS { DWORD dwOffset; //File's raw offset of the TLS header descriptor. union UNPETLS { IMAGE_TLS_DIRECTORY32 stTLSDir32; //x86 standard TLS header. IMAGE_TLS_DIRECTORY64 stTLSDir64; //x64 TLS header. } unTLS; std::vector<DWORD> vecTLSCallbacks; //Array of the TLS callbacks. };
[[nodiscard]] auto GetLoadConfig()const->std::optional<PELOADCONFIG>;
Returns file's Load Config Directory info.
struct PELOADCONFIG { DWORD dwOffset; //File's raw offset of the LCD descriptor. union UNPELOADCONFIG { IMAGE_LOAD_CONFIG_DIRECTORY32 stLCD32; //x86 LCD descriptor. IMAGE_LOAD_CONFIG_DIRECTORY64 stLCD64; //x64 LCD descriptor. } unLCD; };
[[nodiscard]] auto GetBoundImport()const->std::optional<PEBOUNDIMPORT_VEC>;
Returns an array of file's Bound Import entries.
struct PEBOUNDFORWARDER { DWORD dwOffset; //File's raw offset of the Bound Forwarder descriptor. IMAGE_BOUND_FORWARDER_REF stBoundForwarder; //Standard IMAGE_BOUND_FORWARDER_REF struct. std::string strBoundForwarderName; //Bound forwarder name. }; struct PEBOUNDIMPORT { DWORD dwOffset; //File's raw offset of the Bound Import descriptor. IMAGE_BOUND_IMPORT_DESCRIPTOR stBoundImpDesc; //Standard IMAGE_BOUND_IMPORT_DESCRIPTOR struct. std::string strBoundName; //Bound Import name. std::vector<PEBOUNDFORWARDER> vecBoundForwarder; //Array of the Bound Forwarder structs. }; using PEBOUNDIMPORT_VEC = std::vector<PEBOUNDIMPORT>;
[[nodiscard]] auto GetDelayImport()const->std::optional<PEDELAYIMPORT_VEC>;
Returns an array of file's Delay Import entries.
struct PEDELAYIMPORTFUNC { union UNPEDELAYIMPORTTHUNK { struct x32 { IMAGE_THUNK_DATA32 stImportAddressTable; //x86 Import Address Table struct. IMAGE_THUNK_DATA32 stImportNameTable; //x86 Import Name Table struct. IMAGE_THUNK_DATA32 stBoundImportAddressTable; //x86 Bound Import Address Table struct. IMAGE_THUNK_DATA32 stUnloadInformationTable; //x86 Unload Information Table struct. } st32; struct x64 { IMAGE_THUNK_DATA64 stImportAddressTable; //x64 Import Address Table struct. IMAGE_THUNK_DATA64 stImportNameTable; //x64 Import Name Table struct. IMAGE_THUNK_DATA64 stBoundImportAddressTable; //x64 Bound Import Address Table struct IMAGE_THUNK_DATA64 stUnloadInformationTable; //x64 Unload Information Table struct. } st64; } unThunk; IMAGE_IMPORT_BY_NAME stImpByName; //Standard IMAGE_IMPORT_BY_NAME struct. std::string strFuncName; //Function name. }; struct PEDELAYIMPORT { DWORD dwOffset; //File's raw offset of this Delay Import descriptor. IMAGE_DELAYLOAD_DESCRIPTOR stDelayImpDesc; //Standard IMAGE_DELAYLOAD_DESCRIPTOR struct. std::string strModuleName; //Import module name. std::vector<PEDELAYIMPORTFUNC> vecDelayImpFunc; //Array of the Delay Import module functions. }; using PEDELAYIMPORT_VEC = std::vector<PEDELAYIMPORT>;
[[nodiscard]] auto GetCOMDescriptor()const->std::optional<PECOMDESCRIPTOR>;
Gets file's .NET info.
struct PECOMDESCRIPTOR { DWORD dwOffset; //File's raw offset of the IMAGE_COR20_HEADER descriptor. IMAGE_COR20_HEADER stCorHdr; //Standard IMAGE_COR20_HEADER struct. };
These freestanding methods do not need an active Clibpe
object with an opened file. They instead take references to the previously obtained structures.
[[nodiscard]] inline constexpr auto GetFileType(const PENTHDR& stNTHdr)->EFileType
Returns PE file type in form of the EFileType
enum.
enum class EFileType : std::uint8_t { UNKNOWN = 0, PE32, PE64, PEROM };
[[nodiscard]] inline constexpr auto GetImageBase(const PENTHDR& stNTHdr)->ULONGLONG
Returns file's Image Base.
[[nodiscard]] inline constexpr auto GetOffsetFromRVA(ULONGLONG ullRVA, const PESECHDR_VEC& vecSecHdr)->DWORD
Converts file's RVA to the file's physical raw offset on disk.
[[nodiscard]] inline constexpr auto FlatResources(const PERESROOT& stResRoot)
This function is kind of a light version of the GetResources
method. It takes PERESROOT
struct returned by the GetResources
, and returns std::vector
of PERESFLAT
structures.
PERESFLAT
is a light struct that only possesses pointers to an actual resources data, unlike heavy PERESROOT
. FlatResources
flattens all resources, making accessing them more convenient.
struct PERESFLAT { std::span<const std::byte> spnData { }; //Resource data. std::wstring_view wsvTypeStr { }; //Resource Type name. std::wstring_view wsvNameStr { }; //Resource Name name (resource itself name). std::wstring_view wsvLangStr { }; //Resource Lang name. WORD wTypeID { }; //Resource Type ID (RT_CURSOR, RT_BITMAP, etc...). WORD wNameID { }; //Resource Name ID (resource itself ID). WORD wLangID { }; //Resource Lang ID. }; using PERESFLAT_VEC = std::vector<PERESFLAT>;
A PE file consists of many structures, they in turn possess many fields some of which have predefined values.
These maps are meant to alleviate such fields' conversion to a human-reading format. They are simple std::unordered_map<DWORD, std::wstring_view>
maps.
Note that some fields can only have one value, while the others can combine many values with a bitwise or |
operation.
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_FILE_HEADER::Machine
field.
This map forms one or more values from IMAGE_NT_HEADERS::IMAGE_FILE_HEADER::Characteristics
field.
const auto pNTHdr = m_pLibpe->GetNTHeader(); const auto pDescr = &pNTHdr->unHdr.stNTHdr32.FileHeader; //Same for both x86/x64. std::wstring wstrCharact; for (const auto& flags : MapFileHdrCharact) { if (flags.first & pDescr->Characteristics) { wstrCharact += flags.second; wstrCharact += L"\n"; } }
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Magic
field.
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Subsystem
field.
This map forms one or more values from IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::DllCharacteristics
field.
const auto pNTHdr = m_pLibpe->GetNTHeader(); const auto pOptHdr = &pNTHdr->unHdr.stNTHdr32.OptionalHeader //For x64: pNTHdr->unHdr.stNTHdr64.OptionalHeader std::wstring wstrCharact; for (const auto& flags : MapOptHdrDllCharact) { if (flags.first & pOptHdr->DllCharacteristics) { wstrCharact += flags.second; wstrCharact += L"\n"; } }
This map forms one or more values from IMAGE_SECTION_HEADER::Characteristics
field.
const auto pSecHeaders = m_pLibpe->GetSecHeaders(); std::wstring wstrCharact; auto IdOfSection = 0; //ID of desired section. for (const auto& flags : MapSecHdrCharact) { if (flags.first & pSecHeaders->at(IdOfSection).stSecHdr.Characteristics) { wstrCharact += flags.second; wstrCharact += L"\n"; } }
This map forms one of the values from IMAGE_RESOURCE_DIRECTORY_ENTRY::Id
field.
This map forms one of the values from WIN_CERTIFICATE::wRevision
field.
This map forms one of the values from WIN_CERTIFICATE::wCertificateType
field.
This map forms one of the values from PERELOCDATA::wRelocType
field.
This map forms one of the values from IMAGE_DEBUG_DIRECTORY::Type
field.
This map forms one of the values from IMAGE_TLS_DIRECTORY::Characteristics
field.
This map forms one or more values from IMAGE_LOAD_CONFIG_DIRECTORY::GuardFlags
field.
const auto pLCD = m_pLibpe->GetLoadConfig(); const auto pPELCD = &pLCD->unLCD.stLCD32; //For x64: pLCD->unLCD.stLCD64 std::wstring wstrGFlags; for (const auto& flags : MapLCDGuardFlags) { if (flags.first & pPELCD->GuardFlags) { wstrGFlags += flags.second; wstrGFlags += L"\n"; } }
This map forms one or more values from IMAGE_COR20_HEADER::Flags
field.
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"; } }
This software is available under the MIT License.