Skip to content

Instantly share code, notes, and snippets.

@caiorss
Last active December 12, 2023 05:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caiorss/f5edeaa4d929f5acf258d71caf6915cd to your computer and use it in GitHub Desktop.
Save caiorss/f5edeaa4d929f5acf258d71caf6915cd to your computer and use it in GitHub Desktop.
Parse metadata from Windows PE files (PE32, PE64) executables and dll's
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)
# 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
#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;
}
#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