Code:
// Copyright 2009 Apoc @ ApocDev.com | Apoc @ MMOwned.com | Apoc @ GameDeception.net
#define X64
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using System.Xml.Linq;
namespace FindPatternSharp
{
/// <summary>
/// Credits to Dominik, Patrick, Bobbysing, and whoever else I forgot, for most of the ripped code here!
/// </summary>
public class FindPattern
{
#if !X64
private readonly Dictionary<string, uint> _patterns = new Dictionary<string, uint>();
#else
private readonly Dictionary<string, ulong> _patterns = new Dictionary<string, ulong>();
#endif
/// <summary>
/// Creates a new instance of the <see cref="FindPattern"/> class. This class will read from a specified patterns XML file
/// and search out those patterns in the specified process's memory.
/// </summary>
/// <param name="patternFile">The full path to the pattern XML file.</param>
/// <param name="processHandle">An open process handle to the process to read memory from.</param>
/// <param name="startAddr">The 'base' address of the process (or module)</param>
/// <param name="endAddr">The 'end' of the process (or module). Eg; where to stop reading memory from.</param>
#if !X64
public FindPattern(string patternFile, IntPtr processHandle, uint startAddr, uint endAddr)
#else
public FindPattern(string patternFile, IntPtr processHandle, ulong startAddr, ulong endAddr)
#endif
{
// Get a temporary set of data to work with. :)
byte[] data = ReadBytes(processHandle, (IntPtr) startAddr, (int) (endAddr - startAddr));
LoadFile(XElement.Load(patternFile), data, startAddr);
}
/// <summary>
/// Retrieves an address from the found patterns stash.
/// </summary>
/// <param name="name">The name of the pattern, as per the XML file provided in the constructor of this class instance.</param>
/// <returns></returns>
#if !X64
public uint this[string name]
#else
public ulong this[string name]
#endif
{ get { return Get(name); } }
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize,
out int lpNumberOfBytesRead);
private static byte[] ReadBytes(IntPtr processHandle, IntPtr address, int count)
{
var ret = new byte[count];
int numRead;
if (ReadProcessMemory(processHandle, address, ret, count, out numRead) && numRead == count)
{
return ret;
}
return null;
}
/// <summary>
/// Retrieves an address from the found patterns stash.
/// </summary>
/// <param name="name">The name of the pattern, as per the XML file provided in the constructor of this class instance.</param>
/// <returns></returns>
#if !X64
public uint Get(string name)
#else
public ulong Get(string name)
#endif
{
return _patterns[name];
}
#if !X64
private void LoadFile(XContainer file, byte[] data, uint start)
#else
private void LoadFile(XContainer file, byte[] data, ulong start)
#endif
{
// Grab all the <Pattern /> elements from the XML.
IEnumerable<XElement> pats = from p in file.Descendants("Pattern")
select p;
// Each Pattern element needs to be handled seperately.
// The enumeration we're goinv over, is in document order, so attributes such as 'start'
// should work perfectly fine.
foreach (XElement pat in pats)
{
#if !X64
uint tmpStart = 0;
#else
ulong tmpStart = 0;
#endif
string name = pat.Attribute("desc").Value;
string mask = pat.Attribute("mask").Value;
byte[] patternBytes = GetBytesFromPattern(pat.Attribute("pattern").Value);
// Make sure we're not getting some sort of screwy XML data.
if (mask.Length != patternBytes.Length)
throw new Exception("Pattern and mask lengths do not match!");
// If we run into a 'start' attribute, we need to remember that we're working from a 0
// based 'memory pool'. So we just remove the 'start' from the address we found earlier.
if (pat.Attribute("start") != null)
{
tmpStart = Get(pat.Attribute("start").Value) - start + 1;
}
// Actually search for the pattern match...
#if !X64
uint found = Find(data, mask, patternBytes, tmpStart);
#else
ulong found = Find(data, mask, patternBytes, tmpStart);
#endif
if (found == 0)
throw new Exception("FindPattern failed... figure it out ****tard!");
// Handle specific child elements for the pattern.
// <Lea> <Rel> <Add> <Sub> etc
foreach (XElement e in pat.Elements())
{
switch (e.Name.LocalName)
{
case "Lea":
#if !X64
found = BitConverter.ToUInt32(data, (int) found);
#else
found = BitConverter.ToUInt64(data, (int) found);
#endif
break;
case "Rel":
uint instructionSize = uint.Parse(e.Attribute("size").Value, NumberStyles.HexNumber);
uint operandOffset = uint.Parse(e.Attribute("offset").Value, NumberStyles.HexNumber);
#if !X64
found = (uint) (BitConverter.ToUInt32(data, (int) found) + found + instructionSize - operandOffset);
#else
found = (BitConverter.ToUInt64(data, (int) found) + found + instructionSize - operandOffset);
#endif
break;
case "Add":
found += uint.Parse(e.Attribute("value").Value, NumberStyles.HexNumber);
break;
case "Sub":
found -= uint.Parse(e.Attribute("value").Value, NumberStyles.HexNumber);
break;
}
}
_patterns.Add(name, found + start);
}
}
private static byte[] GetBytesFromPattern(string pattern)
{
// Because I'm lazy, and this just makes life easier.
string[] split = pattern.Split(new[] {'\\', 'x'}, StringSplitOptions.RemoveEmptyEntries);
var ret = new byte[split.Length];
for (int i = 0; i < split.Length; i++)
{
ret[i] = byte.Parse(split[i], NumberStyles.HexNumber);
}
return ret;
}
#if !X64
private static uint Find(byte[] data, string mask, byte[] byteMask, uint start)
#else
private static ulong Find(byte[] data, string mask, byte[] byteMask, ulong start)
#endif
{
// There *has* to be a better way to do this stuff,
// but for now, we'll deal with it.
#if !X64
for (uint i = start; i < data.Length; i++)
#else
for (ulong i = start; i < (ulong) data.Length; i++)
#endif
{
if (DataCompare(data, (int) i, byteMask, mask))
return i;
}
return 0;
}
private static bool DataCompare(byte[] data, int offset, byte[] byteMask, string mask)
{
// Only check for 'x' mismatches. As we'll assume anything else is a wildcard.
for (int i = 0; i < mask.Length; i++)
{
if (mask[i] == 'x' && byteMask[i] != data[i + offset])
{
return false;
}
}
return true;
}
}
}
Example XML file:
Code:
// Copyright 2009 Apoc @ ApocDev.com | Apoc @ MMOwned.com | Apoc @ GameDeception.net
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using FindPatternSharp;
namespace FindPatternSharpTest
{
internal class Program
{
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
private static void Main()
{
Process.EnterDebugMode();
int procId = Process.GetProcessesByName("Wow")[0].Id;
IntPtr hProc = OpenProcess(0x001F0FFF, true, procId);
var fp = new FindPattern("Patterns.xml", hProc, 0x401000, 0x800000);
Console.WriteLine(fp.Get("FrameScript_Execute").ToString("X"));
Console.ReadLine();
}
}
}
Notes; I didn't find the need to include any type of process handle opening and whatnot in the actual FindPatterns class, as it really isn't the job of that class. (I included RPM for brevity, and to make sure the code will work out of the box, with no issues.)