# Forum > World of Warcraft > World of Warcraft Bots and Programs > WoW Memory Editing >  [WoW][C#]RemoteThreadHijack (alternative to BlackMagic's InjectAndExecute)

## vulcanaoc

The full source code (which uses remote thread hijacking to call one of WoW's functions) can be found near the bottom of this post.

*What?*
*Remote thread hijacking* is what I am dubbing the process of redirecting a thread's execution in a remote process in order to execute some (temporary) code.

In regards to WoW, this can be used as a functional equivalent to BlackMagic's InjectAndExecute. Whereas InjectAndExecute relies upon CreateRemoteThread, remote thread hijacking relies upon Get/SetThreadContext.

*Why?*
The main motive to using this technique is to naturally bring about thread ..nirvana.. when *executing WoW's code from out-of-process.* Instantly, this removes the need for any TLS-handling code. (CreateRemoteThread requires TLS-handling)

*Code & Analysis*
(Warning: in this demo, I use some poor coding practices.)

We need to do a few things first, namely: open WoW and its main thread. I will not talk in detail about this here, but it is in the source.

Moving on, we need a generic way to call code on WoW's main thread using remote thread hijacking. In my code, this method is embedded in a class that knows about the Process and MainThread on which code must run. Hence, the method signature:


```
public uint RemoteThreadHijackToExecuteAsm(string fnInAsm)
```

It takes one parameter, the assembly string to inject and execute, and returns a uint, which will be either the return value of the executed assembly string OR 0xDEADBEEF if it failed.

The next step is to gather information about the thread and suspend it after we allocate memory for our code.


```
uint lpAsmStub, returnValue = 0, initialReturnValue = 0xDEADBEEF;
CONTEXT ctx;
lpAsmStub = Win32Wrapper.AllocateMemory(_client.ProcessHandle);

if (SThread.SuspendThread(_client.MainThreadHandle) != uint.MaxValue)
{
    ctx = SThread.GetThreadContext(_client.MainThreadHandle, CONTEXT_FLAGS.CONTEXT_CONTROL);
```

You must suspend the thread before calling GetThreadContext, as the context will change when the thread is running.

Now that the thread is suspended, we can inject our code snippet before executing it.


```
//This is for holding a return values
                    _client.Fasm.AddLine("lpReturnValue dd 0x{0:X}", initialReturnValue);

                    //This is what gets executed
                    _client.Fasm.AddLine("push 0x{0:X}", ctx.Eip);
                    _client.Fasm.AddLine("pushfd");
                    _client.Fasm.AddLine("pushad");
                    _client.Fasm.AddLine(fnInAsm);
                    _client.Fasm.AddLine("mov [lpReturnValue], eax");
                    _client.Fasm.AddLine("popad");
                    _client.Fasm.AddLine("popfd");
                    _client.Fasm.AddLine("retn");

                    _client.Fasm.Inject(lpAsmStub);
                    _client.Fasm.Clear();
```

As you can see, we need to keep the thread in the same state that it was in when it suspended, which is why we are calling pushad/popad and pushfd/popfd (which essentially save the registers and flags.)

Now we begin to set up the thread for execution!



```
ctx.ContextFlags = CONTEXT_FLAGS.CONTEXT_CONTROL;
                    ctx.Eip = lpAsmStub + 4; //skip over lpReturnValue data

                    if (SThread.SetThreadContext(_client.MainThreadHandle, ctx))
                    {
                        if (SThread.ResumeThread(_client.MainThreadHandle) != uint.MaxValue)
                        {
```

And we wait for it to return before cleaning up the memory we allocated for our code.



```
for (int i = 0; i < 2000; i++) //wait for up to 2 seconds for the function to return
                            {
                                if ((returnValue = _client.ReadUInt(lpAsmStub)) != initialReturnValue)
                                    break;
                                System.Threading.Thread.Sleep(1);
                            }
                        }
                    }
                }
            }

            Win32Wrapper.FreeMemory(_client.ProcessHandle, lpAsmStub);

            return returnValue;
        }
```

Download Full Source

*Credits*
Shynd, for writing BlackMagic, ManagedFasm, and much of the code referenced by this demo. Oh, and for the general idea too!

-------------------------------------
Hope someone finds this useful

----------


## Cypher

I can see a few problems.

Beside the obvious portability issues of course....

First off


```
                            for (int i = 0; i < 2000; i++) //wait for up to 2 seconds for the function to return
                            {
                                if ((returnValue = _client.ReadUInt(lpAsmStub)) != initialReturnValue)
                                    break;
                                System.Threading.Thread.Sleep(1);
                            }
```

WHY GOD WHY!  :Stick Out Tongue: 

Please, for the love of god take a look at WaitForSingleObject. You can use that to wait until a thread finishes executing.

Second, you're not resuming the thread if for example GetThreadContext fails, or SetThreadContext fails.

Third, you should be comparing the return value of SuspendThread to '-1', not uint.MaxValue. Whilst they are the same thing at the binary level they're not the same thing at the source level and using -1 makes your intentions clear when someone is checking your usage of the function against the documentation. It's just good practice.

In the coming days I'll be posting something similar to this, except slightly more advanced. If you're interested in some overall ways you could improve the concept then you should keep a look out.  :Wink: 
(Mine is based off the implementation of RtlRemoteThread, an undocumented Windows API exported by NTDLL)

----------


## Viano

So I could use this method for executing DoString and GetLocalizedText without WoW crashing whole the time and without injecting DLLs? 

Well the following code is working, my toon is dancing (DoEmote("dance") and GetLocalizedText seem to work too. Please evaluate my code and tell me what is missing, should be better (remember I am still learning things).



```
public String GetLocalizedText(string variable)
        {
            uint lpAsmStub, returnValue = 0, initialReturnValue = 0xDEADBEEF;
            Magic.CONTEXT ctx;

            lpAsmStub = Win32Wrapper.AllocateMemory(magic.ProcessHandle);
            // my dirty addition
            ProcessThread wowMainThread = SThread.GetMainThread(magic.ProcessId);
            IntPtr hThread = SThread.OpenThread(wowMainThread.Id);
            // --
            if (SThread.SuspendThread(hThread) != uint.MaxValue)
            {
                // modified SThread
                ctx = SThread.GetThreadContext(hThread, CONTEXT_FLAGS.CONTEXT_CONTROL);
                // --
                if (ctx.Eip > 0)
                {
                    try
                    {
                        //This is for holding a return values
                        magic.Asm.AddLine("lpReturnValue dd 0x{0:X}", initialReturnValue);

                        //This is what gets executed
                        magic.Asm.AddLine("push 0x{0:X}", ctx.Eip);
                        magic.Asm.AddLine("pushfd");
                        magic.Asm.AddLine("pushad");
                        // my asm code
                        // magic.Asm.AddLine(fnInAsm);
                        uint codecave = magic.AllocateMemory();
                        ulong playerGuid = magic.ReadUInt64(
                        manager + (uint)Globals.Constants.Offsets.LOCAL_GUID);
                        uint playerPointer = Globals.ObjectManager.Instance.GetObjectByGuid(playerGuid);
                        magic.WriteASCIIString(codecave + 0x100, variable);

                        magic.Asm.AddLine("mov ecx, {0}", playerPointer);
                        magic.Asm.AddLine("push {0}", -1);
                        magic.Asm.AddLine("push {0}", codecave + 0x100);
                        magic.Asm.AddLine("call {0}", (uint)Globals.Constants.LuaFunctions.GET_LOCALIZED_TEXT);

                        // ---
                        magic.Asm.AddLine("mov [lpReturnValue], eax");
                        magic.Asm.AddLine("popad");
                        magic.Asm.AddLine("popfd");
                        magic.Asm.AddLine("retn");

                        magic.Asm.Inject(lpAsmStub);
                        magic.Asm.Clear();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);

                        Win32Wrapper.FreeMemory(magic.ProcessHandle, lpAsmStub);
                        SThread.ResumeThread(hThread);
                        return "";
                    }

                    ctx.ContextFlags = CONTEXT_FLAGS.CONTEXT_CONTROL;
                    ctx.Eip = lpAsmStub + 4; //skip over lpReturnValue data

                    if (SThread.SetThreadContext(hThread, ctx))
                    {
                        if (SThread.ResumeThread(hThread) != uint.MaxValue)
                        {
                            // WHY GOD WHY ... I KNOW ...
                            for (int i = 0; i < 2000; i++) //wait for up to 2 seconds for the function to return
                            {
                                if ((returnValue = magic.ReadUInt(lpAsmStub)) != initialReturnValue)
                                    break;
                                Thread.Sleep(1);
                            }
                        }
                    }
                }
            }

            Win32Wrapper.FreeMemory(magic.ProcessHandle, lpAsmStub);

            String sResult = magic.ReadASCIIString(returnValue, 256);
            return sResult;
        }

        public uint DoString(string command)
        {
            uint lpAsmStub, returnValue = 0, initialReturnValue = 0xDEADBEEF;
            Magic.CONTEXT ctx;

            lpAsmStub = Win32Wrapper.AllocateMemory(magic.ProcessHandle);
            // my dirty addition
            ProcessThread wowMainThread = SThread.GetMainThread(magic.ProcessId);
            IntPtr hThread = SThread.OpenThread(wowMainThread.Id);
            // --
            if (SThread.SuspendThread(hThread) != uint.MaxValue)
            {
                // modified SThread
                ctx = SThread.GetThreadContext(hThread, CONTEXT_FLAGS.CONTEXT_CONTROL);
                // --
                if (ctx.Eip > 0)
                {
                    try
                    {
                        //This is for holding a return values
                        magic.Asm.AddLine("lpReturnValue dd 0x{0:X}", initialReturnValue);

                        //This is what gets executed
                        magic.Asm.AddLine("push 0x{0:X}", ctx.Eip);
                        magic.Asm.AddLine("pushfd");
                        magic.Asm.AddLine("pushad");
                        // my asm code
                        // magic.Asm.AddLine(fnInAsm);
                        uint space = magic.AllocateMemory(command.Length + 1);
                        magic.WriteASCIIString(space, command);

                        magic.Asm.AddLine("mov ecx, 0");
                        magic.Asm.AddLine("mov eax, " + space);
                        magic.Asm.AddLine("push ecx");
                        magic.Asm.AddLine("push eax");
                        magic.Asm.AddLine("push eax");
                        magic.Asm.AddLine("mov eax, " + (uint)Globals.Constants.LuaFunctions.DO_STRING);
                        magic.Asm.AddLine("call eax");
                        magic.Asm.AddLine("add esp, 0xC");

                        // ---
                        magic.Asm.AddLine("mov [lpReturnValue], eax");
                        magic.Asm.AddLine("popad");
                        magic.Asm.AddLine("popfd");
                        magic.Asm.AddLine("retn");

                        magic.Asm.Inject(lpAsmStub);
                        magic.Asm.Clear();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);

                        Win32Wrapper.FreeMemory(magic.ProcessHandle, lpAsmStub);
                        SThread.ResumeThread(hThread);
                        return 0;
                    }

                    ctx.ContextFlags = CONTEXT_FLAGS.CONTEXT_CONTROL;
                    ctx.Eip = lpAsmStub + 4; //skip over lpReturnValue data

                    if (SThread.SetThreadContext(hThread, ctx))
                    {
                        if (SThread.ResumeThread(hThread) != uint.MaxValue)
                        {
                            // WHY GOD WHY ... I KNOW ...
                            for (int i = 0; i < 2000; i++) //wait for up to 2 seconds for the function to return
                            {
                                if ((returnValue = magic.ReadUInt(lpAsmStub)) != initialReturnValue)
                                    break;
                                Thread.Sleep(1);
                            }
                        }
                    }
                }
            }

            Win32Wrapper.FreeMemory(magic.ProcessHandle, lpAsmStub);

            return returnValue;
        }
```

I love you vulcanaoc. Cannot +REP you anymore  :Wink: 

*EDIT:*
Strange thing is DoString works if I test it at the beginning of my application. But it ALWAYS crashes at "ResumeThread(IntPtr hThread)" when executing somewhere in the middle. Any ideas why? It's like calling something totally different.

What can I possibly be doing "wrong" during execution of my program that it causes DoString() from above to crash WoW and why does that work at the beginning of it?
Is it possible to check whether some other part of my code is doing something with WoW's main thread so that I should wait? Something like GetThreadStatus?

----------


## Kryso

I'm actually working with same method. However I'm trying to make it more dotnetty  :Smile:  Gonna publish it in like week or so, still have some things to figure out.

----------


## amadmonk

Um, this won't save you from the need to inject DLL's any more than CreateRemoteThread does. You still need to have code to execute in the remote process, and there are only two real choices for that; inject a dll and execute that, or create a code cave and execute that.

----------


## Viano

> Um, this won't save you from the need to inject DLL's any more than CreateRemoteThread does. You still need to have code to execute in the remote process, and there are only two real choices for that; inject a dll and execute that, or create a code cave and execute that.


Mhm, so I guess I have trouble working with threads somewhere in my application. As stated before DoString() is working 1000 times if I execute it at the beginning of my application and fails in the middle of it. But what can possibly go wrong?

Sigh ... if only someone could take a look at my code and tell me what I am missing ...

----------


## amadmonk

Sounds like either a timing issue or a memory leak. Hope for a memory leak; you can actually FIX those...

----------


## Viano

> Sounds like either a timing issue or a memory leak. Hope for a memory leak; you can actually FIX those...


What do you mean by a memory leak? How do you identify those?

----------


## amadmonk

Well, I'm just going off the basis of the behavior changing over time, particularly becoming more flaky after more calls -- to me this sounds like a memory leak (for instance, perhaps you're forgetting to deallocate a code cave or something). Eventually you'd start doing bad things (exhausting your heap or fragmenting your VA space or whatever).

I may be way off base -- can't be sure just from a description of the issue. But double check to make sure that you free every piece of memory that you allocate (even in exception cases, etc.) I'm sure Cypher would have more to say on the topic  :Wink:

----------


## Viano

> Well, I'm just going off the basis of the behavior changing over time, particularly becoming more flaky after more calls -- to me this sounds like a memory leak (for instance, perhaps you're forgetting to deallocate a code cave or something). Eventually you'd start doing bad things (exhausting your heap or fragmenting your VA space or whatever).
> 
> I may be way off base -- can't be sure just from a description of the issue. But double check to make sure that you free every piece of memory that you allocate (even in exception cases, etc.) I'm sure Cypher would have more to say on the topic


It was my GetUnitName function. Now I am using VMT to get it and everything seems fine. Bot is running over an 2 hours now  :Wink:  Thank you amadmonk fo your tips and the patience.

@Cypher: can you please show me how to use WaitForSingleObject in this particular code example? My thread seems to run for ages and is not returning.

----------


## violentmagician

umm mate i tired this code... it seems that


your code deallocates just the asm stub
the codecave is not deallocated causing a memory leak.





> ```
> lpAsmStub = Win32Wrapper.AllocateMemory(magic.ProcessHandle);
> ...
> uint codecave = magic.AllocateMemory();
> ...
> Win32Wrapper.FreeMemory(magic.ProcessHandle, lpAsmStub);
> ```


as such there is a memory leak.
just deallocate codecave with


```
 magic.FreeMemory(codecave);
```

the code will then not crash after multiple runs.

----------


## vulcanaoc

> umm mate i tired this code... it seems that
> 
> 
> your code deallocates just the asm stub
> the codecave is not deallocated causing a memory leak.
> 
> 
> 
> as such there is a memory leak.
> ...


You're talking to Viano, right?

----------


## Viano

> ...
> just deallocate codecave with
> 
> 
> ```
>  magic.FreeMemory(codecave);
> ```
> 
> ...


Yep that was a memory leak. But still, after freeing memory my WoW crashes.



```
public String HijackGetLocalizedText(string variable)
        {
            uint lpAsmStub, returnValue = 0, initialReturnValue = 0xDEADBEEF,
            // --- alloc memory    
                codecave, playerPointer;
            codecave = _magic.AllocateMemory();
            playerPointer = ObjectManager.Instance.Player.Pointer;
            String sResult = "null";
            // --- and vars

            Magic.CONTEXT ctx;

            lpAsmStub = Win32Wrapper.AllocateMemory(_magic.ProcessHandle);

            // --- getting main handle
            ProcessThread wowMainThread = SThread.GetMainThread(_magic.ProcessId);
            IntPtr hThread = SThread.OpenThread(wowMainThread.Id);
            // ---

            if (SThread.SuspendThread(hThread) != uint.MaxValue)
            {
                ctx = SThread.GetThreadContext(hThread, CONTEXT_FLAGS.CONTEXT_CONTROL);
                if (ctx.Eip > 0)
                {
                    try
                    {
                        //This is for holding a return values
                        _magic.Asm.AddLine("lpReturnValue dd 0x{0:X}", initialReturnValue);

                        //This is what gets executed
                        _magic.Asm.AddLine("push 0x{0:X}", ctx.Eip);
                        _magic.Asm.AddLine("pushfd");
                        _magic.Asm.AddLine("pushad");

                        // --- asm code for GetLocalizedText
                        
                        _magic.WriteASCIIString(codecave + 0x100, variable);

                        _magic.Asm.AddLine("mov ecx, {0}", playerPointer);
                        _magic.Asm.AddLine("push {0}", -1);
                        _magic.Asm.AddLine("push {0}", codecave + 0x100);
                        _magic.Asm.AddLine("call {0}", (uint)Constants.LuaFunctions.GET_LOCALIZED_TEXT);

                        // --- asm code

                        _magic.Asm.AddLine("mov [lpReturnValue], eax");
                        _magic.Asm.AddLine("popad");
                        _magic.Asm.AddLine("popfd");
                        _magic.Asm.AddLine("retn");

                        _magic.Asm.Inject(lpAsmStub);
                        _magic.Asm.Clear();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);

                        Win32Wrapper.FreeMemory(_magic.ProcessHandle, lpAsmStub);
                        // --- free memory
                        _magic.FreeMemory(codecave);
                        SThread.ResumeThread(hThread);
                        return sResult;
                    }

                    ctx.ContextFlags = CONTEXT_FLAGS.CONTEXT_CONTROL;
                    ctx.Eip = lpAsmStub + 4; //skip over lpReturnValue data

                    if (SThread.SetThreadContext(hThread, ctx))
                    {
                        if (SThread.ResumeThread(hThread) != uint.MaxValue)
                        {
                            for (int i = 0; i < 2000; i++) //wait for up to 2 seconds for the function to return
                            {
                                if ((returnValue = _magic.ReadUInt(lpAsmStub)) != initialReturnValue)
                                    break;
                                System.Threading.Thread.Sleep(1);
                            }
                        }
                    }
                }
            }

            Win32Wrapper.FreeMemory(_magic.ProcessHandle, lpAsmStub);
            // --- free memory
            _magic.FreeMemory(codecave);

            sResult = _magic.ReadASCIIString(returnValue, 256);
            return sResult;
        }
```

EDIT:
Strange thing is DoString works o0.

----------

