I figured i'd give a little bit back (and hopefully get a bit of feedback from people who know what they're doing). I've created a pattern searcher for my bot that seems to work pretty well for me although I recognize that there are a few optimizations that could be made.
The reason I made this was because I wanted to implement a search that would directly grab values out for me and would eventually have the flexibility to search for relative addresses. I recognize that it doesn't support move semantics (which is probably okay because of return value optimization in most cases). Also, I want to directly read the PE file rather than dumping it to a vector, this was just a bit of a hack to pull it all in. I should also note that i've really only worked with 64 bit executables at this point, there's some intptr_t junk in there that won't work on 32 bit executables, but the code should be good otherwise.
Patterns are created by the factory functions that can be selected by searching for a string or a template version that supports directly pulling out values from the result string. It's called by doing something like:
Code:
CreateBytePattern<uint32_t,uint8_t>("FE 43 CD ?? ?? ?? ?? [0] FE [1]");
Notice the sentinel values in there, [0] and [1]. The parser deduces the types of these and stuffs in the bytes. The consumers of the pattern can query this and stuff the found values in. I did it this way because i was sick of counting (miscounting) the byte offsets and losing my position among the ?? bytes.
That said, here's the code, comments welcome.
Pattern.hpp
Code:
#pragma once
#include <string>
#include <vector>
#include <cstdint>
#include <memory>
#include <sstream>
namespace ev
{
class PatternResultElement
{
public:
PatternResultElement(std::size_t size)
{
rawData.resize(size);
}
//position that it was found in
void SetAddress(intptr_t a) { Address = a; }
intptr_t GetAddress() const { return Address; }
//value + location + sizeof data
void SetRelativeAddress(intptr_t a) { RelativeValue = a; }
intptr_t GetRelativeAddress() const { return RelativeValue; }
//the actual found string
template <typename T>
void SetValue(const T& value)
{
if (sizeof(T) == rawData.size())
{
memcpy(&rawData[0], &value, rawData.size());
}
}
void SetValue(char *value, std::size_t size)
{
if (size == rawData.size())
{
memcpy(&rawData[0], value, rawData.size());
}
}
template <typename T>
T GetValue() const
{
T retval = 0;
if (sizeof(T) == rawData.size())
{
memcpy(&retval, &rawData[0], rawData.size());
return retval;
}
else
{
std::cerr << sizeof(T) << "!=" << rawData.size() << std::endl;
return retval;
}
}
private:
intptr_t Address;
intptr_t RelativeValue;
std::vector<char> rawData;
};
struct PatternResult
{
bool PatternFound;
intptr_t PatternFoundLocation;
std::vector<PatternResultElement> PatternElements;
};
struct PatternMatchElements
{
PatternMatchElements(std::size_t size){ PatternElementSize = size; }
std::size_t PatternElementSize;
std::size_t PatternElementLocation;
};
struct PatternElement
{
PatternElement(char v, char m) : Value(v), Mask(m) {}
bool Matches(char q) const
{
return (Value & Mask) == (q & Mask);
}
char Value;
char Mask;
};
class Pattern
{
private:
std::vector<PatternElement> m_Pattern;
std::vector<PatternMatchElements> m_Elements;
public:
Pattern(){};
char GetPatternElement(std::size_t idx) const { return m_Pattern[idx].Value; }
char GetMaskElement(std::size_t idx) const { return m_Pattern[idx].Mask; }
std::size_t GetPatternLength() const { return m_Pattern.size(); }
std::size_t GetNumMatchElements() const { return m_Elements.size(); }
PatternMatchElements GetMatchElement(std::size_t idx) const { return m_Elements[idx]; }
std::vector<PatternElement>::iterator begin(){ return m_Pattern.begin(); }
std::vector<PatternElement>::iterator end(){ return m_Pattern.end(); }
std::vector<PatternElement>::const_iterator cbegin() const { return m_Pattern.cbegin(); }
std::vector<PatternElement>::const_iterator cend() const { return m_Pattern.cend(); }
public:
template <typename... Args>
static Pattern CreateBytePattern(const std::string& inputString);
static Pattern CreateStringPattern(const std::string& inputString);
template <typename Arg1, typename Arg2, typename... Args> //2 or more
static void CreatePatternMatchElements(std::vector<PatternMatchElements>& elements)
{
elements.emplace_back(sizeof(Arg1));
CreatePatternMatchElements<Arg2, Args...>(elements);
}
template <typename Arg> //1
static void CreatePatternMatchElements(std::vector<PatternMatchElements>& elements)
{
elements.emplace_back(sizeof(Arg));
}
//0
static void CreatePatternMatchElements(){};
};
template <typename... Args>
Pattern Pattern::CreateBytePattern(const std::string& inputString)
{
//declare values
Pattern returnPattern;
std::string patternString(inputString);
//Parse Out the elements
std::vector<PatternMatchElements> elements;
CreatePatternMatchElements<Args...>(elements);
returnPattern.m_Elements = elements;
//normalize the string
//remove spaces
auto loc = patternString.find(" ");
while (loc != std::string::npos)
{
patternString.replace(loc, 1, "");
loc = patternString.find(" ");
}
//pullvalues
for (std::size_t i = 0; i < returnPattern.m_Elements.size(); ++i)
{
std::stringstream ss;
ss << "[" << i << "]";
auto loc = patternString.find(ss.str());
if (loc != std::string::npos)
{
auto offset = loc / 2;
returnPattern.m_Elements[i].PatternElementLocation = offset;
std::stringstream insertedPattern;
for (auto j = 0; j < returnPattern.m_Elements[i].PatternElementSize; ++j)
{
insertedPattern << "??";
}
patternString.replace(loc, ss.str().length(), insertedPattern.str().c_str());
}
else
{
//error
__debugbreak();
}
}
if (patternString.length() % 2 != 0)
{
patternString.insert(0, "0");
}
std::transform(patternString.begin(), patternString.end(), patternString.begin(), ::toupper);
for (auto i = 0; i < patternString.length(); i += 2)
{
char patLow, patHigh, matchLow, matchHigh;
if (patternString[i] == '?' || patternString[i] == '*')
{
matchHigh = 0;
patHigh = 0;
}
else if (patternString[i] >= '0' && patternString[i] <= '9')
{
matchHigh = 0xF;
patHigh = patternString[i] - '0';
}
else if (patternString[i] >= 'A' && patternString[i] <= 'F')
{
matchHigh = 0xF;
patHigh = patternString[i] - 'A' + 10;
}
else
{
std::cerr << "Invalid Pattern Sequence: " << inputString << std::endl;
}
if (patternString[i + 1] == '?' || patternString[i + 1] == '*')
{
matchLow = 0;
patLow = 0;
}
else if (patternString[i + 1] >= '0' && patternString[i + 1] <= '9')
{
matchLow = 0xF;
patLow = patternString[i + 1] - '0';
}
else if (patternString[i + 1] >= 'A' && patternString[i + 1] <= 'F')
{
matchLow = 0xF;
patLow = patternString[i + 1] - 'A' + 10;
}
else
{
std::cerr << "Invalid Pattern Sequence: " << inputString << std::endl;
}
char byte, match;
byte = (patHigh << 4) | patLow;
match = (matchHigh << 4) | matchLow;
returnPattern.m_Pattern.push_back(PatternElement(byte, match));
}
return returnPattern;
}
};
Pattern.cpp
Code:
#include "evlib\System\Pattern.hpp"
#include <algorithm>
#include <iostream>
namespace ev
{
Pattern Pattern::CreateStringPattern(const std::string& inputString)
{
Pattern returnPattern;
for (auto &c : inputString)
{
returnPattern.m_Pattern.push_back(PatternElement(c, 0xFF));
}
return returnPattern;
}
}
And for reference for how it's used, here's my file I wrote to scrub PE files...
PEHeader.hpp
Code:
#pragma once
#include <string>
#include <cstdint>
#include <vector>
#include <algorithm>
#include "evlib\System\Pattern.hpp"
namespace ev
{
class Executable
{
public:
Executable(const std::string& filename);
enum MachineTypes : uint16_t
{
UnknownMachineType = 0,
AM33 = 0x1d3,
AMD64 = 0x8664,
ARM = 0x1c0,
ARMNT = 0x1c4,
ARM64 = 0xaa64,
EBC = 0xebc,
i386 = 0x14c,
IA64 = 0x200,
M32R = 0x9041,
MIPS16 = 0x266,
MIPSFPU = 0x366,
MIPSFPU16 = 0x466,
PowerPC = 0x1f0,
PowerPCFP = 0x1f1,
R4000 = 0x166,
SH3 = 0x1a2,
SH3DSP = 0x1a3,
SH4 = 0x1a6,
SH5 = 0x1a8,
Thumb = 0x1c2,
WCEMIPSV2 = 0x169
};
enum Characteristics : uint16_t
{
RelocsStripped = 0x0001,
ExecutableImage = 0x0002,
LineNumsStripped = 0x0004,
LocalSymbolsStripped = 0x0008,
AgressiveWorkingSetTrim = 0x0010,
LargeAddressAware = 0x0020,
Unused = 0x0040,
BytesReverseLow = 0x0080,
Machine32Bit = 0x0100,
DebugStripped = 0x0200,
RemovableRunFromSwap = 0x0400,
NetRunFromSwap = 0x0800,
System = 0x1000,
DLL = 0x2000,
UpSystemOnly = 0x4000,
BytesReversedHigh = 0x8000
};
enum WindowsSubsystems : uint16_t
{
UnknownSubsystem = 0,
Native = 1,
WindowsGUI = 2,
WindowsCUI = 3,
POSIXCui = 7,
WindowsCEGUI = 9,
EFIApplication = 10,
EFIBootServiceDriver = 11,
EFIRuntimeDriver = 12,
EFIROM = 13,
Xbox = 14
};
enum SectionCharacteristics : uint32_t
{
NoPad = 0x00000008,
ContainsCode = 0x00000020,
ContainsInitializedData = 0x00000040,
ContainsUninitializedData = 0x00000080,
ContainsComments = 0x00000200,
LinkRemove = 0x00000800,
LinkComdat = 0x00001000,
GlobalPointer = 0x00008000,
ThumbCode = 0x00020000,
Align1Bytes = 0x00100000,
Align2Bytes = 0x00200000,
Align4Bytes = 0x00300000,
Align8Bytes = 0x00400000,
Align16Bytes = 0x00500000,
Align32Bytes = 0x00600000,
Align64Bytes = 0x00700000,
Align128Bytes = 0x00800000,
Align256Bytes = 0x00900000,
Align512Bytes = 0x00A00000,
Align1024Bytes = 0x00B00000,
Align2048Bytes = 0x00C00000,
Align4096Bytes = 0x00D00000,
Align8192Bytes = 0x00E00000,
ContainsExtendedReallocations = 0x01000000,
MemoryDiscardable = 0x02000000,
MemoryNotCached = 0x04000000,
MemoryNotPaged = 0x08000000,
MemoryShared = 0x10000000,
MemoryExecute = 0x20000000,
MemoryRead = 0x40000000,
MemoryWrite = 0x80000000
};
#pragma pack(push)
#pragma pack(1)
struct Dos20CompatSection
{
char Magic[2];
char Buffer[0x40 - 4 - 2];
uint32_t PEOffset;
};
struct StandardFields64
{
char Magic[2];
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode;
};
struct StandardFields32
{
char Magic[2];
uint8_t MajorLinkerVersion;
uint8_t MinorLinkerVersion;
uint32_t SizeOfCode;
uint32_t SizeOfInitializedData;
uint32_t SizeOfUninitializedData;
uint32_t AddressOfEntryPoint;
uint32_t BaseOfCode;
uint32_t BaseOfData;
};
struct WindowsSpecificFields32
{
uint32_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t Checksum;
uint16_t Subsystem;
uint16_t DLLCharacteristics;
uint32_t SizeOfStackReserve;
uint32_t SizeOfStackCommit;
uint32_t SizeOfHeapReserve;
uint32_t SizeOfHeapCommit;
uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes;
};
struct WindowsSpecificFields64
{
uint64_t ImageBase;
uint32_t SectionAlignment;
uint32_t FileAlignment;
uint16_t MajorOperatingSystemVersion;
uint16_t MinorOperatingSystemVersion;
uint16_t MajorImageVersion;
uint16_t MinorImageVersion;
uint16_t MajorSubsystemVersion;
uint16_t MinorSubsystemVersion;
uint32_t Win32VersionValue;
uint32_t SizeOfImage;
uint32_t SizeOfHeaders;
uint32_t Checksum;
uint16_t Subsystem;
uint16_t DLLCharacteristics;
uint64_t SizeOfStackReserve;
uint64_t SizeOfStackCommit;
uint64_t SizeOfHeapReserve;
uint64_t SizeOfHeapCommit;
uint32_t LoaderFlags;
uint32_t NumberOfRvaAndSizes;
};
struct DataDirectory
{
uint32_t VirtualAddress;
uint32_t Size;
};
struct OptionalHeader64
{
StandardFields64 StandardFields;
WindowsSpecificFields64 WindowsSpecificFields;
DataDirectory ExportTable;
DataDirectory ImportTable;
DataDirectory ResourceTable;
DataDirectory ExceptionTable;
DataDirectory CertificateTable;
DataDirectory BaseRelocationTable;
DataDirectory Debug;
DataDirectory Architecture;
DataDirectory GlobalPointer;
DataDirectory TLSTable;
DataDirectory LoadConfigTable;
DataDirectory BoundImport;
DataDirectory IAT;
DataDirectory DelayImportDescriptor;
DataDirectory CLRRuntimeHeader;
DataDirectory Reserved;
};
struct OptionalHeader32
{
StandardFields32 StandardFields;
WindowsSpecificFields32 WindowsSpecificFields;
DataDirectory ExportTable;
DataDirectory ImportTable;
DataDirectory ResourceTable;
DataDirectory ExceptionTable;
DataDirectory CertificateTable;
DataDirectory BaseRelocationTable;
DataDirectory Debug;
DataDirectory Architecture;
DataDirectory GlobalPointer;
DataDirectory TLSTable;
DataDirectory LoadConfigTable;
DataDirectory BoundImport;
DataDirectory IAT;
DataDirectory DelayImportDescriptor;
DataDirectory CLRRuntimeHeader;
DataDirectory Reserved;
};
struct FileHeader
{
uint16_t Machine;
uint16_t NumberOfSections;
uint32_t TimeDateStamp;
uint32_t PointerToSymbolTable;
uint32_t NumberOfSymbols;
uint16_t SizeOfOptionalHeader;
uint16_t Characteristics;
};
struct SectionHeader
{
char Name[8];
uint32_t VirtualSize;
uint32_t VirtualAddress;
uint32_t SizeOfRawData;
uint32_t PointerToRawData;
uint32_t PointerToRelocations;
uint32_t PointerToLineNumbers;
uint16_t NumberOfRelocations;
uint16_t NumberOfLineNumbers;
uint32_t Characteristics;
};
struct SectionData
{
intptr_t StartAddress;
std::string SectionName;
std::vector<char> SectionMemory;
};
struct PESignature
{
char Signature[4];
};
#pragma pack(pop)
//template <typename T>
//T FindPattern(const Pattern& pattern);
PatternResult Executable::FindPattern(const Pattern& pattern);
void Dump() const;
template <typename T>
T ReadMemory(intptr_t address);
void ReadMemoryRaw(intptr_t address, char *data, std::size_t size);
private:
std::vector<char> m_FileData;
FileHeader m_FileHeader;
union
{
OptionalHeader32 m_OptionalHeader32;
OptionalHeader64 m_OptionalHeader64;
};
std::vector<SectionHeader> m_Sections;
std::vector<SectionData> m_SectionData;
bool m_Is32Bit;
};
//template <typename T>
//T Executable::FindPattern(const Pattern& pattern)
//{
// for (auto& sd : m_SectionData)
// {
// T start = static_cast<T>(sd.StartAddress);
// auto found = std::search(sd.SectionMemory.cbegin(), sd.SectionMemory.cend(), pattern.m_Pattern.cbegin(), pattern.m_Pattern.cend(),
// [](const char& actualByte, const PatternElement& patternToMatch){ return patternToMatch.Matches(actualByte); });
// if (found != sd.SectionMemory.end())
// {
// return start + static_cast<T>(found - sd.SectionMemory.begin() + pattern.GetSentinelLocation(0));
// }
// }
// return 0;
//}
template <typename T>
T Executable::ReadMemory(intptr_t address)
{
T returnValue = T();
for (auto& sd : m_SectionData)
{
if (sd.StartAddress < address && address < (intptr_t)(sd.StartAddress + sd.SectionMemory.size() - sizeof(T)))
{
memcpy(&returnValue, &sd.SectionMemory[address - sd.StartAddress], sizeof(T));
return returnValue;
}
}
return returnValue;
}
}
and PEHeader.cpp
Code:
#include "evlib\System\PEHeader.hpp"
#include <fstream>
#include <iostream>
namespace ev
{
template <typename T>
T ReadFromFileData(const std::vector<char>& file, std::size_t ptr)
{
T data;
memset(&data, 0, sizeof(T));
memcpy(&data, &file[0] + ptr, sizeof(T));
return data;
}
Executable::Executable(const std::string& filename)
{
std::ifstream inputFile(filename, std::ios::binary | std::ios::in);
if (inputFile.is_open())
{
inputFile.unsetf(std::ios::skipws);
inputFile.seekg(0, std::ios::end);
auto fileSize = inputFile.tellg();
inputFile.seekg(0, std::ios::beg);
m_FileData.resize(fileSize);
m_FileData.assign(
std::istreambuf_iterator<char>(inputFile),
std::istreambuf_iterator<char>()
);
Dos20CompatSection dosHeader = ReadFromFileData<Dos20CompatSection>(m_FileData, 0);
//std::cout << "Magic: " << dosHeader.Magic[0] << dosHeader.Magic[1] << std::endl;
//std::cout << "PE Offset: " << dosHeader.PEOffset << std::endl;
std::size_t currentReadOffset = dosHeader.PEOffset;
PESignature signature = ReadFromFileData<PESignature>(m_FileData, currentReadOffset);
//std::cout << "PE Signature: " << signature.Signature << std::endl;
currentReadOffset += sizeof(PESignature);
m_FileHeader = ReadFromFileData<FileHeader>(m_FileData, currentReadOffset);
currentReadOffset += sizeof(FileHeader);
m_Is32Bit = (m_FileHeader.Characteristics & Characteristics::Machine32Bit) > 0;
if (m_Is32Bit)
{
}
else
{
m_OptionalHeader64 = ReadFromFileData<OptionalHeader64>(m_FileData, currentReadOffset);
currentReadOffset += m_FileHeader.SizeOfOptionalHeader;
}
for (auto i = 0; i < m_FileHeader.NumberOfSections; ++i)
{
m_Sections.push_back(ReadFromFileData<SectionHeader>(m_FileData, currentReadOffset));
currentReadOffset += sizeof(SectionHeader);
}
for (auto& sh : m_Sections)
{
SectionData d;
sh.PointerToRawData;
d.StartAddress = sh.VirtualAddress;
d.SectionMemory.resize(sh.SizeOfRawData);
memcpy(&d.SectionMemory[0], &(m_FileData[sh.PointerToRawData]), sh.SizeOfRawData);
m_SectionData.push_back(std::move(d));
}
}
}
void Executable::Dump() const
{
std::cout << "COFF File Header\n=================================\n";
std::cout << "Machine Type: ";
switch (m_FileHeader.Machine)
{
case MachineTypes::UnknownMachineType:
std::cout << "Unknown";
break;
case MachineTypes::AM33:
std::cout << "Matsushita AM33";
break;
case MachineTypes::AMD64:
std::cout << "x64";
break;
case MachineTypes::ARM:
std::cout << "ARM Little Endian";
break;
case MachineTypes::ARMNT:
std::cout << "ARMv7 (or Higher) Thumb Mode Only";
break;
case MachineTypes::ARM64:
std::cout << "ARMv8 in 64-Bit Mode";
break;
case MachineTypes::EBC:
std::cout << "EFI Byte Code";
break;
case MachineTypes::i386:
std::cout << "Intel 386 or Later Processors and Compatible Processors";
break;
case MachineTypes::IA64:
std::cout << "Intel Itanium Processor Family";
break;
case MachineTypes::M32R:
std::cout << "Mitsubishi M32R Little Endian";
break;
case MachineTypes::MIPS16:
std::cout << "MIPS16";
break;
case MachineTypes::MIPSFPU:
std::cout << "MIPS with FPU";
break;
case MachineTypes::MIPSFPU16:
std::cout << "MIPS16 with FPU";
break;
case MachineTypes::PowerPC:
std::cout << "Power PC Little Endian";
break;
case MachineTypes::PowerPCFP:
std::cout << "Power PC with Floating Point Support";
break;
case MachineTypes::R4000:
std::cout << "MIPS Little Endian";
break;
case MachineTypes::SH3:
std::cout << "Hitachi SH3";
break;
case MachineTypes::SH3DSP:
std::cout << "Hitachi SH3 DSP";
break;
case MachineTypes::SH4:
std::cout << "Hitachi SH4";
break;
case MachineTypes::SH5:
std::cout << "Hitachi SH5";
break;
case MachineTypes::Thumb:
std::cout << "ARM or Thumb (\"Interworking\")";
break;
case MachineTypes::WCEMIPSV2:
std::cout << "MIPS Little Endian WCE v2";
break;
default:
std::cout << "Unknown";
break;
}
std::cout << std::endl;
std::cout << "Number of Sections: " << m_FileHeader.NumberOfSections << std::endl;
std::cout << "Time Date Stamp: " << m_FileHeader.TimeDateStamp << std::endl;
std::cout << "Number of Symbols: " << m_FileHeader.NumberOfSymbols << std::endl;
std::cout << "Size of Optional Header: " << m_FileHeader.SizeOfOptionalHeader << std::endl;
std::cout << "Characteristics: \n";
if ((m_FileHeader.Characteristics & Characteristics::RelocsStripped) > 0)
{
std::cout << " " << "Relocations Stripped\n";
}
if ((m_FileHeader.Characteristics & Characteristics::ExecutableImage) > 0)
{
std::cout << " " << "Executable Image\n";
}
if ((m_FileHeader.Characteristics & Characteristics::LineNumsStripped) > 0)
{
std::cout << " " << "Line Numbers Stripped (deprecated)\n";
}
if ((m_FileHeader.Characteristics & Characteristics::LocalSymbolsStripped) > 0)
{
std::cout << " " << "Local Symbols Stripped (deprecated)\n";
}
if ((m_FileHeader.Characteristics & Characteristics::AgressiveWorkingSetTrim) > 0)
{
std::cout << " " << "Aggressive Working Set Trim (obsolete)\n";
}
if ((m_FileHeader.Characteristics & Characteristics::LargeAddressAware) > 0)
{
std::cout << " " << "Large Address Aware\n";
}
if ((m_FileHeader.Characteristics & Characteristics::Unused) > 0)
{
std::cout << " " << "Unused (invalid)\n";
}
if ((m_FileHeader.Characteristics & Characteristics::BytesReverseLow) > 0)
{
std::cout << " " << "Little Endian\n";
}
if ((m_FileHeader.Characteristics & Characteristics::Machine32Bit) > 0)
{
std::cout << " " << "32-Bit Word Architecture\n";
}
else
{
std::cout << " " << "64-Bit Word Architecture\n";
}
if ((m_FileHeader.Characteristics & Characteristics::DebugStripped) > 0)
{
std::cout << " " << "Debugging Information Stripped\n";
}
if ((m_FileHeader.Characteristics & Characteristics::RemovableRunFromSwap) > 0)
{
std::cout << " " << "Image on Removable Media\n";
}
if ((m_FileHeader.Characteristics & Characteristics::NetRunFromSwap) > 0)
{
std::cout << " " << "Image on Network Media\n";
}
if ((m_FileHeader.Characteristics & Characteristics::System) > 0)
{
std::cout << " " << "System File\n";
}
if ((m_FileHeader.Characteristics & Characteristics::DLL) > 0)
{
std::cout << " " << "DLL\n";
}
if ((m_FileHeader.Characteristics & Characteristics::UpSystemOnly) > 0)
{
std::cout << " " << "Uniprocessor Machine\n";
}
if ((m_FileHeader.Characteristics & Characteristics::BytesReversedHigh) > 0)
{
std::cout << " " << "Big Endian\n";
}
std::cout << "\n\nOptional Header\n=================================\n";
if (m_Is32Bit)
{
std::cout << "Major Linker Version: " << m_OptionalHeader32.StandardFields.MajorLinkerVersion << std::endl;
std::cout << "Minor Linker Version: " << m_OptionalHeader32.StandardFields.MinorLinkerVersion << std::endl;
std::cout << "Size of Code: " << m_OptionalHeader32.StandardFields.SizeOfCode << std::endl;
std::cout << "Size of Initialized Data: " << m_OptionalHeader32.StandardFields.SizeOfInitializedData << std::endl;
std::cout << "Size of Uninitialized Data: " << m_OptionalHeader32.StandardFields.SizeOfUninitializedData << std::endl;
std::cout << "Entry Point Address: " << m_OptionalHeader32.StandardFields.AddressOfEntryPoint << std::endl;
std::cout << "Base of Code: " << m_OptionalHeader32.StandardFields.BaseOfCode << std::endl;
std::cout << "Base of Data: " << m_OptionalHeader32.StandardFields.BaseOfData << std::endl;
}
else
{
std::cout << "Major Linker Version: " << std::dec << (uint32_t) m_OptionalHeader64.StandardFields.MajorLinkerVersion << std::endl;
std::cout << "Minor Linker Version: " << std::dec << (uint32_t) m_OptionalHeader64.StandardFields.MinorLinkerVersion << std::endl;
std::cout << "Size of Code: " << std::hex << m_OptionalHeader64.StandardFields.SizeOfCode << std::endl;
std::cout << "Size of Initialized Data: " << std::hex << m_OptionalHeader64.StandardFields.SizeOfInitializedData << std::endl;
std::cout << "Size of Uninitialized Data: " << std::hex << m_OptionalHeader64.StandardFields.SizeOfUninitializedData << std::endl;
std::cout << "Entry Point Address: " << std::hex << m_OptionalHeader64.StandardFields.AddressOfEntryPoint << std::endl;
std::cout << "Base of Code: " << std::hex << m_OptionalHeader64.StandardFields.BaseOfCode << std::endl;
std::cout << std::endl;
std::cout << "Image Base: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.ImageBase << std::endl;
std::cout << "SectionAlignment: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SectionAlignment << std::endl;
std::cout << "FileAlignment: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.FileAlignment << std::endl;
std::cout << "MajorOperatingSystemVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MajorOperatingSystemVersion << std::endl;
std::cout << "MinorOperatingSystemVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MinorOperatingSystemVersion << std::endl;
std::cout << "MajorImageVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MajorImageVersion << std::endl;
std::cout << "MinorImageVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MinorImageVersion << std::endl;
std::cout << "MajorSubsystemVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MajorSubsystemVersion << std::endl;
std::cout << "MinorSubsystemVersion: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.MinorSubsystemVersion << std::endl;
std::cout << "Win32VersionValue: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.Win32VersionValue << std::endl;
std::cout << "SizeOfImage: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfImage << std::endl;
std::cout << "SizeOfHeaders: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfHeaders << std::endl;
std::cout << "Checksum: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.Checksum << std::endl;
std::cout << "Subsystem: ";
switch (m_OptionalHeader64.WindowsSpecificFields.Subsystem)
{
case WindowsSubsystems::UnknownSubsystem:
std::cout << "Unknown\n";
break;
case WindowsSubsystems::Native:
std::cout << "Native\n";
break;
case WindowsSubsystems::WindowsGUI:
std::cout << "Windows GUI\n";
break;
case WindowsSubsystems::WindowsCUI:
std::cout << "Windows CUI\n";
break;
case WindowsSubsystems::POSIXCui:
std::cout << "POSIX CUI\n";
break;
case WindowsSubsystems::WindowsCEGUI:
std::cout << "Windows CE GUI\n";
break;
case WindowsSubsystems::EFIApplication:
std::cout << "EFI Application\n";
break;
case WindowsSubsystems::EFIBootServiceDriver:
std::cout << "EFI Boot Service Driver\n";
break;
case WindowsSubsystems::EFIRuntimeDriver:
std::cout << "EFI Runtime Driver\n";
break;
case WindowsSubsystems::EFIROM:
std::cout << "EFI ROM\n";
break;
case WindowsSubsystems::Xbox:
std::cout << "Xbox\n";
break;
default:
std::cout << "Unknown\n";
break;
}
std::cout << "DLLCharacteristics: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.DLLCharacteristics << std::endl;
std::cout << "SizeOfStackReserve: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfStackReserve << std::endl;
std::cout << "SizeOfStackCommit: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfStackCommit << std::endl;
std::cout << "SizeOfHeapReserve: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfHeapReserve << std::endl;
std::cout << "SizeOfHeapCommit: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.SizeOfHeapCommit << std::endl;
std::cout << "LoaderFlags: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.LoaderFlags << std::endl;
std::cout << "NumberOfRvaAndSizes: " << std::hex << m_OptionalHeader64.WindowsSpecificFields.NumberOfRvaAndSizes << std::endl;
std::cout << "Export Table @: " << std::hex << m_OptionalHeader64.ExportTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.ExportTable.Size << ")\n";
std::cout << "Import Table @: " << std::hex << m_OptionalHeader64.ImportTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.ImportTable.Size << ")\n";
std::cout << "Resource Table @: " << std::hex << m_OptionalHeader64.ResourceTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.ResourceTable.Size << ")\n";
std::cout << "Exception Table @: " << std::hex << m_OptionalHeader64.ExceptionTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.ExceptionTable.Size << ")\n";
std::cout << "Certificate Table @: " << std::hex << m_OptionalHeader64.CertificateTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.CertificateTable.Size << ")\n";
std::cout << "Base Relocation Table @: " << std::hex << m_OptionalHeader64.BaseRelocationTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.BaseRelocationTable.Size << ")\n";
std::cout << "Debug @: " << std::hex << m_OptionalHeader64.Debug.VirtualAddress << " (" << std::dec << m_OptionalHeader64.Debug.Size << ")\n";
/*std::cout << "Architecture @: " << std::hex << m_OptionalHeader64.Architecture.VirtualAddress << " (" << std::dec << m_OptionalHeader64.Architecture.Size << ")\n";*/
std::cout << "Global Pointer @: " << std::hex << m_OptionalHeader64.GlobalPointer.VirtualAddress << " (" << std::dec << m_OptionalHeader64.GlobalPointer.Size << ")\n";
std::cout << "TLS Table @: " << std::hex << m_OptionalHeader64.TLSTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.TLSTable.Size << ")\n";
std::cout << "Load Config Table @: " << std::hex << m_OptionalHeader64.LoadConfigTable.VirtualAddress << " (" << std::dec << m_OptionalHeader64.LoadConfigTable.Size << ")\n";
std::cout << "Bound Import @: " << std::hex << m_OptionalHeader64.BoundImport.VirtualAddress << " (" << std::dec << m_OptionalHeader64.BoundImport.Size << ")\n";
std::cout << "IAT @: " << std::hex << m_OptionalHeader64.IAT.VirtualAddress << " (" << std::dec << m_OptionalHeader64.IAT.Size << ")\n";
std::cout << "Delay Import Descriptor @: " << std::hex << m_OptionalHeader64.DelayImportDescriptor.VirtualAddress << " (" << std::dec << m_OptionalHeader64.DelayImportDescriptor.Size << ")\n";
std::cout << "CLR Runtime Header @: " << std::hex << m_OptionalHeader64.CLRRuntimeHeader.VirtualAddress << " (" << std::dec << m_OptionalHeader64.CLRRuntimeHeader.Size << ")\n";
//std::cout << "Reserved @: " << std::hex << m_OptionalHeader64.Reserved.VirtualAddress << " (" << m_OptionalHeader64.Reserved.Size << ")\n";
std::cout << "\n\nSections\n=================================\n\n";
for (auto& sh : m_Sections)
{
std::cout << "Section: " << sh.Name << std::endl;
std::cout << " Virtual Address: " << std::hex << sh.VirtualAddress << std::endl;
std::cout << " Virtual Size: " << std::hex << sh.VirtualSize << std::endl;
std::cout << " Raw Data Size: " << std::hex << sh.SizeOfRawData << std::endl;
std::cout << " Pointer To Raw Data: " << std::hex << sh.PointerToRawData << std::endl;
std::cout << " Pointer To Relocations: " << std::hex << sh.PointerToRelocations << std::endl;
std::cout << " Pointer To Line Numbers: " << std::hex << sh.PointerToLineNumbers << std::endl;
std::cout << " Number of Relocations: " << std::dec << sh.NumberOfRelocations << std::endl;
std::cout << " Number of Line Numbers: " << std::dec << sh.NumberOfLineNumbers << std::endl;
std::cout << " Characteristics: " << std::endl;
if ((sh.Characteristics & NoPad) > 0)
{
std::cout << " NoPad" << std::endl;
}
if ((sh.Characteristics & ContainsCode) > 0)
{
std::cout << " ContainsCode" << std::endl;
}
if ((sh.Characteristics & ContainsInitializedData) > 0)
{
std::cout << " ContainsInitializedData" << std::endl;
}
if ((sh.Characteristics & ContainsUninitializedData) > 0)
{
std::cout << " ContainsUninitializedData" << std::endl;
}
if ((sh.Characteristics & ContainsComments) > 0)
{
std::cout << " ContainsComments" << std::endl;
}
if ((sh.Characteristics & LinkRemove) > 0)
{
std::cout << " LinkRemove" << std::endl;
}
if ((sh.Characteristics & LinkComdat) > 0)
{
std::cout << " LinkComdat" << std::endl;
}
if ((sh.Characteristics & GlobalPointer) > 0)
{
std::cout << " GlobalPointer" << std::endl;
}
if ((sh.Characteristics & ThumbCode) > 0)
{
std::cout << " ThumbCode" << std::endl;
}
if ((sh.Characteristics & Align1Bytes) > 0)
{
std::cout << " Align1Bytes" << std::endl;
}
if ((sh.Characteristics & Align2Bytes) > 0)
{
std::cout << " Align2Bytes" << std::endl;
}
if ((sh.Characteristics & Align4Bytes) > 0)
{
std::cout << " Align4Bytes" << std::endl;
}
if ((sh.Characteristics & Align8Bytes) > 0)
{
std::cout << " Align8Bytes" << std::endl;
}
if ((sh.Characteristics & Align16Bytes) > 0)
{
std::cout << " Align16Bytes" << std::endl;
}
if ((sh.Characteristics & Align32Bytes) > 0)
{
std::cout << " Align32Bytes" << std::endl;
}
if ((sh.Characteristics & Align64Bytes) > 0)
{
std::cout << " Align64Bytes" << std::endl;
}
if ((sh.Characteristics & Align128Bytes) > 0)
{
std::cout << " Align128Bytes" << std::endl;
}
if ((sh.Characteristics & Align256Bytes) > 0)
{
std::cout << " Align256Bytes" << std::endl;
}
if ((sh.Characteristics & Align512Bytes) > 0)
{
std::cout << " Align512Bytes" << std::endl;
}
if ((sh.Characteristics & Align1024Bytes) > 0)
{
std::cout << " Align1024Bytes" << std::endl;
}
if ((sh.Characteristics & Align2048Bytes) > 0)
{
std::cout << " Align2048Bytes" << std::endl;
}
if ((sh.Characteristics & Align4096Bytes) > 0)
{
std::cout << " Align4096Bytes" << std::endl;
}
if ((sh.Characteristics & Align8192Bytes) > 0)
{
std::cout << " Align8192Bytes" << std::endl;
}
if ((sh.Characteristics & ContainsExtendedReallocations) > 0)
{
std::cout << " ContainsExtendedReallocations" << std::endl;
}
if ((sh.Characteristics & MemoryDiscardable) > 0)
{
std::cout << " MemoryDiscardable" << std::endl;
}
if ((sh.Characteristics & MemoryNotCached) > 0)
{
std::cout << " MemoryNotCached" << std::endl;
}
if ((sh.Characteristics & MemoryNotPaged) > 0)
{
std::cout << " MemoryNotPaged" << std::endl;
}
if ((sh.Characteristics & MemoryShared) > 0)
{
std::cout << " MemoryShared" << std::endl;
}
if ((sh.Characteristics & MemoryExecute) > 0)
{
std::cout << " MemoryExecute" << std::endl;
}
if ((sh.Characteristics & MemoryRead) > 0)
{
std::cout << " MemoryRead" << std::endl;
}
if ((sh.Characteristics & MemoryWrite) > 0)
{
std::cout << " MemoryWrite" << std::endl;
}
std::cout << std::endl;
}
}
}
void Executable::ReadMemoryRaw(intptr_t address, char *data, std::size_t size)
{
for (auto& sd : m_SectionData)
{
if (sd.StartAddress < address && address < (intptr_t) (sd.StartAddress + sd.SectionMemory.size() - size))
{
memcpy(data, &sd.SectionMemory[address - sd.StartAddress], size);
return ;
}
}
return;
}
PatternResult Executable::FindPattern(const Pattern& pattern)
{
PatternResult result;
result.PatternFound = false;
for (auto& sd : m_SectionData)
{
auto start = sd.StartAddress;
auto found = std::search(sd.SectionMemory.cbegin(), sd.SectionMemory.cend(), pattern.cbegin(), pattern.cend(),
[](const char& actualByte, const PatternElement& patternToMatch){ return patternToMatch.Matches(actualByte); });
if (found != sd.SectionMemory.end())
{
result.PatternFound = true;
result.PatternFoundLocation = start + found - sd.SectionMemory.begin();
for (auto i = 0; i < pattern.GetNumMatchElements(); i++)
{
auto element = pattern.GetMatchElement(i);
PatternResultElement pre(element.PatternElementSize);
pre.SetAddress(result.PatternFoundLocation + element.PatternElementLocation);
//don't know how to do this
char *data = new char[element.PatternElementSize];
ReadMemoryRaw(pre.GetAddress(), data, element.PatternElementSize);
pre.SetValue(data, element.PatternElementSize);
auto ra = pre.GetValue<uint32_t>();
ra += pre.GetAddress() + element.PatternElementSize;
pre.SetRelativeAddress(ra);
result.PatternElements.push_back(pre);
}
}
}
return result;
}
}
And for reference, here's an example of how i use this to get the raid structure offsets
Offsets.cpp
Code:
void CalculateRaidTableOffsets(std::shared_ptr<Executable> e)
{
auto result = e->FindPattern(Pattern::CreateBytePattern<uint32_t, uint32_t>("CC 40 53 55 48 81 EC ?? ?? 00 00 BD 01 00 00 00 48 8B D9 8B D5 E8 ?? ?? ?? ?? 48 8B CB 85 C0 75 18 48 8D 15 ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 C0 48 81 C4 ?? ?? 00 00 5D 5B C3 8B D5 48 89 B4 24 ?? ?? 00 00 48 89 BC 24 ?? ?? 00 00 E8 ?? ?? ?? ?? 48 8B 3D [0] F2 48 0F 2C C0 FF C8 48 85 FF 75 ?? 48 8B 3D [1] 48 85 FF"));
if (result.PatternFound && result.PatternElements.size() == 2)
{
PrintOffset("RaidMemberTable1", result.PatternElements[0].GetRelativeAddress());
PrintOffset("RaidMemberTable2", result.PatternElements[1].GetRelativeAddress());
}
else
{
PrintError("RaidMemberTable1");
PrintError("RaidMemberTable2");
}
}
Perhaps we can use this as an opportunity to share some patterns as well.
-Ev