using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
public static class Extensions
public static string GetProductVersionString(this Expansion expansion)
case Expansion.BurningCrusade:
case Expansion.WrathOfTheLichKing:
private Expansion GetExpansionForProcess(Process plausibleProcess)
foreach (Expansion expansion in Enum.GetValues(typeof(Expansion)))
if (plausibleProcess.MainModule.FileVersionInfo.ProductVersion.Equals(expansion.GetProductVersionString(), StringComparison.OrdinalIgnoreCase))
return Expansion.Unsupported;
static void Main(string[] args)
var procs = Process.GetProcessesByName("WoW");
var list = new List<ExternalProcessHook>();
name = new GreyMagic.ExternalProcessReader(p).ReadString(new IntPtr(0xC79D18), Encoding.UTF8);
if (args.Where(n => n.ToLowerInvariant().Equals(name.ToLowerInvariant())).Count() > 0)
Console.WriteLine("Skipping: " + name);
Program proc = new Program();
var res = proc.Hook(p,cnt,name);
Console.WriteLine("Press any key to exit");
private ExternalProcessHook Hook(Process p,int reg,string name)
var baseAddress = (long)p.MainModule.BaseAddress;
switch (GetExpansionForProcess(p))
case Expansion.BurningCrusade:
case Expansion.WrathOfTheLichKing:
var hook = new ExternalProcessHook(p, (HookRegister)(reg), new IntPtr(baseAddress), new ExternalProcessHook.HandleHookCallback(LuaProtectionCallback));
Console.WriteLine("Patched: " + name + p.Id + " Used HookRegister # " + reg);
private void LuaProtectionCallback(ref ExternalProcessHook.ThreadContext threadContext)
threadContext.Eip += _Padding;
public class ExternalProcessHook : IDisposable
public delegate void HandleHookCallback(ref ThreadContext threadContext);
private readonly List<HookItem> Hooks = new List<HookItem>();
private Thread _workerThread;
private bool _removeHook;
private bool _debugActiveAlready;
private readonly HookItem _hook;
public ExternalProcessHook(Process memory, HookRegister register, IntPtr hookLocation,
HandleHookCallback callback)
var i = new HookItem { Callback = callback, Location = hookLocation, Register = register, Proc = memory };
if (_hookCount == 0 || _workerThread == null)
new Thread(() => InstallHardwareHook(memory));
_workerThread.IsBackground = true;
SetThreadHook(_hook, true);
private static void OpenAllThreads(Process proc)
var te = new THREADENTRY32();
IntPtr hSnapshot = CreateToolhelp32Snapshot(4, 0);
if (!OpenThreadHandles.ContainsKey(proc))
OpenThreadHandles.Add(proc, new List<IntPtr>());
if (Thread32First(hSnapshot, ref te) && Thread32Next(hSnapshot, out te))
if (te.th32OwnerProcessID == proc.Id)
OpenThreadHandles[proc].Add(OpenThread(0x1FFFFF, false, te.th32ThreadID));
while (Thread32Next(hSnapshot, out te));
private static void SetDebugRegisters(HookRegister register, IntPtr hookLocation, ref ThreadContext ct, bool remove)
ct.Dr0 = (uint)hookLocation;
ct.Dr1 = (uint)hookLocation;
ct.Dr2 = (uint)hookLocation;
ct.Dr3 = (uint)hookLocation;
private static void SetThreadHook(HookItem item, bool remove)
var ctx = new ThreadContext();
ctx.ContextFlags = 65559;
if (OpenThreadHandles.ContainsKey(item.Proc))
foreach (IntPtr openThreadHandle in OpenThreadHandles[item.Proc].ToArray())
SuspendThread(openThreadHandle);
GetThreadContext(openThreadHandle, ref ctx);
SetDebugRegisters(item.Register, item.Location, ref ctx, remove);
SetThreadContext(openThreadHandle, ref ctx);
ResumeThread(openThreadHandle);
public void InstallHardwareHook(Process proc)
OpenProcess(0x1FFFFFu, false, (uint)proc.Id);
if (!_debugActiveAlready && !DebugActiveProcess((uint)proc.Id))
throw new Exception("Failed to attach debugger!");
_debugActiveAlready = true;
DebugSetProcessKillOnExit(0);
if (Hooks.Any(i => !i.Hooked))
foreach (HookItem hookItem in Hooks)
SetThreadHook(hookItem, false);
var ctx = new ThreadContext();
ctx.ContextFlags = 0x10017;
if (!WaitForDebugEvent(out evt, 0xFFFFFFFF))
if (evt.Exception.ExceptionRecord.ExceptionCode != 0x80000004)
ContinueDebugEvent((uint)evt.dwProcessId, (uint)evt.dwThreadId, 0x80010001);
IntPtr hThread = OpenThread(0x1FFFFFu, false, (uint)evt.dwThreadId);
GetThreadContext(hThread, ref ctx);
HookItem hook = Hooks.FirstOrDefault(h => (uint)h.Location == ctx.Eip);
SetThreadContext(hThread, ref ctx);
ContinueDebugEvent((uint)evt.dwProcessId, (uint)evt.dwThreadId, 0x10002u);
foreach (HookItem hookItem in Hooks)
SetThreadHook(hookItem, true);
private static readonly Dictionary<Process, List<IntPtr>> OpenThreadHandles = new Dictionary<Process, List<IntPtr>>();
[DllImport("kernel32.dll")]
private static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle,
[DllImport("kernel32.dll")]
private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle,
[DllImport("kernel32.dll")]
private static extern bool Thread32First(IntPtr hSnapshot, ref THREADENTRY32 lpte);
[DllImport("kernel32.dll")]
private static extern bool Thread32Next(IntPtr hSnapshot, out THREADENTRY32 lpte);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateToolhelp32Snapshot(int dwFlags, uint th32ProcessID);
[DllImport("kernel32.dll")]
private static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
private static extern bool GetThreadContext(IntPtr hThread, ref ThreadContext lpContext);
[DllImport("kernel32.dll")]
private static extern bool SetThreadContext(IntPtr hThread,
[In] ref ThreadContext lpContext);
[DllImport("kernel32.dll")]
private static extern uint ResumeThread(IntPtr hThread);
[DllImport("kernel32.dll")]
private static extern bool DebugActiveProcess(uint dwProcessId);
[DllImport("kernel32.dll")]
private static extern bool DebugSetProcessKillOnExit(uint dwProcessId);
[DllImport("kernel32.dll", EntryPoint = "WaitForDebugEvent")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool WaitForDebugEvent(out DEBUG_EVENT lpDebugEvent, uint dwMilliseconds);
[DllImport("kernel32.dll")]
private static extern bool ContinueDebugEvent(uint dwProcessId, uint dwThreadId,
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
private unsafe struct DEBUG_EVENT
public readonly uint dwDebugEventCode;
public readonly int dwProcessId;
public readonly int dwThreadId;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 86, ArraySubType = UnmanagedType.U1)]
private readonly byte[] debugInfo;
public EXCEPTION_DEBUG_INFO Exception
return new EXCEPTION_DEBUG_INFO();
fixed (byte* ptr = debugInfo)
return *(EXCEPTION_DEBUG_INFO*)ptr;
public LOAD_DLL_DEBUG_INFO LoadDll
return new LOAD_DLL_DEBUG_INFO();
fixed (byte* ptr = debugInfo)
return *(LOAD_DLL_DEBUG_INFO*)ptr;
[StructLayout(LayoutKind.Sequential)]
private struct EXCEPTION_DEBUG_INFO
public EXCEPTION_RECORD ExceptionRecord;
public readonly uint dwFirstChance;
[StructLayout(LayoutKind.Sequential)]
private struct EXCEPTION_RECORD
public readonly uint ExceptionCode;
public readonly uint ExceptionFlags;
public readonly IntPtr ExceptionRecord;
public readonly IntPtr ExceptionAddress;
public readonly uint NumberParameters;
public unsafe fixed uint ExceptionInformation[15];
[StructLayout(LayoutKind.Sequential)]
public struct FLOATING_SAVE_AREA
public uint ErrorSelector;
public uint DataSelector;
public unsafe fixed byte RegisterArea[80];
[StructLayout(LayoutKind.Sequential)]
private struct LOAD_DLL_DEBUG_INFO
public readonly IntPtr hFile;
public readonly IntPtr lpBaseOfDll;
public readonly uint dwDebugInfoFileOffset;
public readonly uint nDebugInfoSize;
public readonly IntPtr lpImageName;
public readonly ushort fUnicode;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private struct THREADENTRY32
internal readonly UInt32 cntUsage;
internal readonly UInt32 th32ThreadID;
internal readonly UInt32 th32OwnerProcessID;
internal readonly UInt32 tpBasePri;
internal readonly UInt32 tpDeltaPri;
internal readonly UInt32 dwFlags;
[StructLayout(LayoutKind.Sequential)]
public struct ThreadContext
public uint ContextFlags;
public FLOATING_SAVE_AREA FloatSave;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
public byte[] ExtendedRegisters;
public HandleHookCallback Callback;
public HookRegister Register;