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#.







