This project details the functionality of libpe, a C++ library enabling the manipulation and extraction of various data structures from PE32(x86) and PE32+(x64) binaries. It provides methods to access and retrieve different headers, tables, sections, and resource information.
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"); 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; WORD wId; WORD wVersion; DWORD dwCount; };
using PERICHHDR_VEC = std::vector<PERICHHDR>;
[[nodiscard]] auto GetNTHeader()const->std::optional<PENTHDR>;
Returns a file's NT header.
struct PENTHDR {
DWORD dwOffset; union UNPENTHDR { IMAGE_NT_HEADERS32 stNTHdr32; IMAGE_NT_HEADERS64 stNTHdr64; } unHdr;
};
[[nodiscard]] auto GetDataDirs()const->std::optional<PEDATADIR_VEC>;
Returns an array of file's Data directories structs.
struct PEDATADIR {
IMAGE_DATA_DIRECTORY stDataDir; std::string strSection; };
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; IMAGE_SECTION_HEADER stSecHdr; std::string strSecName; };
using PESECHDR_VEC = std::vector<PESECHDR>;
[[nodiscard]] auto GetExport()const->std::optional<PEEXPORT>;
Returns a file's Export information.
struct PEEXPORTFUNC {
DWORD dwFuncRVA; DWORD dwOrdinal; DWORD dwNameRVA; std::string strFuncName; std::string strForwarderName; };
struct PEEXPORT {
DWORD dwOffset; IMAGE_EXPORT_DIRECTORY stExportDesc; std::string strModuleName; std::vector<PEEXPORTFUNC> vecFuncs; };
Example:
libpe::Clibpe pe(L"PATH_TO_PE_FILE");
const auto peExport = pe.GetExport();
if (!peExport) {
return;
}
peExport->stExportDesc; peExport->strModuleName; peExport->vecFuncs;
for (const auto& itFuncs : peExport->vecFuncs) {
itFuncs.strFuncName; itFuncs.dwOrdinal; itFuncs.dwFuncRVA; itFuncs.strForwarderName; }
[[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; IMAGE_THUNK_DATA64 stThunk64; } unThunk;
IMAGE_IMPORT_BY_NAME stImpByName; std::string strFuncName; };
struct PEIMPORT {
DWORD dwOffset; IMAGE_IMPORT_DESCRIPTOR stImportDesc; std::string strModuleName; std::vector<PEIMPORTFUNC> vecImportFunc; };
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) { std::cout << std::format("{}, Imported funcs: {}\r\n", itModule.strModuleName, itModule.vecImportFunc.size());
for (const auto& itFuncs : itModule.vecImportFunc) { itFuncs.strFuncName; itFuncs.stImpByName; itFuncs.unThunk.stThunk32; }
}
[[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; for (const auto& iterRoot : peResRoot->vecResData) { auto ilvlRoot = 0;
auto pResDirEntry = &iterRoot.stResDirEntry; 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; 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; 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; _IMAGE_RUNTIME_FUNCTION_ENTRY stRuntimeFuncEntry; };
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 { DWORD dwLength;
WORD wRevision;
WORD wCertificateType;
BYTE bCertificate[1];
};
struct PESECURITY {
DWORD dwOffset; PEWIN_CERTIFICATE stWinSert; };
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; WORD wRelocType; WORD wRelocOffset; };
struct PERELOC {
DWORD dwOffset; IMAGE_BASE_RELOCATION stBaseReloc; std::vector<PERELOCDATA> vecRelocData; };
using PERELOC_VEC = std::vector<PERELOC>;
[[nodiscard]] auto GetDebug()const->std::optional<PEDEBUG_VEC>;
Returns an array of file's Debug entries.
struct PEDEBUGDBGHDR {
DWORD dwHdr[6];
std::string strPDBName; };
struct PEDEBUG {
DWORD dwOffset; IMAGE_DEBUG_DIRECTORY stDebugDir; PEDEBUGDBGHDR stDebugHdrInfo; };
using PEDEBUG_VEC = std::vector<PEDEBUG>;
[[nodiscard]] auto GetTLS()const->std::optional<PETLS>;
Returns file's Thread Local Storage information.
struct PETLS {
DWORD dwOffset; union UNPETLS {
IMAGE_TLS_DIRECTORY32 stTLSDir32; IMAGE_TLS_DIRECTORY64 stTLSDir64; } unTLS;
std::vector<DWORD> vecTLSCallbacks; };
[[nodiscard]] auto GetLoadConfig()const->std::optional<PELOADCONFIG>;
Returns file's Load Config Directory info.
struct PELOADCONFIG {
DWORD dwOffset; union UNPELOADCONFIG {
IMAGE_LOAD_CONFIG_DIRECTORY32 stLCD32; IMAGE_LOAD_CONFIG_DIRECTORY64 stLCD64; } unLCD;
};
[[nodiscard]] auto GetBoundImport()const->std::optional<PEBOUNDIMPORT_VEC>;
Returns an array of file's Bound Import entries.
struct PEBOUNDFORWARDER {
DWORD dwOffset; IMAGE_BOUND_FORWARDER_REF stBoundForwarder; std::string strBoundForwarderName; };
struct PEBOUNDIMPORT {
DWORD dwOffset; IMAGE_BOUND_IMPORT_DESCRIPTOR stBoundImpDesc; std::string strBoundName; std::vector<PEBOUNDFORWARDER> vecBoundForwarder; };
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; IMAGE_THUNK_DATA32 stImportNameTable; IMAGE_THUNK_DATA32 stBoundImportAddressTable; IMAGE_THUNK_DATA32 stUnloadInformationTable; } st32;
struct x64 {
IMAGE_THUNK_DATA64 stImportAddressTable; IMAGE_THUNK_DATA64 stImportNameTable; IMAGE_THUNK_DATA64 stBoundImportAddressTable; IMAGE_THUNK_DATA64 stUnloadInformationTable; } st64;
} unThunk;
IMAGE_IMPORT_BY_NAME stImpByName; std::string strFuncName; };
struct PEDELAYIMPORT {
DWORD dwOffset; IMAGE_DELAYLOAD_DESCRIPTOR stDelayImpDesc; std::string strModuleName; std::vector<PEDELAYIMPORTFUNC> vecDelayImpFunc; };
using PEDELAYIMPORT_VEC = std::vector<PEDELAYIMPORT>;
[[nodiscard]] auto GetCOMDescriptor()const->std::optional<PECOMDESCRIPTOR>;
Gets file's .NET info.
struct PECOMDESCRIPTOR {
DWORD dwOffset; IMAGE_COR20_HEADER stCorHdr; };
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 { }; std::wstring_view wsvTypeStr { }; std::wstring_view wsvNameStr { }; std::wstring_view wsvLangStr { }; WORD wTypeID { }; WORD wNameID { }; WORD wLangID { }; };
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; 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 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; 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; 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.