This project is about libpe, a Windows library implemented as a pure abstract virtual interface with a decent amount of methods listed here.
PE32/PE32+ binaries viewer library.
Table of Contents
Introduction
libpe is a Windows library for obtaining inner information from the Portable Executable Format binaries. The library is implemented as a pure abstract virtual interface with a decent amount of methods.
- Works with PE32(x86) and PE32+(x64) binaries
- Supports PE32/PE32+ binaries of any size (although PE format is restricted to 4GB)
- All inner PE32/PE32+ data structures, headers and layouts
- 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
- Built with /std:c++20 standard conformance
Pepper is one of the gui apps that is built on top of the libpe, and using it extensively.
Usage
The usage of the library is quite simple:
- Add libpe.h/libpe.cpp into your project
- Declare
IlibpePtr
variable as: IlibpePtr m_pLibpe { Createlibpe() };
Factory function Createlibpe
returns IlibpePtr
- a std::unique_ptr
with custom deleter.
If you, for some reason, need a raw interface pointer, you can directly call CreateRawlibpe
function, which returns Ilibpe*
interface pointer, but in this case you will need to call Destroy
method manually afterwards, to destroy Ilibpe
object.
To use libpe
as a shared .dll
:
- Compile
libpe
as a .dll
from the MSVS solution - Put the
#define LIBPE_SHARED_DLL
macro into your project, before #include "libpe.h"
.
The libpe uses its own libpe
namespace.
Methods
ParsePE
auto ParsePE(LPCWSTR)->int;
This is the first method you call to proceed a PE file.
IlibpePtr pLibpe { Createlibpe() };
if(pLibpe->ParsePE(L"C:\\MyFile.exe") == PEOK)
{
...
}
After this method succeeds you then can call all the other methods to retrieve needed information. The PE file itself doesn't stay in memory any longer, so you don't have to explicitly unload it.
ParsePE
auto ParsePE(std::span<const std::byte> spnFile)->int;
This method overload is used to parse a PE file that is already in memory.
GetFileInfo
auto GetFileInfo()const->PEFILEINFO;
Retrieves PEFILEINFO
structure that contains all service information about the loaded file.
struct PEFILEINFO {
bool fIsx86 : 1 {};
bool fIsx64 : 1 {};
bool fHasDosHdr : 1 {};
bool fHasRichHdr : 1 {};
bool fHasNTHdr : 1 {};
bool fHasDataDirs : 1 {};
bool fHasSections : 1 {};
bool fHasExport : 1 {};
bool fHasImport : 1 {};
bool fHasResource : 1 {};
bool fHasException : 1 {};
bool fHasSecurity : 1 {};
bool fHasReloc : 1 {};
bool fHasDebug : 1 {};
bool fHasArchitect : 1 {};
bool fHasGlobalPtr : 1 {};
bool fHasTLS : 1 {};
bool fHasLoadCFG : 1 {};
bool fHasBoundImp : 1 {};
bool fHasIAT : 1 {};
bool fHasDelayImp : 1 {};
bool fHasCOMDescr : 1 {};
};
GetOffsetFromRVA
auto GetOffsetFromRVA(ULONGLONG ullRVA)const->DWORD;
Converts file's RVA (Relative Virtual Address) to the raw file offset.
GetOffsetFromVA
auto GetOffsetFromVA(ULONGLONG ullVA)const->DWORD;
Converts file's VA (Virtual Address) to the raw file offset.
GetMSDOSHeader
auto GetMSDOSHeader()->IMAGE_DOS_HEADER*;
Gets file's standard MSDOS header.
GetRichHeader
auto GetRichHeader()->PERICHHDR_VEC*;
Gets array of the unofficial and undocumented, so called, «Rich» header structures.
struct PERICHHDR {
DWORD dwOffset; WORD wId; WORD wVersion; DWORD dwCount; };
using PERICHHDR_VEC = std::vector<PERICHHDR>;
GetNTHeader
auto GetNTHeader()->PENTHDR*;
Gets file's NT header.
struct PENTHDR {
DWORD dwOffset; union UNPENTHDR { IMAGE_NT_HEADERS32 stNTHdr32; IMAGE_NT_HEADERS64 stNTHdr64; } unHdr;
};
GetDataDirs
auto GetDataDirs()->PEDATADIR_VEC*;
Gets array of the file's Data directories structs.
struct PEDATADIR {
IMAGE_DATA_DIRECTORY stDataDir; std::string strSection; };
using PEDATADIR_VEC = std::vector<PEDATADIR>;
GetSecHeaders
auto GetSecHeaders()->PESECHDR_VEC*
Gets array of the file's Sections headers structs.
struct PESECHDR {
DWORD dwOffset; IMAGE_SECTION_HEADER stSecHdr; std::string strSecName; };
using PESECHDR_VEC = std::vector<PESECHDR>;
GetExport
auto GetExport()->PEEXPORT*;
Gets 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
Getting Export information is very simple:
IlibpePtr pLibpe { Createlibpe() };
pLibpe->ParsePE(L"PATH_TO_PE_FILE");
const auto pExport = pLibpe->GetExport();
pExport->stExportDesc; pExport->strModuleName; pExport->vecFuncs;
for (const auto& itFuncs : pExport->vecFuncs)
{
itFuncs.strFuncName; itFuncs.dwOrdinal; itFuncs.dwRVA; itFuncs.strForwarderName; }
GetImport
auto GetImport()->PEIMPORT_VEC*;
Gets array of the 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
To obtain Import table information from the file see the following code:
IlibpePtr pLibpe { Createlibpe() };
pLibpe->ParsePE(L"PATH_TO_PE_FILE");
const auto pImport = pLibpe->GetImport();
for (auto& itModule : *pImport) {
auto pImpDesc = &itModule.stImportDesc; auto& str = itModule.strModuleName;
for (auto& itFuncs : itModule.vecImportFunc) {
itFuncs.strFuncName; itFuncs.stImpByName;
itFuncs.varThunk.stThunk32; if(pLibpe->GetFileInfo().fIsx86)
itFuncs.unThunk.stThunk32 else
itFuncs.unThunk.stThunk64 }
}
GetResources
auto GetResources()->PERESROOT*;
Retrieves all the binary's resources.
Example:
The next code excerpt populates std::wstring
with all resources' types and names that PE binary possesses, and prints it to the standard std::wcout
.
#include <iostream>
#include <map>
#include "libpe.h"
int main()
{
using namespace libpe;
IlibpePtr pLibpe { Createlibpe() };
if (pLibpe->ParsePE(PATH_TO_FILE) != PEOK)
return -1;
const auto pResRoot = pLibpe->GetResources();
wchar_t wstr[MAX_PATH];
long ilvlRoot = 0, ilvl2 = 0, ilvl3 = 0;
std::wstring wstring;
for (auto& iterRoot : pResRoot->vecResData)
{
auto pResDirEntry = &iterRoot.stResDirEntry; if (pResDirEntry->NameIsString)
swprintf(wstr, MAX_PATH, L"Entry: %li [Name: %s]", ilvlRoot, iterRoot.wstrResName.data());
else
{
if (const auto iter = MapResID.find(pResDirEntry->Id); iter != MapResID.end())
swprintf(wstr, MAX_PATH, L"Entry: %li [Id: %u, %s]", ilvlRoot, pResDirEntry->Id, iter->second.data());
else
swprintf(wstr, MAX_PATH, L"Entry: %li [Id: %u]", ilvlRoot, pResDirEntry->Id);
}
if (pResDirEntry->DataIsDirectory)
{
wstring += wstr;
wstring += L"\r\n";
ilvl2 = 0;
auto pstResLvL2 = &iterRoot.stResLvL2;
for (auto& iterLvL2 : pstResLvL2->vecResData)
{
pResDirEntry = &iterLvL2.stResDirEntry; if (pResDirEntry->NameIsString)
swprintf(wstr, MAX_PATH, L"Entry: %li, Name: %s", ilvl2, iterLvL2.wstrResName.data());
else
swprintf(wstr, MAX_PATH, L"Entry: %li, Id: %u", ilvl2, pResDirEntry->Id);
if (pResDirEntry->DataIsDirectory)
{
wstring += L" ";
wstring += wstr;
wstring += L"\r\n";
ilvl3 = 0;
auto pstResLvL3 = &iterLvL2.stResLvL3;
for (auto& iterLvL3 : pstResLvL3->vecResData)
{
pResDirEntry = &iterLvL3.stResDirEntry; if (pResDirEntry->NameIsString)
swprintf(wstr, MAX_PATH, L"Entry: %li, Name: %s", ilvl3, iterLvL3.wstrResName.data());
else
swprintf(wstr, MAX_PATH, L"Entry: %li, lang: %u", ilvl3, pResDirEntry->Id);
wstring += L" ";
wstring += wstr;
wstring += L"\r\n";
++ilvl3;
}
}
++ilvl2;
}
}
++ilvlRoot;
}
std::wcout << wstring;
}
FlatResources
static auto FlatResources(PERESROOT& stResRoot)->PERESFLAT_VEC;
This static
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 a pointers to the actual resources data, unlike heavy PERESROOT
. FlatResources
flattens all the 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>;
GetExceptions
auto GetExceptions()->PEEXCEPTION_VEC*;
Gets array of the file's Exception entries.
struct PEEXCEPTION {
DWORD dwOffset; _IMAGE_RUNTIME_FUNCTION_ENTRY stRuntimeFuncEntry; };
using PEEXCEPTION_VEC = std::vector<PEEXCEPTION>;
GetSecurity
auto GetSecurity()->PESECURITY_VEC*;
Gets array of the file's Security entries.
struct PESECURITY {
DWORD dwOffset; WIN_CERTIFICATE stWinSert; };
using PESECURITY_VEC = std::vector<PESECURITY>;
GetRelocations
auto GetRelocations()->PERELOC_VEC*;
Gets array of the 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>;
GetDebug
auto GetDebug()->PEDEBUG_VEC*;
Gets array of the 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>;
GetTLS
auto GetTLS()->PETLS*;
Gets 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; };
GetLoadConfig
auto GetLoadConfig()->PELOADCONFIG*;
Gets files's LCD info.
struct PELOADCONFIG {
DWORD dwOffset; union UNPELOADCONFIG {
IMAGE_LOAD_CONFIG_DIRECTORY32 stLCD32; IMAGE_LOAD_CONFIG_DIRECTORY64 stLCD64; } unLCD;
};
GetBoundImport
auto GetBoundImport()->PEBOUNDIMPORT_VEC*;
Gets array of the 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>;
GetDelayImport
auto GetDelayImport()->PEDELAYIMPORT_VEC*;
Gets array of the 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>;
GetCOMDescriptor
auto GetCOMDescriptor()->PECOMDESCRIPTOR*;
Gets file's .NET info.
struct PECOMDESCRIPTOR {
DWORD dwOffset; IMAGE_COR20_HEADER stCorHdr; };
Clear
void Clear();
Clears all internal structs to free the memory. Call this method if you don't need loaded PE information anymore. When calling ParsePE
method the Clear
is invoked automatically.
Destroy
void Destroy();
Destroys the libpe object.
You don't usally call this method, it will be called automatically during object destruction.
Maps
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 bitwise or |
operation.
MapFileHdrMachine
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_FILE_HEADER::Machine
field.
MapFileHdrCharact
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";
}
}
MapOptHdrMagic
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Magic
field.
MapOptHdrSubsystem
This map forms one of the values from IMAGE_NT_HEADERS::IMAGE_OPTIONAL_HEADER::Subsystem
field.
MapOptHdrDllCharact
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";
}
}
MapSecHdrCharact
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";
}
}
MapResID
This map forms one of the values from IMAGE_RESOURCE_DIRECTORY_ENTRY::Id
field.
MapWinCertRevision
This map forms one of the values from WIN_CERTIFICATE::wRevision
field.
MapWinCertType
This map forms one of the values from WIN_CERTIFICATE::wCertificateType
field.
MapRelocType
This map forms one of the values from PERELOCDATA::wRelocType
field.
MapDbgType
This map forms one of the values from IMAGE_DEBUG_DIRECTORY::Type
field.
MapTLSCharact
This map forms one of the values from IMAGE_TLS_DIRECTORY::Characteristics
field.
MapLCDGuardFlags
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";
}
}
MapCOR20Flags
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";
}
}
Global Functions
CreateRawlibpe
extern "C" ILIBPEAPI Ilibpe * __cdecl CreateRawlibpe();
It's the main function that creates raw Ilibpe
interface pointer, but you barely need to use it in your code.
See the Usage section for more info.
GetLibInfo
extern "C" ILIBPEAPI PLIBPE_INFO __cdecl GetLibInfo();
Returns pointer to LIBPE_INFO
, which is libpe service information structure.
struct LIBPEINFO
{
const wchar_t* pwszVersion { }; union {
unsigned long long ullVersion { }; struct {
short wMajor;
short wMinor;
short wMaintenance;
short wRevision;
} stVersion;
};
};
License
This software is available under the MIT License.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.