hi all! first of all i want to thank all the people from this section of the forum! 2 weeks ago i tried to work with memory for the first time and now I have achieved a lot, thanks to the messages that have already been. i was able to read the memory of the game, partially learned how to work with a dump, my bot has R\D in its arsenal! i'm happy as a child, but i'm having problems writing to memory. Before creating this post, i read the following posts:
1) __fastcall with C# using a C++ wrapper?
2) Reversing CastSpell
3) https://drewkestell.us/Article/6/Chapter/5
i took most of the code for writing memory from these sources. as for me i was able to inject DLL. my program starts in the process of WoW. i go for my character. then, i try in the main thread to call the method for getting GUID of my character. at this point the game client freezes and execution stops. as I understood, in order to write to memory, I need to perform the following steps:
1) Inject DLL
Code:
static void Main()
{
var currentFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var startupInfo = new STARTUPINFO();
CreateProcess(
"C:\\Games\\World of Warcraft\\_classic_\\WowClassic.exe",
null,
IntPtr.Zero,
IntPtr.Zero,
false,
ProcessCreationFlag.CREATE_DEFAULT_ERROR_MODE,
IntPtr.Zero,
null,
ref startupInfo,
out PROCESS_INFORMATION processInfo);
Thread.Sleep(1000);
var processHandle = Process.GetProcessById((int)processInfo.dwProcessId).Handle;
var loaderPath = Path.Combine(currentFolder, "Loader.dll");
var loaderPathPtr = VirtualAllocEx(
processHandle,
(IntPtr)0,
loaderPath.Length,
MemoryAllocationType.MEM_COMMIT,
MemoryProtectionType.PAGE_EXECUTE_READWRITE);
Thread.Sleep(500);
int error = Marshal.GetLastWin32Error();
if (error > 0)
throw new InvalidOperationException($"Failed to allocate memory for Loader.dll, error code: {error}");
var bytes = Encoding.Unicode.GetBytes(loaderPath);
var bytesWritten = 0; // throw away
WriteProcessMemory(processHandle, loaderPathPtr, bytes, bytes.Length, ref bytesWritten);
Thread.Sleep(1000);
error = Marshal.GetLastWin32Error();
if (error > 0 || bytesWritten == 0)
throw new InvalidOperationException($"Failed to write Loader.dll into the WoW.exe process, error code: {error}");
var loaderDllPointer = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryW");
Thread.Sleep(1000);
error = Marshal.GetLastWin32Error();
if (error > 0)
throw new InvalidOperationException($"Failed to get memory address to Loader.dll in the WoW.exe process, error code: {error}");
CreateRemoteThread(processHandle, (IntPtr)null, (IntPtr)0, loaderDllPointer, loaderPathPtr, 0, (IntPtr)null);
Thread.Sleep(1000);
error = Marshal.GetLastWin32Error();
if (error > 0)
throw new InvalidOperationException($"Failed to create remote thread to start execution of Loader.dll in the WoW.exe process, error code: {error}");
VirtualFreeEx(processHandle, loaderPathPtr, 0, MemoryFreeType.MEM_RELEASE);
}
2) In main thread Get delegate for function pointer and execute it.
Code of ThreadSynchronizer:
Code:
static public class ThreadSynchronizer
{
[DllImport("user32.dll")]
static extern IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
[DllImport("user32.dll")]
static extern int CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
[DllImport("user32.dll")]
static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll")]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll")]
static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll")]
static extern int SendMessage(
int hWnd,
uint Msg,
int wParam,
int lParam
);
[DllImport("kernel32.dll")]
static extern uint GetCurrentThreadId();
delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
delegate int WindowProc(IntPtr hWnd, int Msg, int wParam, int lParam);
static readonly Queue<Action> actionQueue = new Queue<Action>();
static readonly Queue<Delegate> delegateQueue = new Queue<Delegate>();
static readonly Queue<object> returnValueQueue = new Queue<object>();
const int GWL_WNDPROC = -4;
const int WM_USER = 0x0400;
static IntPtr oldCallback;
static WindowProc newCallback;
static int windowHandle;
static ThreadSynchronizer()
{
EnumWindows(FindWindowProc, IntPtr.Zero);
newCallback = WndProc;
oldCallback = SetWindowLong((IntPtr)windowHandle, GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newCallback));
}
static public void RunOnMainThread(Action action)
{
if (GetCurrentThreadId() == System.Diagnostics.Process.GetCurrentProcess().Threads[0].Id)
{
action();
return;
}
actionQueue.Enqueue(action);
SendUserMessage();
}
static public T RunOnMainThread<T>(Func<T> function)
{
if (GetCurrentThreadId() == System.Diagnostics.Process.GetCurrentProcess().Threads[0].Id)
return function();
delegateQueue.Enqueue(function);
SendUserMessage();
return (T)returnValueQueue.Dequeue();
}
static int WndProc(IntPtr hWnd, int msg, int wParam, int lParam)
{
try
{
if (msg != WM_USER) return CallWindowProc(oldCallback, hWnd, msg, wParam, lParam);
while (actionQueue.Count > 0)
actionQueue.Dequeue()?.Invoke();
while (delegateQueue.Count > 0)
{
var invokeTarget = delegateQueue.Dequeue();
returnValueQueue.Enqueue(invokeTarget?.DynamicInvoke());
}
return 0;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return CallWindowProc(oldCallback, hWnd, msg, wParam, lParam);
}
static bool FindWindowProc(IntPtr hWnd, IntPtr lParam)
{
GetWindowThreadProcessId(hWnd, out int procId);
if (procId != System.Diagnostics.Process.GetCurrentProcess().Id) return true;
if (!IsWindowVisible(hWnd)) return true;
var l = GetWindowTextLength(hWnd);
if (l == 0) return true;
var builder = new StringBuilder(l + 1);
GetWindowText(hWnd, builder, builder.Capacity);
if (builder.ToString() == "World of Warcraft")
windowHandle = (int)hWnd;
return true;
}
static void SendUserMessage() => SendMessage(windowHandle, WM_USER, 0, 0);
}
Code of delegete for function:
Code:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate UInt128 GetPlayerGuidDelegate();
static readonly GetPlayerGuidDelegate GetPlayerGuidFunction =
Marshal.GetDelegateForFunctionPointer<GetPlayerGuidDelegate>((IntPtr)0x2DAFCE0);
static public UInt128 GetPlayerGuid()
{
MessageBox.Show(GetPlayerGuidFunction().ToString());
return GetPlayerGuidFunction();
}
how do i run on the main thread:
Code:
ThreadSynchronizer.RunOnMainThread(() => Functions.GetPlayerGuid());