Repository: jovibor/libpe Branch: master Commit: 527db4a263c5 Files: 9 Total size: 136.1 KB Directory structure: gitextract__iegzh_x/ ├── .clang-tidy ├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── libpe/ │ └── libpe.ixx └── test/ ├── libpe.sln ├── libpe.vcxproj └── test.cpp ================================================ FILE CONTENTS ================================================ ================================================ FILE: .clang-tidy ================================================ Checks: '*, -cppcoreguidelines-*,-google-*,-fuchsia-*,-hicpp-*,-cert-*,-clang-*,-llvmlibc-*,-altera-*,-boost-*,-abseil-*, -bugprone-easily-swappable-parameters, -bugprone-implicit-widening-of-multiplication-result, -bugprone-narrowing-conversions, -bugprone-use-after-move, -llvm-header-guard, -llvm-include-order, -llvm-namespace-comment, -llvm-qualified-auto, -misc-include-cleaner, -misc-non-private-member-variables-in-classes, -misc-no-recursion, -misc-use-after-move, -misc-use-internal-linkage, -modernize-use-trailing-return-type, -modernize-avoid-c-arrays, -modernize-use-nodiscard, -modernize-use-ranges, -performance-no-int-to-ptr, -portability-simd-intrinsics, -readability-avoid-nested-conditional-operator, -readability-braces-around-statements, -readability-implicit-bool-conversion, -readability-magic-numbers, -readability-redundant-access-specifiers, -readability-isolate-declaration, -readability-qualified-auto, -readability-convert-member-functions-to-static, -readability-misleading-indentation, -readability-function-cognitive-complexity, -readability-identifier-length, -readability-named-parameter, google-readability-casting' HeaderFilterRegex: '' ================================================ FILE: .editorconfig ================================================ root = true [*.{c++,cc,cpp,cppm,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}] cpp_generate_documentation_comments = xml cpp_indent_braces = false cpp_indent_multi_line_relative_to = innermost_parenthesis cpp_indent_within_parentheses = indent cpp_indent_preserve_within_parentheses = true cpp_indent_case_contents = true cpp_indent_case_labels = false cpp_indent_case_contents_when_block = false cpp_indent_lambda_braces_when_parameter = true cpp_indent_goto_labels = one_left cpp_indent_preprocessor = one_left cpp_indent_access_specifiers = false cpp_indent_namespace_contents = true cpp_indent_preserve_comments = true cpp_new_line_before_open_brace_namespace = same_line cpp_new_line_before_open_brace_type = same_line cpp_new_line_before_open_brace_function = ignore cpp_new_line_before_open_brace_block = same_line cpp_new_line_before_open_brace_lambda = same_line cpp_new_line_scope_braces_on_separate_lines = false cpp_new_line_close_brace_same_line_empty_type = false cpp_new_line_close_brace_same_line_empty_function = false cpp_new_line_before_catch = true cpp_new_line_before_else = true cpp_new_line_before_while_in_do_while = false cpp_space_before_function_open_parenthesis = remove cpp_space_within_parameter_list_parentheses = false cpp_space_between_empty_parameter_list_parentheses = false cpp_space_after_keywords_in_control_flow_statements = true cpp_space_within_control_flow_statement_parentheses = false cpp_space_before_lambda_open_parenthesis = false cpp_space_within_cast_parentheses = false cpp_space_after_cast_close_parenthesis = false cpp_space_within_expression_parentheses = false cpp_space_before_block_open_brace = true cpp_space_between_empty_braces = true cpp_space_before_initializer_list_open_brace = true cpp_space_within_initializer_list_braces = true cpp_space_preserve_in_initializer_list = false cpp_space_before_open_square_bracket = false cpp_space_within_square_brackets = false cpp_space_before_empty_square_brackets = false cpp_space_between_empty_square_brackets = false cpp_space_group_square_brackets = true cpp_space_within_lambda_brackets = false cpp_space_between_empty_lambda_brackets = false cpp_space_before_comma = false cpp_space_after_comma = true cpp_space_remove_around_member_operators = true cpp_space_before_inheritance_colon = true cpp_space_before_constructor_colon = true cpp_space_remove_before_semicolon = true cpp_space_after_semicolon = true cpp_space_remove_around_unary_operator = true cpp_space_around_binary_operator = insert cpp_space_around_assignment_operator = insert cpp_space_pointer_reference_alignment = ignore cpp_space_around_ternary_operator = insert cpp_wrap_preserve_blocks = one_liners ================================================ FILE: .gitignore ================================================ ################# ## Eclipse ################# *stacktrace.* *.zip *.rar *.exe *.pydevproject .project .metadata bin/ tmp/ *.tmp *.bak *.swp *~.nib local.properties .classpath .settings/ .loadpath # External tool builders .externalToolBuilders/ # Locally stored "Eclipse launch configurations" *.launch # CDT-specific .cproject # PDT-specific .buildpath ################# ## Visual Studio ################# ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. .vs/ # User-specific files *.suppress *.suo *.user *.sln.docstates *.opendb *solution_suppressions.cfg # Build results [Dd]ebug*/ [Rr]elease*/ x64/ build/ [Bb]in/ [Oo]bj/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile *.cd *.i-* # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml *.pubxml *.publishproj # NuGet Packages Directory ## TODO: If you have NuGet Package Restore enabled, uncomment the next line #packages/ # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf ############# ## Windows detritus ############# # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac crap .DS_Store ############# ## Python ############# *.py[cod] # Packages *.egg *.egg-info dist/ build/ eggs/ parts/ var/ sdist/ develop-eggs/ .installed.cfg # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox #Translations *.mo #Mr Developer .mr.developer.cfg *.db *.db ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018-2022 Jovibor, https://github.com/jovibor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ## Introduction **libpe** is a lightweight and very fast library for parsing **PE32(x86)** and **PE32+(x64)** binaries, implemented as a C++20 module. ## Table of Contents * [Features](#features) * [Usage](#usage) * [Class Methods](#class-methods)
_Expand_ * [OpenFile](#openfile) * [CloseFile](#closefile) * [GetDOSHeader](#getdosheader) * [GetRichHeader](#getrichheader) * [GetNTHeader](#getntheader) * [GetDataDirs](#getdatadirs) * [GetSecHeaders](#getsecheaders) * [GetExport](#getexport) * [GetImport](#getimport) * [GetResources](#getresources) * [GetExceptions](#getexceptions) * [GetSecurity](#getsecurity) * [GetRelocations](#getrelocations) * [GetDebug](#getdebug) * [GetTLS](#gettls) * [GetLoadConfig](#getloadconfig) * [GetBoundImport](#getboundimport) * [GetDelayImport](#getdelayimport) * [GetCOMDescriptor](#getcomdescriptor)
* [Helper Methods](#helper-methods)
_Expand_ * [GetFileType](#getfiletype) * [GetImageBase](#getimagebase) * [GetOffsetFromRVA](#getoffsetfromrva) * [FlatResources](#flatresources)
* [Maps](#maps)
_Expand_ * [MapFileHdrMachine](#mapfilehdrmachine) * [MapFileHdrCharact](#mapfilehdrcharact) * [MapOptHdrMagic](#mapopthdrmagic) * [MapOptHdrSubsystem](#mapopthdrsubsystem) * [MapOptHdrDllCharact](#mapophdrdllcharact) * [MapSecHdrCharact](#mapsechdrcharact) * [MapResID](#mapresid) * [MapWinCertRevision](#mapwincertrevision) * [MapWinCertType](#mapwincerttype) * [MapRelocType](#mapreloctype) * [MapDbgType](#mapdbgtype) * [MapTLSCharact](#maptlscharact) * [MapLCDGuardFlags](#maplcdguardflags) * [MapCOR20Flags](#mapcor20flags)
* [License](#license) ## [](#)Features * 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](https://github.com/jovibor/Pepper) is one of the apps that is built on top of the **libpe**. ## [](#)Usage ```cpp 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) { ... } ... } ``` ## [](#)Methods ### OpenFile ```cpp auto OpenFile(const wchar_t* pwszFile)->int; ``` Opens a file for further processing, until [`CloseFile`](#closefile) is called or `Clibpe` object goes out of scope and file closes automatically in destructor. ```cpp libpe::Clibpe pe; if(pe.OpenFile(L"C:\\MyFile.exe") == PEOK) { ... } ``` ### [](#)CloseFile(); ```cpp void CloseFile(); ``` Explicitly closes file that was previously opened with the [`OpenFile(const wchar_t*)`](#openfile). This method is invoked automatically in `Clibpe` destructor. ### [](#)GetDOSHeader ```cpp [[nodiscard]] auto GetDOSHeader()const->std::optional; ``` Returns a file's standard **MSDOS** header. ### [](#)GetRichHeader ```cpp [[nodiscard]] auto GetRichHeader()const->std::optional; ``` Returns an array of the unofficial and undocumented so called **«Rich»** structures. ```cpp 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; ``` ### [](#)GetNTHeader ```cpp [[nodiscard]] auto GetNTHeader()const->std::optional; ``` Returns a file's **NT** header. ```cpp 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 ```cpp [[nodiscard]] auto GetDataDirs()const->std::optional; ``` Returns an array of file's **Data directories** structs. ```cpp 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; ``` ### [](#)GetSecHeaders ```cpp [[nodiscard]] auto GetSecHeaders()const->std::optional; ``` Returns an array of file's **Sections headers** structs. ```cpp 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; ``` ### [](#)GetExport ```cpp [[nodiscard]] auto GetExport()const->std::optional; ``` Returns a file's **Export** information. ```cpp 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 vecFuncs; //Array of the exported functions struct. }; ``` **Example:** ```cpp 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. } ``` ### [](#)GetImport ```cpp [[nodiscard]] auto GetImport()const->std::optional; ``` Returns an array of file's **Import table** entries. ```cpp 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 vecImportFunc; //Array of imported functions. }; using PEIMPORT_VEC = std::vector; ``` **Example** ```cpp 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). } } ``` ### [](#)GetResources ```cpp [[nodiscard]] auto GetResources()const->std::optional; ``` Returns all file's resources. ##### Example: 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`. ```cpp #include #include #include 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; ``` ### [](#)GetExceptions ```cpp [[nodiscard]] auto GetExceptions()const->std::optional; ``` Returns an array of file's **Exception** entries. ```cpp 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; ``` ### [](#)GetSecurity ```cpp [[nodiscard]] auto GetSecurity()const->std::optional; ``` Returns an array of file's **Security** entries. ```cpp struct PEWIN_CERTIFICATE { //Full replica of the WIN_CERTIFICATE struct from the . 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; ``` ### [](#)GetRelocations ```cpp [[nodiscard]] auto GetRelocations()const->std::optional; ``` Returns an array of file's relocation information. ```cpp 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 vecRelocData; //Array of the Relocation data struct. }; using PERELOC_VEC = std::vector; ``` ### [](#)GetDebug ```cpp [[nodiscard]] auto GetDebug()const->std::optional; ``` Returns an array of file's **Debug** entries. ```cpp 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; ``` ### [](#)GetTLS ```cpp [[nodiscard]] auto GetTLS()const->std::optional; ``` Returns file's **Thread Local Storage** information. ```cpp 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 vecTLSCallbacks; //Array of the TLS callbacks. }; ``` ### [](#)GetLoadConfig ```cpp [[nodiscard]] auto GetLoadConfig()const->std::optional; ``` Returns file's **Load Config Directory** info. ```cpp 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 ```cpp [[nodiscard]] auto GetBoundImport()const->std::optional; ``` Returns an array of file's **Bound Import** entries. ```cpp 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 vecBoundForwarder; //Array of the Bound Forwarder structs. }; using PEBOUNDIMPORT_VEC = std::vector; ``` ### [](#)GetDelayImport ```cpp [[nodiscard]] auto GetDelayImport()const->std::optional; ``` Returns an array of file's **Delay Import** entries. ```cpp 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 vecDelayImpFunc; //Array of the Delay Import module functions. }; using PEDELAYIMPORT_VEC = std::vector; ``` ### [](#)GetCOMDescriptor ```cpp [[nodiscard]] auto GetCOMDescriptor()const->std::optional; ``` Gets file's **.NET** info. ```cpp struct PECOMDESCRIPTOR { DWORD dwOffset; //File's raw offset of the IMAGE_COR20_HEADER descriptor. IMAGE_COR20_HEADER stCorHdr; //Standard IMAGE_COR20_HEADER struct. }; ``` ## [](#)Helper Methods These freestanding methods do not need an active `Clibpe` object with an opened file. They instead take references to the previously obtained structures. ### [](#)GetFileType ```cpp [[nodiscard]] inline constexpr auto GetFileType(const PENTHDR& stNTHdr)->EFileType ``` Returns **PE** file type in form of the `EFileType` enum. ```cpp enum class EFileType : std::uint8_t { UNKNOWN = 0, PE32, PE64, PEROM }; ``` ### [](#)GetImageBase ```cpp [[nodiscard]] inline constexpr auto GetImageBase(const PENTHDR& stNTHdr)->ULONGLONG ``` Returns file's **Image Base**. ### [](#)GetOffsetFromRVA ```cpp [[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. ### [](#)FlatResources ```cpp [[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. ```cpp struct PERESFLAT { std::span 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; ``` ## [](#)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` maps. Note that some fields can only have one value, while the others can combine many values with a 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. ```cpp 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"; } } ``` ### [](#)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. ```cpp 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"; } } ``` ### [](#)MapSecHdrCharact This map forms one or more values from `IMAGE_SECTION_HEADER::Characteristics` field. ```cpp 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"; } } ``` ### [](#)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. ```cpp 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"; } } ``` ### [](#)MapCOR20Flags This map forms one or more values from `IMAGE_COR20_HEADER::Flags` field. ```cpp 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"; } } ``` ## [](#)**License** This software is available under the **MIT License**. ================================================ FILE: libpe/libpe.ixx ================================================ /***************************************************************** * Copyright © 2018-present Jovibor https://github.com/jovibor/ * * Library for parsing PE32 (x86) and PE32+ (x64) binaries. * * Official git repository: https://github.com/jovibor/libpe * * This software is available under the "MIT License". * *****************************************************************/ module; #include #include #include #include #include #include #include #include #include export module libpe; namespace libpe::ut { //Utility. //Check overflow of addition. [[nodiscard]] constexpr bool IsSumOverflow(DWORD_PTR dwFirst, DWORD_PTR dwSecond) { return (dwFirst + dwSecond) < dwFirst; } } export namespace libpe { constexpr auto LIBPE_VERSION_MAJOR = 2; constexpr auto LIBPE_VERSION_MINOR = 0; constexpr auto LIBPE_VERSION_PATCH = 0; //Rich. struct PERICHHDR { DWORD dwOffset { }; //File's raw offset of this entry. WORD wId { }; //Entry Id. WORD wVersion { }; //Entry version. DWORD dwCount { }; //Amount of occurrences. }; using PERICHHDR_VEC = std::vector; //NT header. struct PENTHDR { DWORD dwOffset { }; //File's raw offset of this header. union UNPENTHDR { //Union of either x86 or x64 NT header. IMAGE_NT_HEADERS32 stNTHdr32; //x86 Header. IMAGE_NT_HEADERS64 stNTHdr64; //x64 Header. } unHdr { }; }; const std::unordered_map MapFileHdrMachine { //IMAGE_FILE_HEADER::Machine. { static_cast(0), L"IMAGE_FILE_MACHINE_UNKNOWN" }, { static_cast(0x0001), L"IMAGE_FILE_MACHINE_TARGET_HOST" }, { static_cast(0x014c), L"IMAGE_FILE_MACHINE_I386" }, { static_cast(0x0162), L"IMAGE_FILE_MACHINE_R3000" }, { static_cast(0x0166), L"IMAGE_FILE_MACHINE_R4000" }, { static_cast(0x0168), L"IMAGE_FILE_MACHINE_R10000" }, { static_cast(0x0169), L"IMAGE_FILE_MACHINE_WCEMIPSV2" }, { static_cast(0x0184), L"IMAGE_FILE_MACHINE_ALPHA" }, { static_cast(0x01a2), L"IMAGE_FILE_MACHINE_SH3" }, { static_cast(0x01a3), L"IMAGE_FILE_MACHINE_SH3DSP" }, { static_cast(0x01a4), L"IMAGE_FILE_MACHINE_SH3E" }, { static_cast(0x01a6), L"IMAGE_FILE_MACHINE_SH4" }, { static_cast(0x01a8), L"IMAGE_FILE_MACHINE_SH5" }, { static_cast(0x01c0), L"IMAGE_FILE_MACHINE_ARM" }, { static_cast(0x01c2), L"IMAGE_FILE_MACHINE_THUMB" }, { static_cast(0x01c4), L"IMAGE_FILE_MACHINE_ARMNT" }, { static_cast(0x01d3), L"IMAGE_FILE_MACHINE_AM33" }, { static_cast(0x01F0), L"IMAGE_FILE_MACHINE_POWERPC" }, { static_cast(0x01f1), L"IMAGE_FILE_MACHINE_POWERPCFP" }, { static_cast(0x0200), L"IMAGE_FILE_MACHINE_IA64" }, { static_cast(0x0266), L"IMAGE_FILE_MACHINE_MIPS16" }, { static_cast(0x0284), L"IMAGE_FILE_MACHINE_ALPHA64" }, { static_cast(0x0366), L"IMAGE_FILE_MACHINE_MIPSFPU" }, { static_cast(0x0466), L"IMAGE_FILE_MACHINE_MIPSFPU16" }, { static_cast(0x0520), L"IMAGE_FILE_MACHINE_TRICORE" }, { static_cast(0x0CEF), L"IMAGE_FILE_MACHINE_CEF" }, { static_cast(0x0EBC), L"IMAGE_FILE_MACHINE_EBC" }, { static_cast(0x8664), L"IMAGE_FILE_MACHINE_AMD64" }, { static_cast(0x9041), L"IMAGE_FILE_MACHINE_M32R" }, { static_cast(0xAA64), L"IMAGE_FILE_MACHINE_ARM64" }, { static_cast(0xC0EE), L"IMAGE_FILE_MACHINE_CEE" } }; const std::unordered_map MapFileHdrCharact { //IMAGE_FILE_HEADER::Characteristics. { static_cast(0x0001), L"IMAGE_FILE_RELOCS_STRIPPED" }, { static_cast(0x0002), L"IMAGE_FILE_EXECUTABLE_IMAGE" }, { static_cast(0x0004), L"IMAGE_FILE_LINE_NUMS_STRIPPED" }, { static_cast(0x0008), L"IMAGE_FILE_LOCAL_SYMS_STRIPPED" }, { static_cast(0x0010), L"IMAGE_FILE_AGGRESIVE_WS_TRIM" }, { static_cast(0x0020), L"IMAGE_FILE_LARGE_ADDRESS_AWARE" }, { static_cast(0x0080), L"IMAGE_FILE_BYTES_REVERSED_LO" }, { static_cast(0x0100), L"IMAGE_FILE_32BIT_MACHINE" }, { static_cast(0x0200), L"IMAGE_FILE_DEBUG_STRIPPED" }, { static_cast(0x0400), L"IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP" }, { static_cast(0x0800), L"IMAGE_FILE_NET_RUN_FROM_SWAP" }, { static_cast(0x1000), L"IMAGE_FILE_SYSTEM" }, { static_cast(0x2000), L"IMAGE_FILE_DLL" }, { static_cast(0x4000), L"IMAGE_FILE_UP_SYSTEM_ONLY" }, { static_cast(0x8000), L"IMAGE_FILE_BYTES_REVERSED_HI" } }; const std::unordered_map MapOptHdrMagic { //IMAGE_OPTIONAL_HEADER::Magic. { static_cast(0x10b), L"IMAGE_NT_OPTIONAL_HDR32_MAGIC" }, { static_cast(0x20b), L"IMAGE_NT_OPTIONAL_HDR64_MAGIC" }, { static_cast(0x107), L"IMAGE_ROM_OPTIONAL_HDR_MAGIC" } }; const std::unordered_map MapOptHdrSubsystem { //IMAGE_OPTIONAL_HEADER::Subsystem. { static_cast(0), L"IMAGE_SUBSYSTEM_UNKNOWN" }, { static_cast(1), L"IMAGE_SUBSYSTEM_NATIVE" }, { static_cast(2), L"IMAGE_SUBSYSTEM_WINDOWS_GUI" }, { static_cast(3), L"IMAGE_SUBSYSTEM_WINDOWS_CUI" }, { static_cast(5), L"IMAGE_SUBSYSTEM_OS2_CUI" }, { static_cast(7), L"IMAGE_SUBSYSTEM_POSIX_CUI" }, { static_cast(8), L"IMAGE_SUBSYSTEM_NATIVE_WINDOWS" }, { static_cast(9), L"IMAGE_SUBSYSTEM_WINDOWS_CE_GUI" }, { static_cast(10), L"IMAGE_SUBSYSTEM_EFI_APPLICATION" }, { static_cast(11), L"IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER" }, { static_cast(12), L"IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER" }, { static_cast(13), L"IMAGE_SUBSYSTEM_EFI_ROM" }, { static_cast(14), L"IMAGE_SUBSYSTEM_XBOX" }, { static_cast(16), L"IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION" }, { static_cast(17), L"IMAGE_SUBSYSTEM_XBOX_CODE_CATALOG" } }; const std::unordered_map MapOptHdrDllCharact { //IMAGE_OPTIONAL_HEADER::DllCharacteristics. { static_cast(0x0001), L"IMAGE_LIBRARY_PROCESS_INIT" }, { static_cast(0x0002), L"IMAGE_LIBRARY_PROCESS_TERM" }, { static_cast(0x0004), L"IMAGE_LIBRARY_THREAD_INIT" }, { static_cast(0x0008), L"IMAGE_LIBRARY_THREAD_TERM" }, { static_cast(0x0020), L"IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA" }, { static_cast(0x0040), L"IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE" }, { static_cast(0x0080), L"IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY" }, { static_cast(0x0100), L"IMAGE_DLLCHARACTERISTICS_NX_COMPAT" }, { static_cast(0x0200), L"IMAGE_DLLCHARACTERISTICS_NO_ISOLATION" }, { static_cast(0x0400), L"IMAGE_DLLCHARACTERISTICS_NO_SEH" }, { static_cast(0x0800), L"IMAGE_DLLCHARACTERISTICS_NO_BIND" }, { static_cast(0x1000), L"IMAGE_DLLCHARACTERISTICS_APPCONTAINER" }, { static_cast(0x2000), L"IMAGE_DLLCHARACTERISTICS_WDM_DRIVER" }, { static_cast(0x4000), L"IMAGE_DLLCHARACTERISTICS_GUARD_CF" }, { static_cast(0x8000), L"IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE" } }; //Data directories. struct PEDATADIR { IMAGE_DATA_DIRECTORY stDataDir { }; //Standard header. //Section index number, in which this directory resides. //This index is also the actual index into the PESECHDR_VEC vector (from GetSecHeaders()). //If the IMAGE_DATA_DIRECTORY::VirtualAddress is > 0 and this index equals 0xFFFFFFFF (DWORD max), //it means that directory's VirtualAddress points to an invalid/nonexistent section, //and the PE file is most likely corrupted. DWORD dwSecIdx { }; }; using PEDATADIR_VEC = std::vector; //Sections headers. //For more info check: //docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_section_header#members //«An 8-byte, null-padded UTF-8 string. For longer names, this member contains a forward slash (/) //followed by an ASCII representation of a decimal number that is an offset into the string table.» 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; const std::unordered_map MapSecHdrCharact { //IMAGE_SECTION_HEADER::Characteristics. { 0x00000000, L"IMAGE_SCN_TYPE_REG (Reserved)" }, { 0x00000001, L"IMAGE_SCN_TYPE_DSECT (Reserved)" }, { 0x00000002, L"IMAGE_SCN_TYPE_NOLOAD (Reserved)" }, { 0x00000004, L"IMAGE_SCN_TYPE_GROUP (Reserved)" }, { 0x00000008, L"IMAGE_SCN_TYPE_NO_PAD (Reserved)" }, { 0x00000010, L"IMAGE_SCN_TYPE_COPY (Reserved)" }, { 0x00000020, L"IMAGE_SCN_CNT_CODE (Section contains code)" }, { 0x00000040, L"IMAGE_SCN_CNT_INITIALIZED_DATA (Section contains initialized data)" }, { 0x00000080, L"IMAGE_SCN_CNT_UNINITIALIZED_DATA (Section contains uninitialized data)" }, { 0x00000100, L"IMAGE_SCN_LNK_OTHER (Reserved)" }, { 0x00000200, L"IMAGE_SCN_LNK_INFO (Section contains comments or some other type of information)" }, { 0x00000400, L"IMAGE_SCN_TYPE_OVER (Reserved)" }, { 0x00000800, L"IMAGE_SCN_LNK_REMOVE (Section contents will not become part of image)" }, { 0x00001000, L"IMAGE_SCN_LNK_COMDAT (Section contents comdat)" }, { 0x00004000, L"IMAGE_SCN_NO_DEFER_SPEC_EXC (Reset speculative exceptions handling bits in the TLB entries for this section)" }, { 0x00008000, L"IMAGE_SCN_GPREL (Section content can be accessed relative to GP)" }, { 0x00010000, L"IMAGE_SCN_MEM_SYSHEAP (Obsolete)" }, { 0x00020000, L"IMAGE_SCN_MEM_PURGEABLE" }, { 0x00040000, L"IMAGE_SCN_MEM_LOCKED" }, { 0x00080000, L"IMAGE_SCN_MEM_PRELOAD" }, { 0x00100000, L"IMAGE_SCN_ALIGN_1BYTES" }, { 0x00200000, L"IMAGE_SCN_ALIGN_2BYTES" }, { 0x00300000, L"IMAGE_SCN_ALIGN_4BYTES" }, { 0x00400000, L"IMAGE_SCN_ALIGN_8BYTES" }, { 0x00500000, L"IMAGE_SCN_ALIGN_16BYTES (Default alignment if no others are specified)" }, { 0x00600000, L"IMAGE_SCN_ALIGN_32BYTES" }, { 0x00700000, L"IMAGE_SCN_ALIGN_64BYTES" }, { 0x00800000, L"IMAGE_SCN_ALIGN_128BYTES" }, { 0x00900000, L"IMAGE_SCN_ALIGN_256BYTES" }, { 0x00A00000, L"IMAGE_SCN_ALIGN_512BYTES" }, { 0x00B00000, L"IMAGE_SCN_ALIGN_1024BYTES" }, { 0x00C00000, L"IMAGE_SCN_ALIGN_2048BYTES" }, { 0x00D00000, L"IMAGE_SCN_ALIGN_4096BYTES" }, { 0x00E00000, L"IMAGE_SCN_ALIGN_8192BYTES" }, { 0x00F00000, L"IMAGE_SCN_ALIGN_MASK" }, { 0x01000000, L"IMAGE_SCN_LNK_NRELOC_OVFL (Section contains extended relocations)" }, { 0x02000000, L"IMAGE_SCN_MEM_DISCARDABLE (Section can be discarded)" }, { 0x04000000, L"IMAGE_SCN_MEM_NOT_CACHED (Section is not cachable)" }, { 0x08000000, L"IMAGE_SCN_MEM_NOT_PAGED (Section is not pageable)" }, { 0x10000000, L"IMAGE_SCN_MEM_SHARED (Section is shareable)" }, { 0x20000000, L"IMAGE_SCN_MEM_EXECUTE (Section is executable)" }, { 0x40000000, L"IMAGE_SCN_MEM_READ (Section is readable)" }, { 0x80000000, L"IMAGE_SCN_MEM_WRITE (Section is writeable)" } }; //Export table. 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 vecFuncs; //Array of the exported functions struct. }; //Import table: 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 this Import descriptor. IMAGE_IMPORT_DESCRIPTOR stImportDesc { }; //Standard Import descriptor. std::string strModuleName; //Imported module name. std::vector vecImportFunc; //Array of imported functions. }; using PEIMPORT_VEC = std::vector; /**************************************Resources by Levels******************************************* * There are 3 levels of resources: 1. Type 2. Name 3. Language. * * https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#the-rsrc-section * * Highest (root) resource structure is PERESROOT. It's a struct that includes: * * an IMAGE_RESOURCE_DIRECTORY of root resource directory itself and vector, * * that contains structs of all IMAGE_RESOURCE_DIRECTORY_ENTRY structures of the root resource * * directory. It also includes: wstring(Resource name), IMAGE_RESOURCE_DATA_ENTRY, * * vector (RAW resource data), and PERESLVL2 that is a struct of the next, second, resource * * level, that replicates struct of root resource level. PERESLVL2 includes IMAGE_RESOURCE_DIRECTORY * * of the second resource level, and vector that includes PERESLVL3 that is a struct * * of the last, third, level of resources. Like previous two, this last level's struct consist of * * IMAGE_RESOURCE_DIRECTORY and vector, that is again a vector of structs of all * * IMAGE_RESOURCE_DIRECTORY_ENTRY of the last, third, level of resources. * ****************************************************************************************************/ //Level 3/Lang (the lowest) resources. struct PERESLVL3DATA { IMAGE_RESOURCE_DIRECTORY_ENTRY stResDirEntry { }; //Level 3 (Lang) standard IMAGE_RESOURCE_DIRECTORY_ENTRY struct. std::wstring wstrResName; //Level 3 (Lang) resource name. IMAGE_RESOURCE_DATA_ENTRY stResDataEntry { }; //Level 3 (Lang) standard IMAGE_RESOURCE_DATA_ENTRY struct. std::vector vecRawResData; //Level 3 (Lang) resource raw data. }; using PERESLANGDATA = PERESLVL3DATA; struct PERESLVL3 { DWORD dwOffset { }; //File's raw offset of this level 3 IMAGE_RESOURCE_DIRECTORY descriptor. IMAGE_RESOURCE_DIRECTORY stResDir { }; //Level 3 standard IMAGE_RESOURCE_DIRECTORY header. std::vector vecResData; //Array of level 3 resource entries. }; using PERESLANG = PERESLVL3; //Level 2/Name resources — Includes Lang resourses. struct PERESLVL2DATA { IMAGE_RESOURCE_DIRECTORY_ENTRY stResDirEntry { }; //Level 2 (Name) standard IMAGE_RESOURCE_DIRECTORY_ENTRY struct. std::wstring wstrResName; //Level 2 (Name) resource name. IMAGE_RESOURCE_DATA_ENTRY stResDataEntry { }; //Level 2 (Name) standard IMAGE_RESOURCE_DATA_ENTRY struct. std::vector vecRawResData; //Level 2 (Name) resource raw data. PERESLVL3 stResLvL3; //Level 3 (Lang) resource struct. }; using PERESNAMEDATA = PERESLVL2DATA; struct PERESLVL2 { DWORD dwOffset { }; //File's raw offset of this level 2 IMAGE_RESOURCE_DIRECTORY descriptor. IMAGE_RESOURCE_DIRECTORY stResDir { }; //Level 2 standard IMAGE_RESOURCE_DIRECTORY header. std::vector vecResData; //Array of level 2 resource entries. }; using PERESNAME = PERESLVL2; //Level 1/Type resources — Includes Name Resources. struct PERESROOTDATA { IMAGE_RESOURCE_DIRECTORY_ENTRY stResDirEntry { }; //Level root (Type) standard IMAGE_RESOURCE_DIRECTORY_ENTRY struct. std::wstring wstrResName; //Level root (Type) resource name. IMAGE_RESOURCE_DATA_ENTRY stResDataEntry { }; //Level root (Type) standard IMAGE_RESOURCE_DATA_ENTRY struct. std::vector vecRawResData; //Level root (Type) resource raw data. PERESLVL2 stResLvL2; //Level 2 (Name) resource struct. }; using PERESTYPEDATA = PERESROOTDATA; struct PERESROOT { DWORD dwOffset { }; //File's raw offset of this level root IMAGE_RESOURCE_DIRECTORY descriptor. IMAGE_RESOURCE_DIRECTORY stResDir { }; //Level root standard IMAGE_RESOURCE_DIRECTORY header. std::vector vecResData; //Array of level root resource entries. }; using PERESTYPE = PERESROOT; //Flattened resources. struct PERESFLAT { std::span 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; const std::unordered_map MapResID { { static_cast(1), L"RT_CURSOR" }, { static_cast(2), L"RT_BITMAP" }, { static_cast(3), L"RT_ICON" }, { static_cast(4), L"RT_MENU" }, { static_cast(5), L"RT_DIALOG" }, { static_cast(6), L"RT_STRING" }, { static_cast(7), L"RT_FONTDIR" }, { static_cast(8), L"RT_FONT" }, { static_cast(9), L"RT_ACCELERATOR" }, { static_cast(10), L"RT_RCDATA" }, { static_cast(11), L"RT_MESSAGETABLE" }, { static_cast(12), L"RT_GROUP_CURSOR" }, { static_cast(14), L"RT_GROUP_ICON" }, { static_cast(16), L"RT_VERSION" }, { static_cast(17), L"RT_DLGINCLUDE" }, { static_cast(19), L"RT_PLUGPLAY" }, { static_cast(20), L"RT_VXD" }, { static_cast(21), L"RT_ANICURSOR" }, { static_cast(22), L"RT_ANIICON" }, { static_cast(23), L"RT_HTML" }, { static_cast(24), L"RT_MANIFEST" }, { static_cast(28), L"RT_RIBBON_XML" }, { static_cast(240), L"RT_DLGINIT" }, { static_cast(241), L"RT_TOOLBAR" } }; /*********************************Resources End*****************************************/ //Exception table. struct PEEXCEPTION { DWORD dwOffset { }; //File's raw offset of this exception's descriptor. _IMAGE_RUNTIME_FUNCTION_ENTRY stRuntimeFuncEntry { }; //Standard _IMAGE_RUNTIME_FUNCTION_ENTRY header. }; using PEEXCEPTION_VEC = std::vector; //Security table. struct PEWIN_CERTIFICATE { //Full replica of the WIN_CERTIFICATE struct from the . 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; const std::unordered_map MapWinCertRevision { //WIN_CERTIFICATE::wRevision. { static_cast(0x0100), L"WIN_CERT_REVISION_1_0" }, { static_cast(0x0200), L"WIN_CERT_REVISION_2_0" } }; const std::unordered_map MapWinCertType { //WIN_CERTIFICATE::wCertificateType. { static_cast(0x0001), L"WIN_CERT_TYPE_X509" }, { static_cast(0x0002), L"WIN_CERT_TYPE_PKCS_SIGNED_DATA" }, { static_cast(0x0003), L"WIN_CERT_TYPE_RESERVED_1" }, { static_cast(0x0004), L"WIN_CERT_TYPE_TS_STACK_SIGNED" } }; //Relocation table. struct PERELOCDATA { DWORD dwOffset { }; //File's raw offset of this Relocation data descriptor. WORD wRelocType { }; //Relocation type. WORD wRelocOffset { }; //Relocation offset (Offset the relocation must be applied to.) }; const std::unordered_map MapRelocType { //PERELOCDATA::wRelocType. { static_cast(0), L"IMAGE_REL_BASED_ABSOLUTE" }, { static_cast(1), L"IMAGE_REL_BASED_HIGH" }, { static_cast(2), L"IMAGE_REL_BASED_LOW" }, { static_cast(3), L"IMAGE_REL_BASED_HIGHLOW" }, { static_cast(4), L"IMAGE_REL_BASED_HIGHADJ" }, { static_cast(5), L"IMAGE_REL_BASED_MACHINE_SPECIFIC_5" }, { static_cast(6), L"IMAGE_REL_BASED_RESERVED" }, { static_cast(7), L"IMAGE_REL_BASED_MACHINE_SPECIFIC_7" }, { static_cast(8), L"IMAGE_REL_BASED_MACHINE_SPECIFIC_8" }, { static_cast(9), L"IMAGE_REL_BASED_MACHINE_SPECIFIC_9" }, { static_cast(10), L"IMAGE_REL_BASED_DIR64" } }; struct PERELOC { DWORD dwOffset { }; //File's raw offset of this Relocation descriptor. IMAGE_BASE_RELOCATION stBaseReloc { }; //Standard IMAGE_BASE_RELOCATION header. std::vector vecRelocData; //Array of the Relocation data struct. }; using PERELOC_VEC = std::vector; //Debug table. 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 this Debug descriptor. IMAGE_DEBUG_DIRECTORY stDebugDir { }; //Standard IMAGE_DEBUG_DIRECTORY header. PEDEBUGDBGHDR stDebugHdrInfo; //Debug info header. }; using PEDEBUG_VEC = std::vector; const std::unordered_map MapDbgType { //IMAGE_DEBUG_DIRECTORY::Type. { 0UL, L"IMAGE_DEBUG_TYPE_UNKNOWN" }, { 1UL, L"IMAGE_DEBUG_TYPE_COFF" }, { 2UL, L"IMAGE_DEBUG_TYPE_CODEVIEW" }, { 3UL, L"IMAGE_DEBUG_TYPE_FPO" }, { 4UL, L"IMAGE_DEBUG_TYPE_MISC" }, { 5UL, L"IMAGE_DEBUG_TYPE_EXCEPTION" }, { 6UL, L"IMAGE_DEBUG_TYPE_FIXUP" }, { 7UL, L"IMAGE_DEBUG_TYPE_OMAP_TO_SRC" }, { 8UL, L"IMAGE_DEBUG_TYPE_OMAP_FROM_SRC" }, { 9UL, L"IMAGE_DEBUG_TYPE_BORLAND" }, { 10UL, L"IMAGE_DEBUG_TYPE_RESERVED10" }, { 11UL, L"IMAGE_DEBUG_TYPE_CLSID" }, { 12UL, L"IMAGE_DEBUG_TYPE_VC_FEATURE" }, { 13UL, L"IMAGE_DEBUG_TYPE_POGO" }, { 14UL, L"IMAGE_DEBUG_TYPE_ILTCG" }, { 15UL, L"IMAGE_DEBUG_TYPE_MPX" }, { 16UL, L"IMAGE_DEBUG_TYPE_REPRO" } }; //TLS table. 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 vecTLSCallbacks; //Array of the TLS callbacks. }; const std::unordered_map MapTLSCharact { //IMAGE_TLS_DIRECTORY::Characteristics. { 0x00100000UL, L"IMAGE_SCN_ALIGN_1BYTES" }, { 0x00200000UL, L"IMAGE_SCN_ALIGN_2BYTES" }, { 0x00300000UL, L"IMAGE_SCN_ALIGN_4BYTES" }, { 0x00400000UL, L"IMAGE_SCN_ALIGN_8BYTES" }, { 0x00500000UL, L"IMAGE_SCN_ALIGN_16BYTES" }, { 0x00600000UL, L"IMAGE_SCN_ALIGN_32BYTES" }, { 0x00700000UL, L"IMAGE_SCN_ALIGN_64BYTES" }, { 0x00800000UL, L"IMAGE_SCN_ALIGN_128BYTES" }, { 0x00900000UL, L"IMAGE_SCN_ALIGN_256BYTES" }, { 0x00A00000UL, L"IMAGE_SCN_ALIGN_512BYTES" }, { 0x00B00000UL, L"IMAGE_SCN_ALIGN_1024BYTES" }, { 0x00C00000UL, L"IMAGE_SCN_ALIGN_2048BYTES" }, { 0x00D00000UL, L"IMAGE_SCN_ALIGN_4096BYTES" }, { 0x00E00000UL, L"IMAGE_SCN_ALIGN_8192BYTES" }, { 0x00F00000UL, L"IMAGE_SCN_ALIGN_MASK" } }; //LoadConfigDirectory. 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 { }; }; const std::unordered_map MapLCDGuardFlags { //IMAGE_LOAD_CONFIG_DIRECTORY::GuardFlags. { 0x00000100UL, L"IMAGE_GUARD_CF_INSTRUMENTED (Module performs control flow integrity checks using system-supplied support)" }, { 0x00000200UL, L"IMAGE_GUARD_CFW_INSTRUMENTED (Module performs control flow and write integrity checks)" }, { 0x00000400UL, L"IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT (Module contains valid control flow target metadata)" }, { 0x00000800UL, L"IMAGE_GUARD_SECURITY_COOKIE_UNUSED (Module does not make use of the /GS security cookie)" }, { 0x00001000UL, L"IMAGE_GUARD_PROTECT_DELAYLOAD_IAT (Module supports read only delay load IAT)" }, { 0x00002000UL, L"IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION (Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected)" }, { 0x00004000UL, L"IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT (Module contains suppressed export information. This also infers that the address taken IAT table is also present in the load config.)" }, { 0x00008000UL, L"IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION (Module enables suppression of exports)" }, { 0x00010000UL, L"IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT (Module contains longjmp target information)" }, { 0x00020000UL, L"IMAGE_GUARD_RF_INSTRUMENTED (Module contains return flow instrumentation and metadata)" }, { 0x00040000UL, L"IMAGE_GUARD_RF_ENABLE (Module requests that the OS enable return flow protection)" }, { 0x00080000UL, L"IMAGE_GUARD_RF_STRICT (Module requests that the OS enable return flow protection in strict mode)" }, { 0xF0000000UL, L"IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK (Stride of Guard CF function table encoded in these bits (additional count of bytes per element))" }, { 28UL, L"IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT (Shift to right-justify Guard CF function table stride)" } }; //Bound import table. struct PEBOUNDFORWARDER { DWORD dwOffset { }; //File's raw offset of this 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 this Bound Import descriptor. IMAGE_BOUND_IMPORT_DESCRIPTOR stBoundImpDesc { }; //Standard IMAGE_BOUND_IMPORT_DESCRIPTOR struct. std::string strBoundName; //Bound Import name. std::vector vecBoundForwarder; //Array of the Bound Forwarder structs. }; using PEBOUNDIMPORT_VEC = std::vector; //Delay import table. 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 vecDelayImpFunc; //Array of the Delay Import module functions. }; using PEDELAYIMPORT_VEC = std::vector; //COM descriptor table. struct PECOMDESCRIPTOR { DWORD dwOffset { }; //File's raw offset of the IMAGE_COR20_HEADER descriptor. IMAGE_COR20_HEADER stCorHdr { }; //Standard IMAGE_COR20_HEADER struct. }; const std::unordered_map MapCOR20Flags { //IMAGE_COR20_HEADER::Flags. { 1UL, L"COMIMAGE_FLAGS_ILONLY" }, { 2UL, L"COMIMAGE_FLAGS_32BITREQUIRED" }, { 4UL, L"COMIMAGE_FLAGS_IL_LIBRARY" }, { 8UL, L"COMIMAGE_FLAGS_STRONGNAMESIGNED" }, { 16UL, L"COMIMAGE_FLAGS_NATIVE_ENTRYPOINT" }, { 65536UL, L"COMIMAGE_FLAGS_TRACKDEBUGDATA" }, { 131072UL, L"COMIMAGE_FLAGS_32BITPREFERRED" } }; enum class EFileType : std::uint8_t { UNKNOWN = 0, PE32, PE64, PEROM }; //Return codes. constexpr auto PEOK = 0x0; constexpr auto ERR_FILE_OPEN = 0x01; constexpr auto ERR_FILE_SIZESMALL = 0x02; constexpr auto ERR_FILE_MAPPING = 0x03; constexpr auto ERR_FILE_NODOSHDR = 0x04; //Helper methods. [[nodiscard]] constexpr auto GetFileType(const PENTHDR& stNTHdr) -> EFileType { const auto& refNTHdr = stNTHdr.unHdr.stNTHdr32; if (refNTHdr.Signature != IMAGE_NT_SIGNATURE) return EFileType::UNKNOWN; const auto& refOptHdr = refNTHdr.OptionalHeader; switch (refOptHdr.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: return EFileType::PE32; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: return EFileType::PE64; case IMAGE_ROM_OPTIONAL_HDR_MAGIC: return EFileType::PEROM; default: return EFileType::UNKNOWN; } } [[nodiscard]] constexpr auto GetImageBase(const PENTHDR& stNTHdr) -> ULONGLONG { switch (GetFileType(stNTHdr)) { case EFileType::PE32: return stNTHdr.unHdr.stNTHdr32.OptionalHeader.ImageBase; case EFileType::PE64: return stNTHdr.unHdr.stNTHdr64.OptionalHeader.ImageBase; default: return { }; } } [[nodiscard]] constexpr auto GetOffsetFromRVA(ULONGLONG ullRVA, const PESECHDR_VEC& vecSecHdr) -> DWORD { for (const auto& stSec : vecSecHdr) { if (const auto pSecHdr = &stSec.stSecHdr; (ullRVA >= pSecHdr->VirtualAddress) //Is RVA within this section? && (ullRVA < (pSecHdr->VirtualAddress + pSecHdr->Misc.VirtualSize))) { return static_cast(ullRVA) - pSecHdr->VirtualAddress + pSecHdr->PointerToRawData; } } return { }; } [[nodiscard]] constexpr auto FlatResources(const PERESROOT& stResRoot) -> PERESFLAT_VEC { std::size_t sTotalRes { 0 }; //How many resources total? for (const auto& iterRoot : stResRoot.vecResData) { //To reserve space in vector, count total amount of resources. if (iterRoot.stResDirEntry.DataIsDirectory) { //Level Root. for (const auto& iterLvL2 : iterRoot.stResLvL2.vecResData) { if (iterLvL2.stResDirEntry.DataIsDirectory) { //Level 2 IMAGE_RESOURCE_DIRECTORY_ENTRY. sTotalRes += iterLvL2.stResLvL3.vecResData.size(); //Level 3. } else { ++sTotalRes; } } } else { ++sTotalRes; } } std::vector vecData; vecData.reserve(sTotalRes); for (const auto& iterRoot : stResRoot.vecResData) { PERESFLAT stRes { }; const auto pResDirEntryRoot = &iterRoot.stResDirEntry; //Level Root IMAGE_RESOURCE_DIRECTORY_ENTRY. if (pResDirEntryRoot->NameIsString) { stRes.wsvTypeStr = iterRoot.wstrResName; } else { stRes.wTypeID = pResDirEntryRoot->Id; } if (pResDirEntryRoot->DataIsDirectory) { for (const auto& iterLvL2 : iterRoot.stResLvL2.vecResData) { const auto pResDirEntry2 = &iterLvL2.stResDirEntry; //Level 2 IMAGE_RESOURCE_DIRECTORY_ENTRY. if (pResDirEntry2->NameIsString) { stRes.wsvNameStr = iterLvL2.wstrResName; } else { stRes.wNameID = pResDirEntry2->Id; } if (pResDirEntry2->DataIsDirectory) { for (const auto& iterLvL3 : iterLvL2.stResLvL3.vecResData) { const auto pResDirEntry3 = &iterLvL3.stResDirEntry; //Level 3 IMAGE_RESOURCE_DIRECTORY_ENTRY. if (pResDirEntry3->NameIsString) { stRes.wsvLangStr = iterLvL3.wstrResName; } else { stRes.wLangID = pResDirEntry3->Id; } stRes.spnData = iterLvL3.vecRawResData; vecData.emplace_back(stRes); } } else { stRes.spnData = iterLvL2.vecRawResData; vecData.emplace_back(stRes); } } } else { stRes.spnData = iterRoot.vecRawResData; vecData.emplace_back(stRes); } } return vecData; } class Clibpe final { public: Clibpe() = default; Clibpe(const wchar_t* pwszFile); Clibpe(const Clibpe&) = delete; Clibpe(Clibpe&&) = delete; ~Clibpe(); auto OpenFile(const wchar_t* pwszFile) -> int; auto OpenFile(std::span spnData) -> int; void CloseFile(); [[nodiscard]] auto GetDOSHeader()const -> std::optional; [[nodiscard]] auto GetRichHeader()const -> std::optional; [[nodiscard]] auto GetNTHeader()const -> std::optional; [[nodiscard]] auto GetDataDirs()const -> std::optional; [[nodiscard]] auto GetSecHeaders()const -> std::optional; [[nodiscard]] auto GetExport()const -> std::optional; [[nodiscard]] auto GetImport()const -> std::optional; [[nodiscard]] auto GetResources()const -> std::optional; [[nodiscard]] auto GetExceptions()const -> std::optional; [[nodiscard]] auto GetSecurity()const -> std::optional; [[nodiscard]] auto GetRelocations()const -> std::optional; [[nodiscard]] auto GetDebug()const -> std::optional; [[nodiscard]] auto GetTLS()const -> std::optional; [[nodiscard]] auto GetLoadConfig()const -> std::optional; [[nodiscard]] auto GetBoundImport()const -> std::optional; [[nodiscard]] auto GetDelayImport()const -> std::optional; [[nodiscard]] auto GetCOMDescriptor()const -> std::optional; private: [[nodiscard]] auto GetFileSize()const -> ULONGLONG; [[nodiscard]] auto GetBaseAddr()const -> DWORD_PTR; [[nodiscard]] auto GetDosPtr()const -> const IMAGE_DOS_HEADER*; [[nodiscard]] auto GetDirEntryRVA(DWORD dwEntry)const -> DWORD; [[nodiscard]] auto GetDirEntrySize(DWORD dwEntry)const -> DWORD; [[nodiscard]] auto GetImageBase()const -> ULONGLONG; [[nodiscard]] auto GetSecHdrFromShortName(LPCSTR lpszName)const -> PIMAGE_SECTION_HEADER; [[nodiscard]] auto GetSecHdrFromRVA(ULONGLONG ullRVA)const -> PIMAGE_SECTION_HEADER; [[nodiscard]] auto GetSecIdxFromRVA(ULONGLONG ullRVA)const -> std::optional; template [[nodiscard]] auto GetTData(ULONGLONG ullOffset)const -> T; template [[nodiscard]] auto IsPtrSafe(T tAddr, bool fCanReferenceBoundary = false)const -> bool; [[nodiscard]] auto PtrToOffset(LPCVOID lp)const -> DWORD; [[nodiscard]] auto RVAToPtr(ULONGLONG ullRVA)const -> LPVOID; bool ParseDOSHeader(); bool ParseNTFileOptHeader(); private: std::span m_spnData; //File data. PIMAGE_NT_HEADERS32 m_pNTHeader32 { }; //NT header for x86. PIMAGE_NT_HEADERS64 m_pNTHeader64 { }; //NT header for x64. EFileType m_ePEType { }; //PE type: x64 or x86. HANDLE m_hFile { }; //Opened file handle. HANDLE m_hFileMap { }; //File-mapping handle. LPVOID m_pFileView { }; //File mapping-view handle. bool m_fOpened { }; //Is file successfully opened (by any OpenFile method)? bool m_fFileHandle { }; //Was file handle opened (by OpenFile(const wchar_t* pwszFile))? bool m_fHasNTHdr { }; //Does file have at least NT header. }; Clibpe::Clibpe(const wchar_t* pwszFile) { OpenFile(pwszFile); } Clibpe::~Clibpe() { CloseFile(); } auto Clibpe::OpenFile(const wchar_t* pwszFile)->int { assert(pwszFile != nullptr); if (m_fFileHandle) { CloseFile(); } m_hFile = ::CreateFileW(pwszFile, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); assert(m_hFile != INVALID_HANDLE_VALUE); if (m_hFile == INVALID_HANDLE_VALUE) return ERR_FILE_OPEN; LARGE_INTEGER stLI { }; if (::GetFileSizeEx(m_hFile, &stLI) == FALSE || stLI.QuadPart < sizeof(IMAGE_DOS_HEADER)) { ::CloseHandle(m_hFile); return ERR_FILE_SIZESMALL; } m_hFileMap = ::CreateFileMappingW(m_hFile, nullptr, PAGE_READONLY, 0, 0, nullptr); assert(m_hFileMap != nullptr); if (m_hFileMap == nullptr) { ::CloseHandle(m_hFile); return ERR_FILE_MAPPING; } m_pFileView = ::MapViewOfFile(m_hFileMap, FILE_MAP_READ, 0, 0, 0); assert(m_pFileView != nullptr); //Not enough memory? File is too big? if (m_pFileView == nullptr) { ::CloseHandle(m_hFileMap); ::CloseHandle(m_hFile); return ERR_FILE_MAPPING; } m_fFileHandle = true; return OpenFile({ static_cast(m_pFileView), static_cast(stLI.QuadPart) }); } auto Clibpe::OpenFile(std::span spnData)->int { assert(!spnData.empty()); if (m_fOpened) { CloseFile(); } if (spnData.size() < sizeof(IMAGE_DOS_HEADER)) return ERR_FILE_SIZESMALL; m_spnData = spnData; if (!ParseDOSHeader()) { CloseFile(); return ERR_FILE_NODOSHDR; } m_fOpened = true; ParseNTFileOptHeader(); return PEOK; } void Clibpe::CloseFile() { if (m_fFileHandle) { ::UnmapViewOfFile(m_pFileView); ::CloseHandle(m_hFileMap); ::CloseHandle(m_hFile); } m_hFile = nullptr; m_hFileMap = nullptr; m_pFileView = nullptr; m_pNTHeader32 = nullptr; m_pNTHeader64 = nullptr; m_ePEType = { }; m_spnData = { }; m_fHasNTHdr = false; m_fFileHandle = false; m_fOpened = false; } auto Clibpe::GetDOSHeader()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; return *GetDosPtr(); } auto Clibpe::GetRichHeader()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; //Undocumented, so called «Rich» header, dwells not in all PE files. //«Rich» stub starts at 0x80 offset, before pDosHdr->e_lfanew (PE header offset start). //If e_lfanew <= 0x80 — there is no «Rich» header. const auto ullBaseAddr = GetBaseAddr(); const auto e_lfanew = GetDosPtr()->e_lfanew; if (e_lfanew <= 0x80 || !IsPtrSafe(ullBaseAddr + static_cast(e_lfanew))) return std::nullopt; const auto pRichStartVA = reinterpret_cast(ullBaseAddr + 0x80); auto pRichIter = pRichStartVA; const auto ulDWORDs = (e_lfanew - 0x80) / sizeof(DWORD); //Maximum amount of DWORDs to check. for (auto i = 0UL; i < ulDWORDs; ++i, ++pRichIter) { //Check for the "Rich" (ASCII) sign, it's always at the end of the «Rich» header. //Then take a DWORD right after the "Rich" sign, it's a XOR mask. //Apply this mask to the first DWORD of the «Rich» header, it must be a "DanS" (ASCII) after XORing. if ((*pRichIter == 0x68636952/*"Rich"*/) && ((*pRichStartVA ^ *(pRichIter + 1)) == 0x536E6144/*"DanS"*/) && (reinterpret_cast(pRichIter) >= ullBaseAddr + 0x90)) { //To avoid too small (bogus) «Rich» header. //An amount of all «Rich» DOUBLE_DWORD structs. //First 16 bytes in the «Rich» header are irrelevant, it's "DanS" itself and 12 zeroed bytes. //That's why we subtracting 0x90, to find out the amount of all «Rich» structures: //0x80 («Rich» start) + 16 (0xF) = 0x90. const auto dwRichQWORDs = static_cast((reinterpret_cast(pRichIter) - ullBaseAddr) - 0x90) / 8; const auto dwRichXORMask = *(pRichIter + 1); //XOR mask of «Rich» header. auto pRichData = reinterpret_cast(ullBaseAddr + 0x90); //Beginning of the «Rich» DOUBLE_DWORD structs. PERICHHDR_VEC vecRichHdr; for (auto j = 0UL; j < dwRichQWORDs; ++j) { //Pushing double DWORD of «Rich» structure, disassembling first DWORD by two WORDs. vecRichHdr.emplace_back(static_cast(reinterpret_cast(pRichData) - ullBaseAddr), HIWORD(dwRichXORMask ^ *pRichData), LOWORD(dwRichXORMask ^ *pRichData), dwRichXORMask ^ *(pRichData + 1)); pRichData += 2; //Jump to the next DOUBLE_DWORD. } return vecRichHdr; } } return std::nullopt; } auto Clibpe::GetNTHeader()const->std::optional { assert(m_fOpened); if (!m_fOpened || !m_fHasNTHdr) return std::nullopt; PENTHDR stNTHdr; switch (m_ePEType) { case EFileType::PE32: stNTHdr.unHdr.stNTHdr32 = *m_pNTHeader32; stNTHdr.dwOffset = PtrToOffset(m_pNTHeader32); break; case EFileType::PE64: stNTHdr.unHdr.stNTHdr64 = *m_pNTHeader64; stNTHdr.dwOffset = PtrToOffset(m_pNTHeader64); break; default: return std::nullopt; } return stNTHdr; } auto Clibpe::GetDataDirs()const->std::optional { assert(m_fOpened); if (!m_fOpened || !m_fHasNTHdr) return std::nullopt; PIMAGE_DATA_DIRECTORY pDataDir; DWORD dwRVAAndSizes; switch (m_ePEType) { case EFileType::PE32: pDataDir = reinterpret_cast(m_pNTHeader32->OptionalHeader.DataDirectory); dwRVAAndSizes = m_pNTHeader32->OptionalHeader.NumberOfRvaAndSizes; break; case EFileType::PE64: pDataDir = reinterpret_cast(m_pNTHeader64->OptionalHeader.DataDirectory); dwRVAAndSizes = m_pNTHeader64->OptionalHeader.NumberOfRvaAndSizes; break; default: return std::nullopt; } PEDATADIR_VEC vecDataDirs; for (DWORD itDir = IMAGE_DIRECTORY_ENTRY_EXPORT; itDir < (dwRVAAndSizes > 15 ? 15 : dwRVAAndSizes); ++itDir, ++pDataDir) { DWORD dwSecIndex { }; if (itDir != IMAGE_DIRECTORY_ENTRY_SECURITY) { //RVA of the IMAGE_DIRECTORY_ENTRY_SECURITY is a file RAW offset. dwSecIndex = GetSecIdxFromRVA(pDataDir->VirtualAddress).value_or(0xFFFFFFFFUL); } vecDataDirs.emplace_back(*pDataDir, dwSecIndex); } return vecDataDirs.empty() ? std::nullopt : std::optional(std::move(vecDataDirs)); } auto Clibpe::GetSecHeaders()const->std::optional { assert(m_fOpened); if (!m_fOpened || !m_fHasNTHdr) return std::nullopt; PIMAGE_SECTION_HEADER pSecHdr; WORD wNumSections; DWORD dwSymbolTable; DWORD dwNumberOfSymbols; switch (m_ePEType) { case EFileType::PE32: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader32); wNumSections = m_pNTHeader32->FileHeader.NumberOfSections; dwSymbolTable = m_pNTHeader32->FileHeader.PointerToSymbolTable; dwNumberOfSymbols = m_pNTHeader32->FileHeader.NumberOfSymbols; break; case EFileType::PE64: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader64); wNumSections = m_pNTHeader64->FileHeader.NumberOfSections; dwSymbolTable = m_pNTHeader64->FileHeader.PointerToSymbolTable; dwNumberOfSymbols = m_pNTHeader64->FileHeader.NumberOfSymbols; break; default: return std::nullopt; } PESECHDR_VEC vecSecHeaders; vecSecHeaders.reserve(wNumSections); for (auto itSec = 0UL; itSec < wNumSections; ++itSec, ++pSecHdr) { if (!IsPtrSafe(reinterpret_cast(pSecHdr) + sizeof(IMAGE_SECTION_HEADER))) break; std::string strSecRealName; if (pSecHdr->Name[0] == '/') { //Deprecated, but still used "feature" of a section name. //«An 8-byte, null-padded UTF-8 string. There is no terminating null character //if the string is exactly eight characters long. //For longer names, this member contains a forward slash (/) followed by an ASCII //representation of a decimal number that is an offset into the string table.» //https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_section_header //The String Table dwells right after the end of a Symbol Table. //Each symbol in the Symbol Table occupies exactly 18 bytes. //So the String Table's beginning can be calculated as: //FileHeader.PointerToSymbolTable + FileHeader.NumberOfSymbols * 18; const auto pStart = reinterpret_cast(&pSecHdr->Name[1]); char* pEnd { }; errno = 0; const auto lOffset = ::strtol(pStart, &pEnd, 10); if (pEnd == pStart || errno == ERANGE) { continue; //Going next section entry. } const auto lpszSecRealName = reinterpret_cast(GetBaseAddr() + static_cast(dwSymbolTable) + (static_cast(dwNumberOfSymbols) * 18) + static_cast(lOffset)); if (IsPtrSafe(lpszSecRealName)) { strSecRealName = lpszSecRealName; } } vecSecHeaders.emplace_back(PtrToOffset(pSecHdr), *pSecHdr, std::move(strSecRealName)); } return vecSecHeaders.empty() ? std::nullopt : std::optional(std::move(vecSecHeaders)); } auto Clibpe::GetExport()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto dwExportStartRVA = GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_EXPORT); const auto dwExportEndRVA = dwExportStartRVA + GetDirEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT); const auto pExportDir = static_cast(RVAToPtr(dwExportStartRVA)); if (pExportDir == nullptr) return std::nullopt; const auto pdwFuncRVA = static_cast(RVAToPtr(pExportDir->AddressOfFunctions)); if (pdwFuncRVA == nullptr) return std::nullopt; const auto pwFuncOrdinals = static_cast(RVAToPtr(pExportDir->AddressOfNameOrdinals)); const auto pdwFuncNames = static_cast(RVAToPtr(pExportDir->AddressOfNames)); std::vector vecFuncs; try { for (auto iterFuncRVA = 0UL; iterFuncRVA < pExportDir->NumberOfFunctions; ++iterFuncRVA) { if (!IsPtrSafe(pdwFuncRVA + iterFuncRVA)) //Checking pdwFuncRVA array. break; const auto dwFuncRVA = pdwFuncRVA[iterFuncRVA]; //Function RVA; if (dwFuncRVA == 0) //if RVA==0 —> going next entry. continue; std::string strFuncName; DWORD dwFuncNameRVA { }; if (pdwFuncNames != nullptr && pwFuncOrdinals != nullptr) { for (auto iterFuncNames = 0UL; iterFuncNames < pExportDir->NumberOfNames; ++iterFuncNames) { if (!IsPtrSafe(pwFuncOrdinals + iterFuncNames)) //Checking pwFuncOrdinals array. break; //Correspondence between ordinal-name-RVA: //ordinal = Biased_Ordinal - OrdinalBase; //FuncRVA = AddressOfFunctions[ordinal]; //index = Search_In_AddressOfNameOrdinals(ordinal); //FuncNameRVA = AddressOfNames[index]; //Comparing data in ordinals array (unbiased ordinals) with the index in the array of func RVA. //This index is in fact a true unbiased ordinal of exported function. //If found then iterFuncNames is an index in the array of func names. if (pwFuncOrdinals[iterFuncNames] == iterFuncRVA) { dwFuncNameRVA = pdwFuncNames[iterFuncNames]; //Export func name RVA. if (const auto pszFuncName = static_cast(RVAToPtr(dwFuncNameRVA)); //Checking func name for length correctness. pszFuncName != nullptr && (::StringCchLengthA(pszFuncName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strFuncName = pszFuncName; } break; } } } std::string strForwarderName; if ((dwFuncRVA >= dwExportStartRVA) && (dwFuncRVA <= dwExportEndRVA)) { if (const auto pszForwarderName = static_cast(RVAToPtr(dwFuncRVA)); //Checking forwarder name for length correctness. pszForwarderName && (::StringCchLengthA(pszForwarderName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strForwarderName = pszForwarderName; } } vecFuncs.emplace_back(dwFuncRVA, iterFuncRVA + pExportDir->Base /*Biased ordinal*/, dwFuncNameRVA, std::move(strFuncName), std::move(strForwarderName)); } std::string strModuleName; //Actual IMG name. if (const auto szExportName = static_cast(RVAToPtr(pExportDir->Name)); //Checking Export name for length correctness. szExportName && (::StringCchLengthA(szExportName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strModuleName = szExportName; } return std::make_optional(PtrToOffset(pExportDir), *pExportDir, std::move(strModuleName), std::move(vecFuncs)); } catch (const std::exception& e) { vecFuncs.clear(); std::cerr << "Exception in GetExport(): " << e.what(); } return std::nullopt; } auto Clibpe::GetImport()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; auto pImpDesc = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_IMPORT))); if (pImpDesc == nullptr) return std::nullopt; PEIMPORT_VEC vecImport; try { //Counter for import modules. If it exceeds iMaxModules we stop parsing file, it's definitely bogus. //Very unlikely PE file has more than 1000 import modules. constexpr auto iMaxModules = 1000; constexpr auto iMaxFuncs = 5000; int iModulesCount = 0; if (m_ePEType == EFileType::PE32) { while (pImpDesc->Name != 0) { auto pThunk32 = reinterpret_cast(static_cast(pImpDesc->OriginalFirstThunk)); if (pThunk32 == nullptr) { pThunk32 = reinterpret_cast(static_cast(pImpDesc->FirstThunk)); } if (pThunk32 != nullptr) { pThunk32 = static_cast(RVAToPtr(reinterpret_cast(pThunk32))); if (pThunk32 == nullptr) break; std::vector vecFunc; //Counter for import module funcs, if it exceeds iMaxFuncs we stop parsing import descr, it's definitely bogus. int iFuncsCount = 0; while (pThunk32->u1.AddressOfData != 0) { PEIMPORTFUNC::UNPEIMPORTTHUNK unImpThunk32; unImpThunk32.stThunk32 = *pThunk32; IMAGE_IMPORT_BY_NAME stImpByName { }; std::string strFuncName; if (!(pThunk32->u1.Ordinal & IMAGE_ORDINAL_FLAG32)) { if (const auto pName = static_cast(RVAToPtr(pThunk32->u1.AddressOfData)); pName && (::StringCchLengthA(pName->Name, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { stImpByName = *pName; strFuncName = pName->Name; } } vecFunc.emplace_back(unImpThunk32, stImpByName, std::move(strFuncName)); if (!IsPtrSafe(++pThunk32)) break; if (++iFuncsCount == iMaxFuncs) break; } std::string strDllName; if (const auto szName = static_cast(RVAToPtr(pImpDesc->Name)); szName && (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strDllName = szName; } vecImport.emplace_back(PtrToOffset(pImpDesc), *pImpDesc, std::move(strDllName), std::move(vecFunc)); if (!IsPtrSafe(++pImpDesc)) break; } else { //No IMPORT pointers for that DLL?... if (!IsPtrSafe(++pImpDesc)) //Going next dll. break; } if (++iModulesCount == iMaxModules) break; } } else if (m_ePEType == EFileType::PE64) { while (pImpDesc->Name != 0) { auto pThunk64 = reinterpret_cast(static_cast(pImpDesc->OriginalFirstThunk)); if (pThunk64 == nullptr) { pThunk64 = reinterpret_cast(static_cast(pImpDesc->FirstThunk)); } if (pThunk64 != nullptr) { pThunk64 = static_cast(RVAToPtr(reinterpret_cast(pThunk64))); if (pThunk64 == nullptr) break; std::vector vecFunc; int iFuncsCount = 0; while (pThunk64->u1.AddressOfData != 0) { PEIMPORTFUNC::UNPEIMPORTTHUNK unImpThunk64; unImpThunk64.stThunk64 = *pThunk64; IMAGE_IMPORT_BY_NAME stImpByName { }; std::string strFuncName; if (!(pThunk64->u1.Ordinal & IMAGE_ORDINAL_FLAG64)) { if (const auto pName = static_cast(RVAToPtr(pThunk64->u1.AddressOfData)); pName && (::StringCchLengthA(pName->Name, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { stImpByName = *pName; strFuncName = pName->Name; } } vecFunc.emplace_back(unImpThunk64, stImpByName, std::move(strFuncName)); if (!IsPtrSafe(++pThunk64)) break; if (++iFuncsCount == iMaxFuncs) break; } std::string strDllName; if (const auto szName = static_cast(RVAToPtr(pImpDesc->Name)); szName && (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strDllName = szName; } vecImport.emplace_back(PtrToOffset(pImpDesc), *pImpDesc, std::move(strDllName), std::move(vecFunc)); if (!IsPtrSafe(++pImpDesc)) break; } else { if (!IsPtrSafe(++pImpDesc)) break; } if (++iModulesCount == iMaxModules) break; } } return vecImport.empty() ? std::nullopt : std::optional(std::move(vecImport)); } catch (const std::exception& e) { vecImport.clear(); std::cerr << "Exception in GetImport(): " << e.what(); } return std::nullopt; } auto Clibpe::GetResources()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto pResDirRoot = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_RESOURCE))); if (pResDirRoot == nullptr) return std::nullopt; auto pResDirEntryRoot = reinterpret_cast(pResDirRoot + 1); if (!IsPtrSafe(pResDirEntryRoot)) return std::nullopt; std::vector vecResDataRoot; try { const DWORD dwNumOfEntriesRoot = pResDirRoot->NumberOfNamedEntries + pResDirRoot->NumberOfIdEntries; if (!IsPtrSafe(pResDirEntryRoot + dwNumOfEntriesRoot)) return std::nullopt; vecResDataRoot.reserve(dwNumOfEntriesRoot); for (auto iLvLRoot = 0UL; iLvLRoot < dwNumOfEntriesRoot; ++iLvLRoot) { PIMAGE_RESOURCE_DATA_ENTRY pResDataEntryRoot { }; std::wstring wstrResNameRoot; std::vector vecRawResDataRoot; PERESLVL2 stResLvL2 { }; if (pResDirEntryRoot->NameIsString) { //Name of Resource Type (ICON, BITMAP, MENU, etc...). if (ut::IsSumOverflow(reinterpret_cast(pResDirRoot), static_cast(pResDirEntryRoot->NameOffset))) break; if (const auto pResDirStr = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryRoot->NameOffset)); IsPtrSafe(pResDirStr)) { //Copy not more then MAX_PATH chars into wstrResNameRoot, avoiding overflow. wstrResNameRoot.assign(pResDirStr->NameString, pResDirStr->Length < MAX_PATH ? pResDirStr->Length : MAX_PATH); } } if (pResDirEntryRoot->DataIsDirectory) { const auto pResDirLvL2 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryRoot->OffsetToDirectory)); if (!IsPtrSafe(pResDirLvL2)) break; if (pResDirLvL2 == pResDirRoot) { //Resource loop hack. stResLvL2 = { .dwOffset { PtrToOffset(pResDirLvL2) }, .stResDir { *pResDirLvL2 } }; } else { auto pResDirEntryLvL2 = reinterpret_cast(pResDirLvL2 + 1); const DWORD dwNumOfEntriesLvL2 = pResDirLvL2->NumberOfNamedEntries + pResDirLvL2->NumberOfIdEntries; if (!IsPtrSafe(pResDirEntryLvL2 + dwNumOfEntriesLvL2)) break; std::vector vecResDataLvL2; vecResDataLvL2.reserve(dwNumOfEntriesLvL2); for (auto iLvL2 = 0UL; iLvL2 < dwNumOfEntriesLvL2; ++iLvL2) { PIMAGE_RESOURCE_DATA_ENTRY pResDataEntryLvL2 { }; std::wstring wstrResNameLvL2; std::vector vecRawResDataLvL2; PERESLVL3 stResLvL3 { }; if (pResDirEntryLvL2->NameIsString) { //Name of resource itself if not presented by ID ("AFX_MY_SUPER_DIALOG"...). if (ut::IsSumOverflow(reinterpret_cast(pResDirRoot), static_cast(pResDirEntryLvL2->NameOffset))) break; if (const auto pResDirStr2 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryLvL2->NameOffset)); IsPtrSafe(pResDirStr2)) { //Copy not more then MAX_PATH chars into wstrResNameLvL2, avoiding overflow. wstrResNameLvL2.assign(pResDirStr2->NameString, pResDirStr2->Length < MAX_PATH ? pResDirStr2->Length : MAX_PATH); } } if (pResDirEntryLvL2->DataIsDirectory) { const auto pResDirLvL3 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryLvL2->OffsetToDirectory)); if (!IsPtrSafe(pResDirLvL3)) break; if (pResDirLvL3 == pResDirLvL2 || pResDirLvL3 == pResDirRoot) { stResLvL3 = { .dwOffset { PtrToOffset(pResDirLvL3) }, .stResDir { *pResDirLvL3 } }; } else { auto pResDirEntryLvL3 = reinterpret_cast(pResDirLvL3 + 1); const DWORD dwNumOfEntriesLvL3 = pResDirLvL3->NumberOfNamedEntries + pResDirLvL3->NumberOfIdEntries; if (!IsPtrSafe(pResDirEntryLvL3 + dwNumOfEntriesLvL3)) break; std::vector vecResDataLvL3; vecResDataLvL3.reserve(dwNumOfEntriesLvL3); for (auto iLvL3 = 0UL; iLvL3 < dwNumOfEntriesLvL3; ++iLvL3) { std::wstring wstrResNameLvL3; std::vector vecRawResDataLvL3; if (pResDirEntryLvL3->NameIsString) { if (ut::IsSumOverflow(reinterpret_cast(pResDirRoot), static_cast(pResDirEntryLvL3->NameOffset))) break; if (const auto pResDirStr3 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryLvL3->NameOffset)); IsPtrSafe(pResDirStr3)) { //Copy not more then MAX_PATH chars into wstrResNameLvL3, avoiding overflow. wstrResNameLvL3.assign(pResDirStr3->NameString, pResDirStr3->Length < MAX_PATH ? pResDirStr3->Length : MAX_PATH); } } const auto pResDataEntryLvL3 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryLvL3->OffsetToData)); if (IsPtrSafe(pResDataEntryLvL3)) { //Resource LvL 3 RAW Data. //IMAGE_RESOURCE_DATA_ENTRY::OffsetToData is actually a general RVA, //not an offset from root IMAGE_RESOURCE_DIRECTORY, like IMAGE_RESOURCE_DIRECTORY_ENTRY::OffsetToData. //Checking RAW Resource data pointer out of bounds. if (const auto pThirdResRawDataBegin = static_cast(RVAToPtr(pResDataEntryLvL3->OffsetToData)); pThirdResRawDataBegin && IsPtrSafe(reinterpret_cast(pThirdResRawDataBegin) + static_cast(pResDataEntryLvL3->Size), true)) { vecRawResDataLvL3.assign(pThirdResRawDataBegin, pThirdResRawDataBegin + pResDataEntryLvL3->Size); } } vecResDataLvL3.emplace_back(*pResDirEntryLvL3, std::move(wstrResNameLvL3), IsPtrSafe(pResDataEntryLvL3) ? *pResDataEntryLvL3 : IMAGE_RESOURCE_DATA_ENTRY { }, std::move(vecRawResDataLvL3)); if (!IsPtrSafe(++pResDirEntryLvL3)) break; } stResLvL3 = { .dwOffset { PtrToOffset(pResDirLvL3) }, .stResDir { *pResDirLvL3 }, .vecResData { std::move(vecResDataLvL3) } }; } } else { //Resource LvL2 RAW Data. pResDataEntryLvL2 = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryLvL2->OffsetToData)); if (IsPtrSafe(pResDataEntryLvL2)) { //Checking RAW Resource data pointer out of bounds. if (const auto pSecondResRawDataBegin = static_cast(RVAToPtr(pResDataEntryLvL2->OffsetToData)); pSecondResRawDataBegin && IsPtrSafe(reinterpret_cast(pSecondResRawDataBegin) + static_cast(pResDataEntryLvL2->Size), true)) { vecRawResDataLvL2.assign(pSecondResRawDataBegin, pSecondResRawDataBegin + pResDataEntryLvL2->Size); } } } vecResDataLvL2.emplace_back(*pResDirEntryLvL2, std::move(wstrResNameLvL2), IsPtrSafe(pResDataEntryLvL2) ? *pResDataEntryLvL2 : IMAGE_RESOURCE_DATA_ENTRY { }, std::move(vecRawResDataLvL2), stResLvL3); if (!IsPtrSafe(++pResDirEntryLvL2)) break; } stResLvL2 = { .dwOffset { PtrToOffset(pResDirLvL2) }, .stResDir { *pResDirLvL2 }, .vecResData { std::move(vecResDataLvL2) } }; } } else { //Resource LvL Root RAW Data. pResDataEntryRoot = reinterpret_cast(reinterpret_cast(pResDirRoot) + static_cast(pResDirEntryRoot->OffsetToData)); if (IsPtrSafe(pResDataEntryRoot)) { //Checking RAW Resource data pointer out of bounds. if (const auto pRootResRawDataBegin = static_cast(RVAToPtr(pResDataEntryRoot->OffsetToData)); pRootResRawDataBegin && IsPtrSafe(reinterpret_cast(pRootResRawDataBegin) + static_cast(pResDataEntryRoot->Size), true)) { vecRawResDataRoot.assign(pRootResRawDataBegin, pRootResRawDataBegin + pResDataEntryRoot->Size); } } } vecResDataRoot.emplace_back(*pResDirEntryRoot, std::move(wstrResNameRoot), IsPtrSafe(pResDataEntryRoot) ? *pResDataEntryRoot : IMAGE_RESOURCE_DATA_ENTRY { }, std::move(vecRawResDataRoot), stResLvL2); if (!IsPtrSafe(++pResDirEntryRoot)) break; } return std::make_optional(PtrToOffset(pResDirRoot), *pResDirRoot, std::move(vecResDataRoot)); } catch (const std::exception& e) { vecResDataRoot.clear(); std::cerr << "Exception in GetResources(): " << e.what(); } return std::nullopt; } auto Clibpe::GetExceptions()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; //IMAGE_RUNTIME_FUNCTION_ENTRY (without leading underscore) might have different typedef, //depending on defined platform, see winnt.h auto pRuntimeFuncsEntry = static_cast<_PIMAGE_RUNTIME_FUNCTION_ENTRY>(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_EXCEPTION))); if (pRuntimeFuncsEntry == nullptr) return std::nullopt; const auto dwEntries = GetDirEntrySize(IMAGE_DIRECTORY_ENTRY_EXCEPTION) / static_cast(sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)); if (!dwEntries || !IsPtrSafe(reinterpret_cast(pRuntimeFuncsEntry) + static_cast(dwEntries))) return std::nullopt; PEEXCEPTION_VEC vecException; for (auto i = 0UL; i < dwEntries; ++i, ++pRuntimeFuncsEntry) { if (!IsPtrSafe(pRuntimeFuncsEntry)) break; vecException.emplace_back(PtrToOffset(pRuntimeFuncsEntry), *pRuntimeFuncsEntry); } return vecException.empty() ? std::nullopt : std::optional(std::move(vecException)); } auto Clibpe::GetSecurity()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto dwSecurityDirOffset = GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_SECURITY); const auto dwSecurityDirSize = GetDirEntrySize(IMAGE_DIRECTORY_ENTRY_SECURITY); if (dwSecurityDirOffset == 0 || dwSecurityDirSize == 0) return std::nullopt; //Checks for bogus file offsets that can cause DWORD_PTR overflow. if (ut::IsSumOverflow(static_cast(dwSecurityDirOffset), GetBaseAddr())) return std::nullopt; auto dwSecurityDirStartVA = GetBaseAddr() + static_cast(dwSecurityDirOffset); if (ut::IsSumOverflow(dwSecurityDirStartVA, static_cast(dwSecurityDirSize))) return std::nullopt; const auto dwSecurityDirEndVA = dwSecurityDirStartVA + static_cast(dwSecurityDirSize); if (!IsPtrSafe(dwSecurityDirStartVA) || !IsPtrSafe(dwSecurityDirEndVA, true)) return std::nullopt; PESECURITY_VEC vecSecurity; while (dwSecurityDirStartVA < dwSecurityDirEndVA) { const auto pCertificate = reinterpret_cast(dwSecurityDirStartVA); const auto dwCertSize = pCertificate->dwLength - static_cast(offsetof(PEWIN_CERTIFICATE, bCertificate)); if (!IsPtrSafe(dwSecurityDirStartVA + static_cast(dwCertSize))) break; vecSecurity.emplace_back(PtrToOffset(pCertificate), *pCertificate); //Get next certificate entry, all entries start at 0x8 aligned address. const auto dwRemainder = (8 - (pCertificate->dwLength & 7)) & 7; const auto dwLength = pCertificate->dwLength + dwRemainder; dwSecurityDirStartVA += static_cast(dwLength); if (!IsPtrSafe(dwSecurityDirStartVA)) break; } return vecSecurity.empty() ? std::nullopt : std::optional(std::move(vecSecurity)); } auto Clibpe::GetRelocations()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; auto pBaseRelocDesc = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_BASERELOC))); if (pBaseRelocDesc == nullptr) return std::nullopt; PERELOC_VEC vecRelocs; try { if (!pBaseRelocDesc->SizeOfBlock || !pBaseRelocDesc->VirtualAddress) { vecRelocs.emplace_back(PtrToOffset(pBaseRelocDesc), *pBaseRelocDesc, std::vector { }); } while ((pBaseRelocDesc->SizeOfBlock) && (pBaseRelocDesc->VirtualAddress)) { if (pBaseRelocDesc->SizeOfBlock < sizeof(IMAGE_BASE_RELOCATION)) { vecRelocs.emplace_back(PtrToOffset(pBaseRelocDesc), *pBaseRelocDesc, std::vector{ }); break; } //Amount of Reloc entries. DWORD dwNumRelocEntries = (pBaseRelocDesc->SizeOfBlock - static_cast(sizeof(IMAGE_BASE_RELOCATION))) / static_cast(sizeof(WORD)); auto pwRelocEntry = reinterpret_cast(reinterpret_cast(pBaseRelocDesc) + sizeof(IMAGE_BASE_RELOCATION)); std::vector vecRelocData; for (auto i = 0UL; i < dwNumRelocEntries; ++i, ++pwRelocEntry) { if (!IsPtrSafe(pwRelocEntry)) break; const WORD wRelocType = (*pwRelocEntry & 0xF000) >> 12; //Getting HIGH 4 bits of reloc's entry WORD —> reloc type. vecRelocData.emplace_back(PtrToOffset(pwRelocEntry), wRelocType, static_cast((*pwRelocEntry) & 0x0fff)/*Low 12 bits —> Offset*/); if (wRelocType == IMAGE_REL_BASED_HIGHADJ) { //The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. //The 16-bit field represents the high value of a 32-bit word. //The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. //This means that this base relocation occupies two slots. (MSDN) if (!IsPtrSafe(++pwRelocEntry)) { vecRelocData.clear(); break; } vecRelocData.emplace_back(PtrToOffset(pwRelocEntry), wRelocType, *pwRelocEntry /*The low 16-bit field.*/); --dwNumRelocEntries; //to compensate ++pwRelocEntry. } } vecRelocs.emplace_back(PtrToOffset(pBaseRelocDesc), *pBaseRelocDesc, std::move(vecRelocData)); //Too big (bogus) SizeOfBlock may cause DWORD_PTR overflow. Checking to prevent. if (ut::IsSumOverflow(reinterpret_cast(pBaseRelocDesc), static_cast(pBaseRelocDesc->SizeOfBlock))) break; pBaseRelocDesc = reinterpret_cast(reinterpret_cast(pBaseRelocDesc) + static_cast(pBaseRelocDesc->SizeOfBlock)); if (!IsPtrSafe(pBaseRelocDesc)) break; } return vecRelocs.empty() ? std::nullopt : std::optional(std::move(vecRelocs)); } catch (const std::exception& e) { vecRelocs.clear(); std::cerr << "Exception in GetRelocations(): " << e.what(); } return std::nullopt; } auto Clibpe::GetDebug()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto dwDebugDirRVA = GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_DEBUG); if (!dwDebugDirRVA) return std::nullopt; PIMAGE_DEBUG_DIRECTORY pDebugDir; DWORD dwDebugDirSize; auto pDebugSecHdr = GetSecHdrFromShortName(".debug"); if (pDebugSecHdr && (pDebugSecHdr->VirtualAddress == dwDebugDirRVA)) { pDebugDir = reinterpret_cast(GetBaseAddr() + static_cast(pDebugSecHdr->PointerToRawData)); dwDebugDirSize = GetDirEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG) * static_cast(sizeof(IMAGE_DEBUG_DIRECTORY)); } else { //Looking for the debug directory. if (pDebugSecHdr = GetSecHdrFromRVA(dwDebugDirRVA); pDebugSecHdr == nullptr) return std::nullopt; if (pDebugDir = static_cast(RVAToPtr(dwDebugDirRVA)); pDebugDir == nullptr) return std::nullopt; dwDebugDirSize = GetDirEntrySize(IMAGE_DIRECTORY_ENTRY_DEBUG); } const auto dwDebugEntries = dwDebugDirSize / static_cast(sizeof(IMAGE_DEBUG_DIRECTORY)); if (!dwDebugEntries || ut::IsSumOverflow(reinterpret_cast(pDebugDir), static_cast(dwDebugDirSize)) || !IsPtrSafe(reinterpret_cast(pDebugDir) + static_cast(dwDebugDirSize))) return std::nullopt; PEDEBUG_VEC vecDebug; try { for (auto i = 0UL; i < dwDebugEntries; ++i) { PEDEBUGDBGHDR stDbgHdr; for (auto iterDbgHdr = 0UL; iterDbgHdr < (sizeof(PEDEBUGDBGHDR::dwHdr) / sizeof(DWORD)); ++iterDbgHdr) { stDbgHdr.dwHdr[iterDbgHdr] = GetTData(static_cast(pDebugDir->PointerToRawData) + (sizeof(DWORD) * iterDbgHdr)); } if (pDebugDir->Type == IMAGE_DEBUG_TYPE_CODEVIEW) { DWORD dwOffset = 0; if (stDbgHdr.dwHdr[0] == 0x53445352) { //"RSDS" dwOffset = sizeof(DWORD) * 6; } else if (stDbgHdr.dwHdr[0] == 0x3031424E) { //"NB10" dwOffset = sizeof(DWORD) * 4; } std::string strPDBName; if (dwOffset > 0) { for (auto iterStr = 0UL; iterStr < MAX_PATH; ++iterStr) { const auto byte = GetTData(pDebugDir->PointerToRawData + dwOffset + iterStr); if (byte == 0) //End of string. break; strPDBName += byte; } } stDbgHdr.strPDBName = std::move(strPDBName); } vecDebug.emplace_back(PtrToOffset(pDebugDir), *pDebugDir, stDbgHdr); if (!IsPtrSafe(++pDebugDir)) break; } return vecDebug.empty() ? std::nullopt : std::optional(std::move(vecDebug)); } catch (const std::exception& e) { vecDebug.clear(); std::cerr << "Exception in GetDebug(): " << e.what(); } return std::nullopt; } auto Clibpe::GetTLS()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto dwTLSDirRVA = GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_TLS); if (!dwTLSDirRVA) return std::nullopt; std::vector vecTLSCallbacks; try { ULONGLONG ullAddressOfCallBacks; PETLS::UNPETLS varTLSDir; PDWORD pdwTLSPtr; if (m_ePEType == EFileType::PE32) { const auto pTLSDir32 = static_cast(RVAToPtr(dwTLSDirRVA)); if (pTLSDir32 == nullptr) return std::nullopt; varTLSDir.stTLSDir32 = *pTLSDir32; pdwTLSPtr = reinterpret_cast(pTLSDir32); ullAddressOfCallBacks = pTLSDir32->AddressOfCallBacks; } else if (m_ePEType == EFileType::PE64) { const auto pTLSDir64 = static_cast(RVAToPtr(dwTLSDirRVA)); if (pTLSDir64 == nullptr) return std::nullopt; varTLSDir.stTLSDir64 = *pTLSDir64; pdwTLSPtr = reinterpret_cast(pTLSDir64); ullAddressOfCallBacks = pTLSDir64->AddressOfCallBacks; } else return std::nullopt; if (auto pTLSCallbacks = static_cast(RVAToPtr(ullAddressOfCallBacks - GetImageBase())); pTLSCallbacks != nullptr) { while (*pTLSCallbacks) { vecTLSCallbacks.push_back(*pTLSCallbacks); if (!IsPtrSafe(++pTLSCallbacks)) { vecTLSCallbacks.clear(); break; } } } return std::make_optional(PtrToOffset(pdwTLSPtr), varTLSDir, std::move(vecTLSCallbacks)); } catch (const std::exception& e) { vecTLSCallbacks.clear(); std::cerr << "Exception in GetTLS(): " << e.what(); } return std::nullopt; } auto Clibpe::GetLoadConfig()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; switch (m_ePEType) { case EFileType::PE32: { const auto pLCD32 = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG))); if (!pLCD32 || !IsPtrSafe(reinterpret_cast(pLCD32) + sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32))) return std::nullopt; return { PELOADCONFIG { .dwOffset { PtrToOffset(pLCD32) }, .unLCD { .stLCD32 { *pLCD32 } } } }; } case EFileType::PE64: { const auto pLCD64 = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG))); if (!pLCD64 || !IsPtrSafe(reinterpret_cast(pLCD64) + sizeof(PIMAGE_LOAD_CONFIG_DIRECTORY64))) return std::nullopt; return { PELOADCONFIG { .dwOffset { PtrToOffset(pLCD64) }, .unLCD { .stLCD64 { *pLCD64 } } } }; } default: return std::nullopt; } } auto Clibpe::GetBoundImport()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; auto pBoundImpDesc = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT))); if (pBoundImpDesc == nullptr) return std::nullopt; PEBOUNDIMPORT_VEC vecBoundImp; while (pBoundImpDesc->TimeDateStamp != 0) { std::vector vecBoundForwarders; auto pBoundImpForwarder = reinterpret_cast(pBoundImpDesc + 1); if (!IsPtrSafe(pBoundImpForwarder)) break; for (auto i = 0UL; i < pBoundImpDesc->NumberOfModuleForwarderRefs; ++i) { std::string strForwarderModuleName { }; if (const auto szName = reinterpret_cast(reinterpret_cast(pBoundImpDesc) + pBoundImpForwarder->OffsetModuleName); IsPtrSafe(szName)) { if (szName && (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strForwarderModuleName = szName; } } vecBoundForwarders.emplace_back(PtrToOffset(pBoundImpForwarder), *pBoundImpForwarder, std::move(strForwarderModuleName)); if (!IsPtrSafe(++pBoundImpForwarder)) break; pBoundImpDesc = reinterpret_cast(reinterpret_cast(pBoundImpDesc) + sizeof(IMAGE_BOUND_FORWARDER_REF)); if (!IsPtrSafe(pBoundImpDesc)) break; } std::string strModuleName; if (const auto szName = reinterpret_cast(reinterpret_cast(pBoundImpDesc) + pBoundImpDesc->OffsetModuleName); IsPtrSafe(szName)) { if (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER) { strModuleName = szName; } } vecBoundImp.emplace_back(PtrToOffset(pBoundImpDesc), *pBoundImpDesc, std::move(strModuleName), std::move(vecBoundForwarders)); if (!IsPtrSafe(++pBoundImpDesc)) break; } return vecBoundImp.empty() ? std::nullopt : std::optional(std::move(vecBoundImp)); } auto Clibpe::GetDelayImport()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; auto pDelayImpDescr = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT))); if (pDelayImpDescr == nullptr) return std::nullopt; PEDELAYIMPORT_VEC vecDelayImp; if (m_ePEType == EFileType::PE32) { while (pDelayImpDescr->DllNameRVA != 0) { auto pThunk32Name = reinterpret_cast(static_cast(pDelayImpDescr->ImportNameTableRVA)); if (pThunk32Name == nullptr) { if (!IsPtrSafe(++pDelayImpDescr)) break; } else { std::vector vecFunc; pThunk32Name = static_cast(RVAToPtr(reinterpret_cast(pThunk32Name))); auto pThunk32IAT = static_cast(RVAToPtr(pDelayImpDescr->ImportAddressTableRVA)); auto pThunk32BoundIAT = static_cast(RVAToPtr(pDelayImpDescr->BoundImportAddressTableRVA)); auto pThunk32UnloadInfoTable = static_cast(RVAToPtr(pDelayImpDescr->UnloadInformationTableRVA)); if (pThunk32Name == nullptr) break; while (pThunk32Name->u1.AddressOfData) { PEDELAYIMPORTFUNC::UNPEDELAYIMPORTTHUNK unDelayImpThunk32 { }; unDelayImpThunk32.st32.stImportAddressTable = *pThunk32Name; unDelayImpThunk32.st32.stImportNameTable = pThunk32IAT ? *pThunk32IAT : IMAGE_THUNK_DATA32 { }; unDelayImpThunk32.st32.stBoundImportAddressTable = pThunk32BoundIAT ? *pThunk32BoundIAT : IMAGE_THUNK_DATA32 { }; unDelayImpThunk32.st32.stUnloadInformationTable = pThunk32UnloadInfoTable ? *pThunk32UnloadInfoTable : IMAGE_THUNK_DATA32 { }; std::string strFuncName; IMAGE_IMPORT_BY_NAME stImpByName { }; if (!(pThunk32Name->u1.Ordinal & IMAGE_ORDINAL_FLAG32)) { if (const auto pName = static_cast(RVAToPtr(pThunk32Name->u1.AddressOfData)); pName && (::StringCchLengthA(pName->Name, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { stImpByName = *pName; strFuncName = pName->Name; } } vecFunc.emplace_back(unDelayImpThunk32, stImpByName, std::move(strFuncName)); if (!IsPtrSafe(++pThunk32Name)) break; if (pThunk32IAT) { if (!IsPtrSafe(++pThunk32IAT)) break; } if (pThunk32BoundIAT) { if (!IsPtrSafe(++pThunk32BoundIAT)) break; } if (pThunk32UnloadInfoTable) { if (!IsPtrSafe(++pThunk32UnloadInfoTable)) break; } } std::string strDllName; if (const auto szName = static_cast(RVAToPtr(pDelayImpDescr->DllNameRVA)); szName != nullptr && (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strDllName = szName; } vecDelayImp.emplace_back(PtrToOffset(pDelayImpDescr), *pDelayImpDescr, std::move(strDllName), std::move(vecFunc)); if (!IsPtrSafe(++pDelayImpDescr)) break; } } } else if (m_ePEType == EFileType::PE64) { while (pDelayImpDescr->DllNameRVA != 0) { auto pThunk64Name = reinterpret_cast(static_cast(pDelayImpDescr->ImportNameTableRVA)); if (pThunk64Name == nullptr) { if (!IsPtrSafe(++pDelayImpDescr)) break; } else { std::vector vecFunc; pThunk64Name = static_cast(RVAToPtr(reinterpret_cast(pThunk64Name))); auto pThunk64IAT = static_cast(RVAToPtr(pDelayImpDescr->ImportAddressTableRVA)); auto pThunk64BoundIAT = static_cast(RVAToPtr(pDelayImpDescr->BoundImportAddressTableRVA)); auto pThunk64UnloadInfoTable = static_cast(RVAToPtr(pDelayImpDescr->UnloadInformationTableRVA)); if (pThunk64Name == nullptr) break; while (pThunk64Name->u1.AddressOfData) { PEDELAYIMPORTFUNC::UNPEDELAYIMPORTTHUNK unDelayImpThunk64 { }; unDelayImpThunk64.st64.stImportAddressTable = *pThunk64Name; unDelayImpThunk64.st64.stImportNameTable = pThunk64IAT ? *pThunk64IAT : IMAGE_THUNK_DATA64 { }; unDelayImpThunk64.st64.stBoundImportAddressTable = pThunk64BoundIAT ? *pThunk64BoundIAT : IMAGE_THUNK_DATA64 { }; unDelayImpThunk64.st64.stUnloadInformationTable = pThunk64UnloadInfoTable ? *pThunk64UnloadInfoTable : IMAGE_THUNK_DATA64 { }; std::string strFuncName; IMAGE_IMPORT_BY_NAME stImpByName { }; if (!(pThunk64Name->u1.Ordinal & IMAGE_ORDINAL_FLAG64)) { if (const auto pName = static_cast(RVAToPtr(pThunk64Name->u1.AddressOfData)); pName && (::StringCchLengthA(pName->Name, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { stImpByName = *pName; strFuncName = pName->Name; } } vecFunc.emplace_back(unDelayImpThunk64, stImpByName, std::move(strFuncName)); if (!IsPtrSafe(++pThunk64Name)) break; if (pThunk64IAT) { if (!IsPtrSafe(++pThunk64IAT)) break; } if (pThunk64BoundIAT) { if (!IsPtrSafe(++pThunk64BoundIAT)) break; } if (pThunk64UnloadInfoTable) { if (!IsPtrSafe(++pThunk64UnloadInfoTable)) break; } } std::string strDllName; if (const auto szName = static_cast(RVAToPtr(pDelayImpDescr->DllNameRVA)); szName != nullptr && (::StringCchLengthA(szName, MAX_PATH, nullptr) != STRSAFE_E_INVALID_PARAMETER)) { strDllName = szName; } vecDelayImp.emplace_back(PtrToOffset(pDelayImpDescr), *pDelayImpDescr, std::move(strDllName), std::move(vecFunc)); if (!IsPtrSafe(++pDelayImpDescr)) break; } } } return vecDelayImp.empty() ? std::nullopt : std::optional(std::move(vecDelayImp)); } auto Clibpe::GetCOMDescriptor()const->std::optional { assert(m_fOpened); if (!m_fOpened) return std::nullopt; const auto pCOR20Hdr = static_cast(RVAToPtr(GetDirEntryRVA(IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR))); if (pCOR20Hdr == nullptr) return std::nullopt; return std::make_optional(PtrToOffset(pCOR20Hdr), *pCOR20Hdr); } //Private methods. auto Clibpe::GetFileSize()const->ULONGLONG { return m_spnData.size(); } auto Clibpe::GetBaseAddr()const->DWORD_PTR { return reinterpret_cast(m_spnData.data()); } auto Clibpe::GetDosPtr()const->const IMAGE_DOS_HEADER* { return reinterpret_cast(m_spnData.data()); } auto Clibpe::GetDirEntryRVA(DWORD dwEntry)const->DWORD { if (!m_fHasNTHdr) return { }; switch (m_ePEType) { case EFileType::PE32: return m_pNTHeader32->OptionalHeader.DataDirectory[dwEntry].VirtualAddress; case EFileType::PE64: return m_pNTHeader64->OptionalHeader.DataDirectory[dwEntry].VirtualAddress; default: return { }; } } auto Clibpe::GetDirEntrySize(DWORD dwEntry)const->DWORD { if (!m_fHasNTHdr) return { }; switch (m_ePEType) { case EFileType::PE32: return m_pNTHeader32->OptionalHeader.DataDirectory[dwEntry].Size; case EFileType::PE64: return m_pNTHeader64->OptionalHeader.DataDirectory[dwEntry].Size; default: return { }; } } auto Clibpe::GetImageBase()const->ULONGLONG { switch (m_ePEType) { case EFileType::PE32: return m_pNTHeader32->OptionalHeader.ImageBase; case EFileType::PE64: return m_pNTHeader64->OptionalHeader.ImageBase; default: return { }; } } auto Clibpe::GetSecHdrFromShortName(LPCSTR lpszName)const->PIMAGE_SECTION_HEADER { if (!m_fHasNTHdr) return nullptr; PIMAGE_SECTION_HEADER pSecHdr; WORD wNumberOfSections; switch (m_ePEType) { case EFileType::PE32: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader32); wNumberOfSections = m_pNTHeader32->FileHeader.NumberOfSections; break; case EFileType::PE64: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader64); wNumberOfSections = m_pNTHeader64->FileHeader.NumberOfSections; break; default: return nullptr; } for (auto itSec = 0UL; itSec < wNumberOfSections; ++itSec, ++pSecHdr) { if (!IsPtrSafe(reinterpret_cast(pSecHdr) + sizeof(IMAGE_SECTION_HEADER))) break; if (::strncmp(reinterpret_cast(pSecHdr->Name), lpszName, IMAGE_SIZEOF_SHORT_NAME) == 0) return pSecHdr; } return nullptr; } auto Clibpe::GetSecHdrFromRVA(ULONGLONG ullRVA)const->PIMAGE_SECTION_HEADER { if (!m_fHasNTHdr) return nullptr; PIMAGE_SECTION_HEADER pSecHdr; WORD wNumberOfSections; switch (m_ePEType) { case EFileType::PE32: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader32); wNumberOfSections = m_pNTHeader32->FileHeader.NumberOfSections; break; case EFileType::PE64: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader64); wNumberOfSections = m_pNTHeader64->FileHeader.NumberOfSections; break; default: return nullptr; } for (auto itSec = 0UL; itSec < wNumberOfSections; ++itSec, ++pSecHdr) { if (!IsPtrSafe(reinterpret_cast(pSecHdr) + sizeof(IMAGE_SECTION_HEADER))) return nullptr; //Is RVA within this section? if ((ullRVA >= pSecHdr->VirtualAddress) && (ullRVA < (pSecHdr->VirtualAddress + pSecHdr->Misc.VirtualSize))) { return pSecHdr; } } return nullptr; } auto Clibpe::GetSecIdxFromRVA(ULONGLONG ullRVA)const->std::optional { if (!m_fHasNTHdr) return std::nullopt; PIMAGE_SECTION_HEADER pSecHdr; WORD wNumberOfSections; switch (m_ePEType) { case EFileType::PE32: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader32); wNumberOfSections = m_pNTHeader32->FileHeader.NumberOfSections; break; case EFileType::PE64: pSecHdr = IMAGE_FIRST_SECTION(m_pNTHeader64); wNumberOfSections = m_pNTHeader64->FileHeader.NumberOfSections; break; default: return std::nullopt; } for (auto itSec = 0UL; itSec < wNumberOfSections; ++itSec, ++pSecHdr) { if (!IsPtrSafe(reinterpret_cast(pSecHdr) + sizeof(IMAGE_SECTION_HEADER))) { return std::nullopt; } //Is RVA within this section? if ((ullRVA >= pSecHdr->VirtualAddress) && (ullRVA < (pSecHdr->VirtualAddress + pSecHdr->Misc.VirtualSize))) { return itSec; //Section sequential index in a PE file. } } return std::nullopt; } template auto Clibpe::GetTData(ULONGLONG ullOffset)const->T { if (ullOffset > (GetFileSize() - sizeof(T))) //Check for file size exceeding. return { }; return *reinterpret_cast(GetBaseAddr() + ullOffset); } template auto Clibpe::IsPtrSafe(const T tAddr, bool fCanReferenceBoundary)const->bool { /************************************************************************************************** * This func checks given pointer for nullptr and, more important, whether it fits allowed bounds. * * In PE headers there are plenty of places where wrong (bogus) values for pointers might reside, * * causing many runtime «fun» if trying to dereference them. * * Second arg (fCanReferenceBoundary) shows if pointer can point to the very end of a file, it's * * valid for some PE structures. Template is used just for convenience, sometimes there is a need * * to check pure address DWORD_PTR instead of a pointer. * **************************************************************************************************/ DWORD_PTR dwAddr; if constexpr (!std::is_same_v) { dwAddr = reinterpret_cast(tAddr); } else { dwAddr = tAddr; } const auto ullBase = GetBaseAddr(); const auto ullMaxAddr = ullBase + GetFileSize(); return ((dwAddr == 0) || (dwAddr < ullBase)) ? false : (fCanReferenceBoundary ? dwAddr <= ullMaxAddr : dwAddr < ullMaxAddr); } auto Clibpe::PtrToOffset(LPCVOID lp)const->DWORD { if (lp == nullptr) return 0; return static_cast(reinterpret_cast(lp) - GetBaseAddr()); } auto Clibpe::RVAToPtr(ULONGLONG ullRVA)const->LPVOID { const auto pSecHdr = GetSecHdrFromRVA(ullRVA); if (pSecHdr == nullptr) return nullptr; const auto ptr = reinterpret_cast(GetBaseAddr() + ullRVA - static_cast(pSecHdr->VirtualAddress) + static_cast(pSecHdr->PointerToRawData)); return IsPtrSafe(ptr, true) ? ptr : nullptr; } bool Clibpe::ParseDOSHeader() { //If a file has at least MSDOS header signature then we can assume //that this is a minimally correct PE file, and process further. return GetDosPtr()->e_magic == IMAGE_DOS_SIGNATURE; } bool Clibpe::ParseNTFileOptHeader() { const auto pNTHeader = reinterpret_cast(GetBaseAddr() + static_cast(GetDosPtr()->e_lfanew)); if (!IsPtrSafe(reinterpret_cast(pNTHeader) + sizeof(IMAGE_NT_HEADERS32))) return false; if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) return false; switch (pNTHeader->OptionalHeader.Magic) { case IMAGE_NT_OPTIONAL_HDR32_MAGIC: m_ePEType = EFileType::PE32; m_pNTHeader32 = pNTHeader; break; case IMAGE_NT_OPTIONAL_HDR64_MAGIC: m_ePEType = EFileType::PE64; m_pNTHeader64 = reinterpret_cast(pNTHeader); break; //case IMAGE_ROM_OPTIONAL_HDR_MAGIC: //Not implemented. default: return false; } m_fHasNTHdr = true; return true; } } ================================================ FILE: test/libpe.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27428.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpe", "libpe.vcxproj", "{3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Debug|x64.ActiveCfg = Debug|x64 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Debug|x64.Build.0 = Debug|x64 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Debug|x86.ActiveCfg = Debug|Win32 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Debug|x86.Build.0 = Debug|Win32 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Release|x64.ActiveCfg = Release|x64 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Release|x64.Build.0 = Release|x64 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Release|x86.ActiveCfg = Release|Win32 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {875A30FD-5011-4251-862B-A11EE1A8B119} EndGlobalSection EndGlobal ================================================ FILE: test/libpe.vcxproj ================================================ Debug Win32 Release Win32 Debug x64 Release x64 15.0 {3C2559D5-7A9A-4E39-A2C5-C4D4806376EA} Win32Proj libpe 10.0 Application true v143 Unicode Application false v143 true Unicode Application true v143 Unicode Application false v143 true Unicode $(SolutionDir)bin\obj\$(Configuration)\$(Platform)\ $(ProjectName)d $(SolutionDir)bin\ false $(SolutionDir)bin\ $(SolutionDir)bin\obj\$(Configuration)\$(Platform)\ $(ProjectName)64d false false $(SolutionDir)bin\obj\$(Configuration)\$(Platform)\ $(VC_IncludePath);$(WindowsSDK_IncludePath); $(SolutionDir)bin\ false false $(SolutionDir)bin\ $(SolutionDir)bin\obj\$(Configuration)\$(Platform)\ $(ProjectName)64 false NotUsing Level4 Disabled true WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true stdcpp20 ProgramDatabase Console true /NOCOFFGRPINFO /pdbaltpath:%_PDB% %(AdditionalOptions) NotUsing Level4 Disabled true _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true stdcpp20 ProgramDatabase Console true /NOCOFFGRPINFO /pdbaltpath:%_PDB% %(AdditionalOptions) NotUsing Level4 MaxSpeed true true WIN32;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) Speed %(AdditionalOptions) stdcpp20 true true Console true true false UseLinkTimeCodeGeneration /NOCOFFGRPINFO /pdbaltpath:%_PDB% %(AdditionalOptions) include NotUsing Level4 MaxSpeed true true false NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true stdcpp20 Speed true %(AdditionalOptions) Console true true false /NOCOFFGRPINFO /pdbaltpath:%_PDB% %(AdditionalOptions) UseLinkTimeCodeGeneration ================================================ FILE: test/test.cpp ================================================ #include #include #include import libpe; using namespace libpe; int wmain(int argc, wchar_t* argv[]) { std::wstring_view wsvFile { L"C:\\Windows\\notepad.exe" }; //Default file. if (argc > 1) { wsvFile = argv[1]; } libpe::Clibpe pe; if (pe.OpenFile(wsvFile.data()) != PEOK) { std::cout << "Open file failed."; return -1; } constexpr auto svFormat = "{:*^{}}\r\n"; constexpr auto wsvFormat = L"{:*^{}}\r\n"; constexpr auto uiWidth = 100UL; std::string strRich = std::format(svFormat, "Rich", uiWidth); if (const auto peRich = pe.GetRichHeader(); peRich) { for (const auto& ref : *peRich) { strRich += std::format("ID: {:04X}, Ver: {:05}, Count: {}\r\n", ref.wId, ref.wVersion, ref.dwCount); } } else { strRich += "No Rich header.\r\n"; } std::cout << strRich << "\r\n"; std::wstring wstrResources = std::format(wsvFormat, L"Resources", uiWidth); if (const auto peResRoot = pe.GetResources(); peResRoot) { 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) { wstrResources += std::format(L"Entry: {} [Name: {}]\r\n", ilvlRoot, iterRoot.wstrResName); } else { if (const auto iter = MapResID.find(pResDirEntry->Id); iter != MapResID.end()) { wstrResources += std::format(L"Entry: {} [Id: {}, {}]\r\n", ilvlRoot, pResDirEntry->Id, iter->second); } else { wstrResources += 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) { wstrResources += std::format(L" Entry: {}, Name: {}\r\n", ilvl2, iterLvL2.wstrResName); } else { wstrResources += 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) { wstrResources += std::format(L" Entry: {}, Name: {}\r\n", ilvl3, iterLvL3.wstrResName); } else { wstrResources += std::format(L" Entry: {}, lang: {}\r\n", ilvl3, pResDirEntry->Id); } ++ilvl3; } } ++ilvl2; } } ++ilvlRoot; } } else { wstrResources += L"No Resources found.\r\n"; } std::wcout << wstrResources << L"\r\n"; std::string strImports = std::format(svFormat, "Imports", uiWidth); if (const auto peImp = pe.GetImport(); peImp) { for (const auto& itModule : *peImp) { //Cycle through all imports. strImports += std::format("{}, Funcs: {}\r\n", itModule.strModuleName, itModule.vecImportFunc.size()); } } else { strImports += "No Imports found.\r\n"; } std::cout << strImports << "\r\n"; std::string strSecurity = std::format(svFormat, "Security Directory", uiWidth); if (const auto peSecur = pe.GetSecurity(); peSecur) { for (const auto& itSecur : *peSecur) { const auto& refWinSert = itSecur.stWinSert; strSecurity += std::format("Offset: {}, Length: {}, Revision: {}, Cert Type: {}\r\n", itSecur.dwOffset, refWinSert.dwLength, refWinSert.wRevision, refWinSert.wCertificateType); } } else { strSecurity += "No Security directory found.\r\n"; } std::cout << strSecurity << "\r\n"; return 0; }