Last active
December 12, 2023 05:39
-
-
Save caiorss/f5edeaa4d929f5acf258d71caf6915cd to your computer and use it in GitHub Desktop.
Parse metadata from Windows PE files (PE32, PE64) executables and dll's
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
cmake_minimum_required(VERSION 3.9) | |
project(pe-metadata) | |
#========== Global Configurations =============# | |
#----------------------------------------------# | |
set(CMAKE_CXX_STANDARD 17) | |
set(CMAKE_VERBOSE_MAKEFILE ON) | |
add_executable(pe-info pe-info.cpp pe_data.hpp) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# GNU Make file script for easier compilation from | |
# command line | |
debug: | |
cmake -B_build -H. -DCMAKE_BUILD_TYPE=Debug | |
cmake --build _build --target | |
release: | |
cmake -B_build -H. -DCMAKE_BUILD_TYPE=Release | |
cmake --build _build --target | |
clean: | |
rm -rfv ./_build | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <string> | |
#include <cstdint> | |
#include <fstream> | |
#include <cassert> | |
#include <optional> // C++17 | |
#include <algorithm> | |
#include "pe_data.hpp" | |
/** Read binary data from a input stream at a given offset. | |
* | |
* @param tref Reference to type that will be read from input stream | |
* @param ifs Input stream | |
* @param offset Offset from the beggining of the input stream or file. | |
*/ | |
template<typename T> | |
void read_at( T& tref | |
, std::istream& ifs | |
, std::optional<size_t> offset = {}) | |
{ | |
if(offset) ifs.seekg(offset.value()); | |
ifs.read(reinterpret_cast<char*>(&tref), sizeof (T)); | |
} | |
int main(int argc, char** argv) | |
{ | |
if(argc < 2) | |
{ | |
std::cout << "Usage: $ pe-info <FILE> " << "\n"; | |
return EXIT_FAILURE; | |
} | |
auto file_path = std::string{ argv[1] } ; | |
std::cout << " File: " << file_path << "\n"; | |
auto ifs = std::ifstream (file_path, std::ios::in | std::ios::binary ); | |
assert( ifs.good() ); | |
IMAGE_DOS_HEADER dos_header; | |
// Position cursor at beggining of file | |
read_at(dos_header, ifs, 0x00 ); | |
assert( dos_header.e_magic == 0x5A4D ); | |
// Offset of PE-Header from the beggining of the file | |
auto e_lfanew = dos_header.e_lfanew; | |
char magic[3] = { | |
dos_header.e_magic & 0xFF // Fist byte | |
, (dos_header.e_magic >> 8) & 0xff // Second byte | |
, 0x00 // Null character | |
}; | |
std::printf("\n ------ DOS HEADER ---------------\n"); | |
std::printf(" [DOS_HEADER] MAGIC NUMBER = 0x%X (hex) \n", dos_header.e_magic); | |
std::printf(" [DOS_HEADER] MAGIC NUMBER = %s (str) \n", magic); | |
std::printf(" [DOS_HEADER] e_lfanew = 0x%X \n", e_lfanew); | |
//--------------------------------------------------// | |
// PE HEADER // | |
//--------------------------------------------------// | |
PE_HEADER pe_header; | |
read_at(pe_header, ifs, dos_header.e_lfanew); | |
//The signature should always have this value (0x4550) | |
assert( pe_header.Signature == 0x4550 ); | |
std::printf("\n -------- PE - Header ----------------------\n\n"); | |
std::printf(" Note: if machine is 0x8664 => the processor is AMD-Intel x86-64 \n"); | |
std::printf("\n"); | |
std::printf(" Signature = 0x%X \n", pe_header.Signature); | |
std::printf(" Machine = 0x%X \n", pe_header.Machine); | |
std::printf(" Number of sections = %d \n", pe_header.NumberOfSections); | |
//--------------------------------------------------// | |
// PE OPTIONAL HEADER // | |
//--------------------------------------------------// | |
PE_OPTIONAL_HEADER64 pe_opt; | |
read_at(pe_opt, ifs); | |
std::string subsystem = [&pe_opt]() { | |
if(pe_opt.Subsystem == 1) return "No subsystem required (device drivers)"; | |
if(pe_opt.Subsystem == 2) return "gui / graphical"; | |
if(pe_opt.Subsystem == 3) return "console"; | |
if(pe_opt.Subsystem == 9) return "Windows CE system"; | |
if(pe_opt.Subsystem == 10) return "EFI - Extensible Firmware Interfgace"; | |
if(pe_opt.Subsystem == 16) return "Windows Boot"; | |
return "Unknown"; | |
}(); | |
std::printf("\n -------- PE Optional Header ----------------------\n\n"); | |
std::printf(" Magic = 0x%X \n", pe_opt.Magic); | |
std::printf(" Subsystem = 0x%X \n", pe_opt.Subsystem); | |
std::printf(" Subsystem = %s \n", subsystem.c_str()); | |
std::printf(" Address of Entrypoint = 0x%X \n", pe_opt.AddressOfEntryPoint); | |
std::printf(" Major link version = 0x%X \n", pe_opt.MajorLinkerVersion); | |
std::printf(" Size of code = 0x%X \n", pe_opt.SizeOfCode); | |
std::printf(" Major Operating System Version = 0x%X \n", pe_opt.MajorOperatingSystemVersion); | |
std::printf(" Minor Operating System Version = 0x%X \n", pe_opt.MinorOperatingSystemVersion); | |
//--------------------------------------------------// | |
// DATA SECTIONS // | |
//--------------------------------------------------// | |
std::printf("\n -------- PE Sections ---------------\n\n"); | |
PE_SECTION_HEADER sec; | |
for(int i = 0; i < pe_header.NumberOfSections; i++) | |
{ | |
read_at(sec, ifs); | |
std::printf(" [%d] section name = %s \n", i, sec.Name); | |
std::printf(" => Virtual Address = 0x%X \n", sec.VirtualAddress); | |
std::printf(" => Raw Address = 0x%X \n", sec.PointerToRawData); | |
std::printf("\n"); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef _PE_DATA_ | |
#define _PE_DATA_ | |
//#include <windows.h> | |
//#include <winnt.h> | |
constexpr size_t IMAGE_SIZEOF_SHORT_NAME = 8; | |
constexpr size_t IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16; | |
// using LONG = long; | |
using LONG = std::int32_t; | |
using WORD = std::uint16_t; // unsigned short; | |
using DWORD = std::uint32_t; // unsigned long; | |
using BYTE = std::uint8_t; //unsigned char; | |
using ULONGLONG = std::uint64_t; // unsigned long long | |
// Source: <winttt.h> => Original: typedef struct _IMAGE_DOS_HEADER | |
struct IMAGE_DOS_HEADER { | |
WORD e_magic; | |
WORD e_cblp; | |
WORD e_cp; | |
WORD e_crlc; | |
WORD e_cparhdr; | |
WORD e_minalloc; | |
WORD e_maxalloc; | |
WORD e_ss; | |
WORD e_sp; | |
WORD e_csum; | |
WORD e_ip; | |
WORD e_cs; | |
WORD e_lfarlc; | |
WORD e_ovno; | |
WORD e_res[4]; | |
WORD e_oemid; | |
WORD e_oeminfo; | |
WORD e_res2[10]; | |
// Offset to the PE header from the beginning of the file. | |
LONG e_lfanew; | |
}; | |
struct PE_HEADER | |
{ | |
DWORD Signature; | |
WORD Machine; | |
WORD NumberOfSections; | |
DWORD TimeDateStamp; | |
DWORD PointerToSymbolTable; | |
DWORD NumberOfSymbols; | |
WORD SizeOfOptionalHeader; | |
WORD Characteristics; | |
}; | |
struct PE_IMAGE_DATA_DIRECTORY { | |
DWORD VirtualAddress; | |
DWORD Size; | |
}; | |
struct PE_OPTIONAL_HEADER64 { | |
WORD Magic; | |
BYTE MajorLinkerVersion; | |
BYTE MinorLinkerVersion; | |
DWORD SizeOfCode; | |
DWORD SizeOfInitializedData; | |
DWORD SizeOfUninitializedData; | |
DWORD AddressOfEntryPoint; | |
DWORD BaseOfCode; | |
ULONGLONG ImageBase; | |
DWORD SectionAlignment; | |
DWORD FileAlignment; | |
WORD MajorOperatingSystemVersion; | |
WORD MinorOperatingSystemVersion; | |
WORD MajorImageVersion; | |
WORD MinorImageVersion; | |
WORD MajorSubsystemVersion; | |
WORD MinorSubsystemVersion; | |
DWORD Win32VersionValue; | |
DWORD SizeOfImage; | |
DWORD SizeOfHeaders; | |
DWORD CheckSum; | |
WORD Subsystem; | |
WORD DllCharacteristics; | |
ULONGLONG SizeOfStackReserve; | |
ULONGLONG SizeOfStackCommit; | |
ULONGLONG SizeOfHeapReserve; | |
ULONGLONG SizeOfHeapCommit; | |
DWORD LoaderFlags; | |
DWORD NumberOfRvaAndSizes; | |
PE_IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; | |
}; | |
// source: winnt.h header | |
struct PE_SECTION_HEADER { | |
BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; | |
union { | |
DWORD PhysicalAddress; | |
DWORD VirtualSize; | |
} Misc; | |
DWORD VirtualAddress; | |
DWORD SizeOfRawData; | |
DWORD PointerToRawData; | |
DWORD PointerToRelocations; | |
DWORD PointerToLinenumbers; | |
WORD NumberOfRelocations; | |
WORD NumberOfLinenumbers; | |
DWORD Characteristics; | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment