Since I'm most likely leaving the scene of making these tools (apart from my macro if anyone is interested.), I'll be releasing a library that can be used to bypass all of GameGuard's protection methods on x64 Win7+.
I've document the 2 files in there as much as I could.
The source code also contains a rather useful header containing some native functions and some custom C and NT/WIN API functions.
Note that you need to compile in x86, I haven't tested it in x64 and most likely something will go wrong.
So this means that you need your AutoIt, C# or whatever other compiler to compile for x86 otherwise LdrLoadDll/LoadLibraryA won't return a handle.
Edit: I'll make a x64 version of this soon.
Edit 2: I forgot that VS Inline ASM does not support x64, so nevermind, 64bit wont be around.
If you don't want to compile the dll, you can go to Release/ and pick it up there.
The exported functions are:
- extCloseHandle
- extGetCursorPos
- extGetProcessActive
- extGetProcessBaseAddress
- extGetProcessImageSize
- extGetWindowThreadProcessId
- extOpenProcess
- extPostMessageA/W
- extSendMessageA/W
- extWriteMemory
- extReadMemory
- extGetModuleHandleA/W
Note that I called this DLL "GameGuard" but well that doesn't really matter at all. I'm not very creative.
Quick and simple example use for C# coming up.
Import the library exports.
Code:
[DllImport("GameGuard.dll")] private static extern bool extSendMessageA(IntPtr hWnd, uint msg, uint wParam, IntPtr lParam); [DllImport("GameGuard.dll")] private static extern IntPtr extOpenProcess(uint processId); [DllImport("GameGuard.dll")] private static extern bool extReadMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesRead); [DllImport("GameGuard.dll")] private static extern bool extWriteMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, int nSize, out IntPtr lpNumberOfBytesWritten); [DllImport("GameGuard.dll")] private static extern IntPtr extGetProcessBaseAddress(IntPtr hProcess); [DllImport("GameGuard.dll")] private static extern IntPtr extGetProcessImageSize(IntPtr hProcess); [DllImport("GameGuard.dll")] private static extern bool extCloseHandle(IntPtr hObject); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int MapVirtualKey(int uCode, int uMapType); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int VkKeyScan(char ch);
Code:
public static class Defines { public static IntPtr AddressTargetBase = new IntPtr(0xD00C84); /* VA */ public static IntPtr AddressCharacterBase = new IntPtr(0xD00B14); /* VA */ public static IntPtr LocalBar = new IntPtr(0x5412B0); /* RVA */ }
Code:
private static string GetString(byte[] buffer) { if (buffer.Length == 0 || buffer[0] == '\0') { return string.Empty; } var strBuilder = new StringBuilder(); foreach (var b in buffer) { if (b == '\0') { break; } strBuilder.Append((char)b); } return strBuilder.ToString(); } /* gets the VK equivalent of the '~' key. */ private static byte GetUpKey() { return (byte)MapVirtualKey(0x29, 1); } private static byte[] ToByteArray(object value, uint maxLength) { int rawsize = Marshal.SizeOf(value); byte[] rawdata = new byte[rawsize]; GCHandle gcHandle; if ((gcHandle = GCHandle.Alloc(rawdata, GCHandleType.Pinned)) == null) { throw new Exception("Not enough RAM memory available."); } Marshal.StructureToPtr(value, gcHandle.AddrOfPinnedObject(), false); gcHandle.Free(); if (maxLength < rawdata.Length) { byte[] temp = new byte[maxLength]; Array.Copy(rawdata, temp, maxLength); return temp; } else { return rawdata; } }
Code:
private static T Read<T>(IntPtr hProcess, IntPtr dwBaseAddress, int[] offsets = null, bool IsRVA = false) { int bytesRead; object ret; var size = typeof(T) != typeof(string) ? (uint)Marshal.SizeOf(typeof(T)) : 30; if (offsets == null) { offsets = new int[0]; } if (IsRVA) { IntPtr ImageBaseAddress; if ((ImageBaseAddress = extGetProcessBaseAddress(hProcess)) == IntPtr.Zero) { return default(T); } dwBaseAddress = IntPtr.Add(dwBaseAddress, (int)ImageBaseAddress); } var buffer = new byte[size]; extReadMemory(hProcess, dwBaseAddress, buffer, size, out bytesRead); foreach (int offset in offsets) { dwBaseAddress = new IntPtr(BitConverter.ToInt32(buffer, 0)); extReadMemory(hProcess, dwBaseAddress + offset, buffer, size, out bytesRead); } switch (Type.GetTypeCode(typeof(T))) { case TypeCode.Boolean: ret = BitConverter.ToBoolean(buffer, 0); break; case TypeCode.Char: ret = BitConverter.ToChar(buffer, 0); break; case TypeCode.Byte: ret = buffer[0]; break; case TypeCode.Int16: ret = BitConverter.ToInt16(buffer, 0); break; case TypeCode.UInt16: ret = BitConverter.ToUInt16(buffer, 0); break; case TypeCode.Int32: ret = BitConverter.ToInt32(buffer, 0); break; case TypeCode.UInt32: ret = BitConverter.ToUInt32(buffer, 0); break; case TypeCode.Int64: ret = BitConverter.ToInt64(buffer, 0); break; case TypeCode.UInt64: ret = BitConverter.ToUInt64(buffer, 0); break; case TypeCode.Single: ret = BitConverter.ToSingle(buffer, 0); break; case TypeCode.Double: ret = BitConverter.ToDouble(buffer, 0); break; case TypeCode.String: ret = GetString(buffer); break; default: throw new NotSupportedException( $"{typeof(T).FullName} is not currently supported by this function."); } return (T)ret; }
Code:
private static bool Write<T>(T value, IntPtr hProcess, IntPtr dwBaseAddress, int[] offsets, bool IsRVA = false) { int bytesRead; if (IsRVA) { IntPtr ImageBaseAddress; if ((ImageBaseAddress = extGetProcessBaseAddress(hProcess)) == IntPtr.Zero) { return false; } dwBaseAddress = IntPtr.Add(dwBaseAddress, (int)ImageBaseAddress); } var buffer = new byte[4]; extReadMemory(hProcess, dwBaseAddress, buffer, 4, out bytesRead); for (int i = 0; i < offsets.Length - 1; i++) { dwBaseAddress = new IntPtr(BitConverter.ToInt32(buffer, 0) + offsets[i]); extReadMemory(hProcess, dwBaseAddress, buffer, 4, out bytesRead); } dwBaseAddress = new IntPtr(BitConverter.ToInt32(buffer, 0) + offsets[offsets.Length - 1]); var size = (uint)Marshal.SizeOf(typeof(T)); byte[] val = ToByteArray(value, size); IntPtr lpBytesWritten; bool retn = extWriteMemory(hProcess, dwBaseAddress, val, val.Length, out lpBytesWritten); return retn; }
Code:
public static void Main(string[] args) { var alefClients = Process.GetProcessesByName("alefclient"); if (alefClients.Length == 0) { Console.WriteLine("Archlord is not running."); return; } var client = alefClients[0]; IntPtr windowHandle = client.MainWindowHandle; if (windowHandle == IntPtr.Zero) { Console.WriteLine($"Could not get window handle of {client.Id}"); return; } IntPtr hProcess; if ((hProcess = extOpenProcess((uint)client.Id)) == IntPtr.Zero) { Console.WriteLine($"Could not open the process {client.Id}"); return; } IntPtr ImageSize; if ((ImageSize = extGetProcessImageSize(hProcess)) == IntPtr.Zero) { Console.WriteLine($"Could not get image size {client.Id}"); extCloseHandle(hProcess); return; } IntPtr ImageBaseAddress; if ((ImageBaseAddress = extGetProcessBaseAddress(hProcess)) == IntPtr.Zero) { Console.WriteLine($"Could not get image base address {client.Id}"); extCloseHandle(hProcess); return; } int currentBarIndex = Read<int>(hProcess, Defines.LocalBar, new[] { 0x1440 }, true); Console.WriteLine($"Current bar number: {currentBarIndex + 1}."); /* let's go up 1 bar */ byte keyUp = GetUpKey(); extSendMessageA(windowHandle, 0x100, keyUp, new IntPtr((MapVirtualKey(keyUp, 0) << 16) & 0x00FF0000)); /* wait a bit */ Thread.Sleep(50); currentBarIndex = Read<int>(hProcess, Defines.LocalBar, new[] { 0x1440 }, true); Console.WriteLine($"new bar number: {currentBarIndex + 1}."); /* we can set back the number through memory, however this wont update it graphically */ bool success = Write(currentBarIndex - 1, hProcess, Defines.LocalBar, new[] { 0x1440 }, true); if (success) { currentBarIndex = Read<int>(hProcess, Defines.LocalBar, new[] { 0x1440 }, true); Console.WriteLine($"We're back on bar {currentBarIndex + 1}."); } else { Console.Write($"Couldn't write the memory."); } string targetName = Read<string>(hProcess, Defines.AddressTargetBase, new[] { 0xca4, 0x5c, 0x54, 0x74, 0xb8, 0x0 }); if (targetName == string.Empty) { /* monsters have this set to null so lets grab their name */ targetName = Read<string>(hProcess, Defines.AddressTargetBase, new[] { 0xc94, 0x34, 0x30 }); } if (targetName != string.Empty) { Console.WriteLine($"Your target's name is: {targetName}"); } else { Console.WriteLine("You have no target."); } /* cleanup */ extCloseHandle(hProcess); }
For C/C++ you can just take a look at the source code and do whatever you like.
That'll be all, have fun; hope somebody makes tools out of this.
Update: added a nicer example for c#.