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