Evan's Pattern Searcher menu

Shout-Out

User Tag List

Results 1 to 7 of 7
  1. #1
    Evansbee's Avatar Active Member
    Reputation
    31
    Join Date
    Jul 2009
    Posts
    24
    Thanks G/R
    1/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Evan's Pattern Searcher

    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

    Evan's Pattern Searcher
  2. #2
    reliasn's Avatar Legendary Authenticator enabled
    Reputation
    774
    Join Date
    Jan 2009
    Posts
    136
    Thanks G/R
    24/215
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Interesting implementation when compared to mine lol:
    Code:
    DWORD searchPattern(byte *v1, byte *v2, int v1_size, int v2_size){
    	int ofs = 0;
    	for(int i = 0, j = 0; i < v1_size && j < v2_size; i++, j++){
    		if(v1[i] == v2[j] || v1[i] == '?'){
    			if(i == 0){
    				ofs = j;
    			}
    		}else{
    			j -= i;
    			i = -1;
    			ofs = 0;
    		}
    	}
    	return ofs;
    }
    I do have others to handle std::string directly, but in this one, I tried to avoid using any std:: functions. In any case, I doubt it would have any significant performance difference.

    For diffing, I have my tool to quickly find the offsets that I need. It parses this JSON file and gives me another file with all the offsets. The interesting thing is that, besides pattern matching, it also supports xrefs for static pointers and functions. Then if the offset found through pattern doesn't match the one found through xref, it shows a warning in the output file.

    Another interesting thing to include in your lib is a function to generate those binary patterns that you're using. Given an offset in the executable and a length, it generates the string or byte array which is basically the pattern used by your CreateBytePattern. You might need to use a disassembly library for that, not sure.

    Anyways, good job!
    Last edited by reliasn; 07-13-2015 at 08:14 AM.

  3. #3
    Evansbee's Avatar Active Member
    Reputation
    31
    Join Date
    Jul 2009
    Posts
    24
    Thanks G/R
    1/0
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by reliasn View Post
    Interesting implementation when compared to mine lol:
    Code:
    DWORD searchPattern(byte *v1, byte *v2, int v1_size, int v2_size){
    	int ofs = 0;
    	for(int i = 0, j = 0; i < v1_size && j < v2_size; i++, j++){
    		if(v1[i] == v2[j] || v1[i] == '?'){
    			if(i == 0){
    				ofs = j;
    			}
    		}else{
    			j -= i;
    			i = -1;
    			ofs = 0;
    		}
    	}
    	return ofs;
    }
    I do have others to handle std::string directly, but in this one, I tried to avoid using any std:: functions. In any case, I doubt it would have any significant performance difference.

    For diffing, I have my tool to quickly find the offsets that I need. It parses this JSON file and gives me another file with all the offsets. The interesting thing is that, besides pattern matching, it also supports xrefs for static pointers and functions. Then if the offset found through pattern doesn't match the one found through xref, it shows a warning in the output file.

    Another interesting thing to include in your lib is a function to generate those binary patterns that you're using. Given an offset in the executable and a length, it generates the string or byte array which is basically the pattern used by your CreateBytePattern. You might need to use a disassembly library for that, not sure.

    Anyways, good job!

    Haha, i recognize the complexity (and that there are a ton of simple FindPattern implementations that work well, but it's there because I want to do a lot of those things you just mentioned. I did have the idea of taking historical binaries and automatically computing optimal/minimal patterns from it. I elected to not share that bit yet because it's a huge mucking-fess, although it does sort of work.

    Thanks for the feedback.

  4. #4
    Filint's Avatar Contributor Authenticator enabled
    Reputation
    167
    Join Date
    Mar 2014
    Posts
    97
    Thanks G/R
    23/56
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by reliasn View Post
    That's a very useful file! :O Describes nicely some short routes to finding certain functions. Thanks.

  5. #5
    reliasn's Avatar Legendary Authenticator enabled
    Reputation
    774
    Join Date
    Jan 2009
    Posts
    136
    Thanks G/R
    24/215
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by Filint View Post
    That's a very useful file! :O Describes nicely some short routes to finding certain functions. Thanks.
    Yeah, it's all right, but still needs a lot of work. Currently, it finds all my offsets in less than 3 seconds. You can test the JSON file in the "Dev Tools" section of my bot.

    I created it mainly for learning purposes and because I didn't want to rely on IDA all the time. My idea is to apply multiple diffing algorithms and compare the results for matching. Somewhat like BinDiff, but way simpler and convenient for small binary changes. For complex cases, e.g., diffing against a new expansion's .exe, then I'd definitely go with IDA/BinDiff.

    To genereate patterns, I'm currently using HDE (Hacker Disassembler Engine) and I simply put "??" on arguments. Opcodes are kept. I'm really interested in implementing this in another way, if possible, without HDE.

    The main problem with patterns is the fact that it fails against instruction swaps and shifts. Sometimes the function logic is the same but with one "test" being changed by a "cmp" - and this is enough to break your pattern. That's when hash, graph and xref analysis enter to help.

  6. #6
    wraithZX's Avatar Active Member
    Reputation
    43
    Join Date
    May 2007
    Posts
    122
    Thanks G/R
    0/1
    Trade Feedback
    1 (100%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This is my search code, optimised in the loops, not in the locks:

    Code:
    inline std::vector<std::uintptr_t> kaos::memory_searcher::find_byte_sequence(std::uint8_t* dataPtr,
                                                                                 std::size_t dataLength,
                                                                                 const std::uint8_t* patt,
                                                                                 const std::uint8_t* mask,
                                                                                 std::size_t pattLength) const
    {
      std::size_t masksRequired = ((pattLength + 15) / 16);
      std::uint32_t masks[32];
      for(int i = 0; i < masksRequired; ++i)
      {
        masks[i] = 0;
        for(int j = int(std::min(pattLength - i * 16, std::size_t(16))) - 1; j >= 0; --j)
          masks[i] |= (mask[i * 16 + j] != 0) ? (1 << j) : 0;
      }
    
      std::vector<std::uintptr_t> results;
      std::mutex mtx;
      BYTE* foundPtr = nullptr;
    #pragma omp parallel for
      for(int checkOffset = 0; checkOffset < (dataLength - pattLength); ++checkOffset)
      {
        if(foundPtr != nullptr)
          break;
        if(dataPtr[checkOffset] != patt[0])
          continue;
        for(std::size_t maskIndex = 0, patternOffset = 0; maskIndex < masksRequired; ++maskIndex, patternOffset += 16)
        {
          __m128i m =
              _mm_cmpeq_epi8(_mm_lddqu_si128(reinterpret_cast<const __m128i*>(dataPtr + checkOffset + patternOffset)),
                             _mm_lddqu_si128(reinterpret_cast<const __m128i*>(patt + patternOffset)));
          if((_mm_movemask_epi8(m) & masks[maskIndex]) != masks[maskIndex])
            break;
          if(maskIndex == (masksRequired - 1))
          {
            std::unique_lock<std::mutex> lock(mtx);
            results.push_back(reinterpret_cast<std::uintptr_t>(&dataPtr[checkOffset]));
          }
        }
      }
      return results;
    }

  7. #7
    ioctl's Avatar Active Member
    Reputation
    23
    Join Date
    Jan 2013
    Posts
    35
    Thanks G/R
    2/4
    Trade Feedback
    0 (0%)
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It looks to me like you can transform your pattern scheme pretty trivially into a regular expression. I am guessing that a good RE library (PCRE or RE2) will evaluate these much faster.

Similar Threads

  1. JC pattern exploit *Make lots of gold* JC not req.
    By Paperboi in forum World of Warcraft Exploits
    Replies: 10
    Last Post: 06-30-2007, 05:27 PM
  2. Hidden spots to buy rare patterns
    By Pixo in forum World of Warcraft Guides
    Replies: 20
    Last Post: 01-20-2007, 02:17 AM
  3. selling pattern: herbalist's gloves
    By Marmos in forum World of Warcraft Guides
    Replies: 1
    Last Post: 01-13-2007, 06:04 PM
  4. Couple Patterns
    By funkdmonkey in forum World of Warcraft Guides
    Replies: 2
    Last Post: 05-25-2006, 12:46 PM
All times are GMT -5. The time now is 12:29 AM. Powered by vBulletin® Version 4.2.3
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. User Alert System provided by Advanced User Tagging (Pro) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Google Authenticator verification provided by Two-Factor Authentication (Free) - vBulletin Mods & Addons Copyright © 2025 DragonByte Technologies Ltd.
Digital Point modules: Sphinx-based search