evolvencemsm / libpe

Library for working with PE/PE+ binaries' inner information.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

libpe

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 PE/PE+ binaries of any size (although PE format is restricted to 4GB)
  • All inner PE/PE+ 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:

  1. Add libpe.h/libpe.cpp into your project
  2. 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:

  1. Compile libpe as a .dll from the MSVS solution
  2. Put the #define LIBPE_SHARED_DLL macro into your project, before #include "libpe.h".

The libpe uses its own libpe namespace.

Methods

LoadPe

auto LoadPe(LPCWSTR)->int;

This is the first method you call to proceed a PE file.

IlibpePtr pLibpe { Createlibpe() };
if(pLibpe->LoadPe(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.

LoadPe

auto LoadPe(std::span<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; //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>;

GetNTHeader

auto GetNTHeader()->PENTHDR*;

Gets 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;
};

GetDataDirs

auto GetDataDirs()->PEDATADIR_VEC*;

Gets array of the 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>;

GetSecHeaders

auto GetSecHeaders()->PESECHDR_VEC*

Gets array of the file's Sections headers structs.

struct PESECHDR {
	DWORD                dwOffset;   //File's raw offset of the section header descriptor.
	IMAGE_SECTION_HEADER stSecHdr;   //Standard section header.
	std::string          strSecName; //Section full name.
};
using PESECHDR_VEC = std::vector<PESECHDR>;

GetExport

auto GetExport()->PEEXPORT*;

Gets file's Export information.

struct PEEXPORTFUNC {
    DWORD       dwRVA;            //Function RVA.
    DWORD       dwOrdinal;        //Function ordinal.
    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
Getting Export information is very simple:

IlibpePtr pLibpe { Createlibpe() };
pLibpe->LoadPe(L"PATH_TO_PE_FILE");
const auto pExport = pLibpe->GetExport();

pExport->stExportDesc;  //IMAGE_EXPORT_DIRECTORY struct.
pExport->strModuleName; //Export module name.
pExport->vecFuncs;      //Vector of exported functions.

for (const auto& itFuncs : pExport->vecFuncs)
{
    itFuncs.strFuncName;      //Function name.
    itFuncs.dwOrdinal;        //Ordinal.
    itFuncs.dwRVA;            //Function RVA.
    itFuncs.strForwarderName; //Forwarder name.
}

GetImport

auto GetImport()->PEIMPORT_VEC*;

Gets array of the 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
To obtain Import table information from the file see the following code:

IlibpePtr pLibpe { Createlibpe() };
pLibpe->LoadPe(L"PATH_TO_PE_FILE");
const auto pImport = pLibpe->GetImport();

for (auto& itModule : *pImport) //Cycle through all imports that this PE file contains.
{
    auto pImpDesc = &itModule.stImportDesc; //IMAGE_IMPORT_DESCRIPTOR struct.
    auto& str = itModule.strModuleName;     //Name of the import module.
	
    for (auto& itFuncs : itModule.vecImportFunc) //Cycle through all the functions imported from itModule module.
    {
    	itFuncs.strFuncName;        //Imported function name (std::string).
        itFuncs.stImpByName;        //IMAGE_IMPORT_BY_NAME struct for this function.
       
        itFuncs.varThunk.stThunk32; //Union of IMAGE_THUNK_DATA32 or IMAGE_THUNK_DATA64 (depending on the binary type).
        if(pLibpe->GetFileInfo().fIsx86)
            itFuncs.unThunk.stThunk32 //Process stThunk32 data
        else
            itFuncs.unThunk.stThunk64 //Process stThunk64 data
    }
}

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()
{
	const std::map<WORD, std::wstring> g_mapResType {
	{ 1, L"RT_CURSOR" },
	{ 2, L"RT_BITMAP" },
	{ 3, L"RT_ICON" },
	{ 4, L"RT_MENU" },
	{ 5, L"RT_DIALOG" },
	{ 6, L"RT_STRING" },
	{ 7, L"RT_FONTDIR" },
	{ 8, L"RT_FONT" },
	{ 9, L"RT_ACCELERATOR" },
	{ 10, L"RT_RCDATA" },
	{ 11, L"RT_MESSAGETABLE" },
	{ 12, L"RT_GROUP_CURSOR" },
	{ 14, L"RT_GROUP_ICON" },
	{ 16, L"RT_VERSION" },
	{ 17, L"RT_DLGINCLUDE" },
	{ 19, L"RT_PLUGPLAY" },
	{ 20, L"RT_VXD" },
	{ 21, L"RT_ANICURSOR" },
	{ 22, L"RT_ANIICON" },
	{ 23, L"RT_HTML" },
	{ 24, L"RT_MANIFEST" },
	{ 28, L"RT_RIBBON_XML" },
	{ 240, L"RT_DLGINIT" },
	{ 241, L"RT_TOOLBAR" }
	};

	using namespace libpe;
	IlibpePtr pLibpe { Createlibpe() };
	if (pLibpe->LoadPe(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; // This wstring will contain all resources by name.

	//Main loop to extract Resources.
	for (auto& iterRoot : pResRoot->vecResData)
	{
		auto pResDirEntry = &iterRoot.stResDirEntry; //ROOT IMAGE_RESOURCE_DIRECTORY_ENTRY
		if (pResDirEntry->NameIsString)
			swprintf(wstr, MAX_PATH, L"Entry: %li [Name: %s]", ilvlRoot, iterRoot.wstrResName.data());
		else
		{
			if (const auto iter = g_mapResType.find(pResDirEntry->Id); iter != g_mapResType.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; //Level 2 IMAGE_RESOURCE_DIRECTORY_ENTRY
				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; //Level 3 IMAGE_RESOURCE_DIRECTORY_ENTRY
						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

auto FlatResources(PERESROOT& stResRoot)const->PERESFLAT_VEC;

This function is kind of light version of the GetResources method. It takes PERESROOT struct, returned by the GetResources, as an argument, and returns 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::wstring_view    wstrTypeName { }; //Type name.
    std::wstring_view    wstrResName { };  //Resource name.
    std::wstring_view    wstrLangName { }; //Lang name.
    std::span<std::byte> spnData { };      //Resource data.
    WORD                 wTypeID { };      //Type ID, e.g. RT_CURSOR, RT_BITMAP, etc...
    WORD                 wResID { };       //Resource ID.
    WORD                 wLangID { };      //Lang ID.
};
using PERESFLAT_VEC = std::vector< PERESFLAT>;

GetExceptions

auto GetExceptions()->PEEXCEPTION_VEC*;

Gets array of the 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>;

GetSecurity

auto GetSecurity()->PESECURITY_VEC*;

Gets array of the file's Security entries.

struct PESECURITY {
    DWORD           dwOffset;  //File's raw offset of the security descriptor.
    WIN_CERTIFICATE stWinSert; //Standard WIN_CERTIFICATE header.
};
using PESECURITY_VEC = std::vector<PESECURITY>;

GetRelocations

auto GetRelocations()->PERELOC_VEC*;

Gets array of the 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>;

GetDebug

auto GetDebug()->PEDEBUG_VEC*;

Gets array of the 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>;

GetTLS

auto GetTLS()->PETLS*;

Gets 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.
};

GetLoadConfig

auto GetLoadConfig()->PELOADCONFIG*;

Gets files's LCD 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;
};

GetBoundImport

auto GetBoundImport()->PEBOUNDIMPORT_VEC*;

Gets array of the 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>;

GetDelayImport

auto GetDelayImport()->PEDELAYIMPORT_VEC*;

Gets array of the 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 the 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>;

GetCOMDescriptor

auto GetCOMDescriptor()->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.
};

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 LoadPe 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.

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 { };        //wchar_t string Version.
    union {
    	unsigned long long ullVersion { }; //long long number Version.
    	struct {
    		short wMajor;
    		short wMinor;
    		short wMaintenance;
    		short wRevision;
    	} stVersion;
    };
};

License

This software is available under the MIT License.

About

Library for working with PE/PE+ binaries' inner information.

License:MIT License


Languages

Language:C++ 100.0%