Bloog Bot

Chapter 10
Drew Kestell, 2018
[email protected]

A Brief Detour

The next thing we should do is give our bot the ability to move around. The most common technique for making bots move hooks into the Click-To-Move function that's built into the vanilla WoW client. I didn't even know Click-To-Move existed until I started playing around with botting way later on. Essentially CTM lets you move your character by right-clicking a spot on the ground. We're also going to implement more traditional movement (WASD style) down the road, but CTM is what we'll use 95% of the time, so let's do that first.

CTM in the early versions of the WoW client has some strange issues related to framerate. If your framerate is higher than ~80 FPS, CTM doesn't work (see this thread at OwnedCore for more info). If your monitor is set to 60hz refresh rate, and you enable vSync, you won't run into any problems. I'm working with a monitor that has a 144hz refresh rate, and I'd prefer not to lower my refresh rate. Thankfully there's another solution. What we're going to do is artificially throttle the framerate of the WoW client by hooking the DirectX EndScene function. There are definitely easier solutions to this problem, and this may seem heavy-handed, but when we start talking about Warden down the road, function detouring is a very important tool to have in our toolbox, so I thought it made sense to introduce it here. This solution also has performance implications, but modern computers should have no problem running the vanilla WoW client, so I'm not too concerned about this.

So what is hooking? The wikipedia article has some good information with examples, but the here's the gist. The WoW client's rendering engine uses DirectX. If you recall the earlier chapter on game loops, you'll remember that there are typically 3 things that happen every frame:

  1. Handle Input
  2. Update Simulation
  3. Render Scene

Step 3 is relevant to this discussion. We won't get into the nitty gritty of how DirectX renders a frame, but what's important is that DirectX has a function called EndScene that is called once per frame. So back to the previous question - "what is hooking"? The WoW client calls EndScene internally, so whenever WoW tries to call EndScene we're going to trick it into calling some other function that we've written. Here's the high level:

  1. Write our own EndSceneHook method that does some extra stuff, then calls the actual EndScene function
  2. Find the memory location of the EndScene function in the WoW process (there are actually multiple steps involved here)
  3. Write to the WoW process memory, replacing the address of EndScene with EndSceneHook

Step #2 is a bit tricky. The EndScene function is not always in the same memory location, but we do have a reliable way of finding it. There's a ISceneEnd function in the WoW client that calls EndScene internally, and thankfully, ISceneEnd is at a static memory address. So doing some familiar memory reading and pointer dereferencing, we can reliably find the address of EndScene at runtime. However the way we do this is a bit different than we've done it previously. When ISceneEnd is called internally by the WoW client's rendering engine, a pointer is passed as a parameter. The memory address of the EndScene function is at an offset from this pointer parameter. So we actually need to hook the ISceneEnd function with our own method that first sets a private variable as a pointer to the EndScene function. So let's modify our list of steps above to be a bit more specific:

  1. Find the memory location of the EndScene function:
    • Write custom ISceneEndHook method that will find the memory address of the EndScene function and set a private variable to that address, then unhook itself
    • Hook ISceneEnd function to detour to ISceneEndHook
  2. Write custom EndSceneHook method that will make sure frames don't render faster than ~16ms (~60fps)
  3. Hook EndScene function to detour to EndSceneHook using the pointer to EndScene that we found in step 1

I'm about to show a lot of new code. If this is confusing, I encourage you to spend some time studying the code, dissecting it and taking each chunk one line at a time. Drawing the flow of execution out on a piece of paper always helps me.

First - now that we're writing to memory, we need to give MemoryReader a new WriteBytes method. This new method uses WriteProcessMemory from kernel32.dll which we used before, so that should look familiar. It also bothers me that a class called MemoryReader also writes, so I'm going to rename that class to MemoryManager. I'm also changing the access modifier of the ReadBytes method from private to internal because, as you'll see, we need to use that method while hooking. Here's what MemoryManager looks like now:

unsafe class MemoryManager
{
    [DllImport("kernel32.dll")]
    internal static extern bool WriteProcessMemory(
        IntPtr hProcess,
        IntPtr lpBaseAddress,
        byte[] lpBuffer,
        int dwSize,
        ref int lpNumberOfBytesWritten);

    internal byte ReadByte(IntPtr address) => *(byte*)address;

    internal int ReadInt(IntPtr address) => *(int*)address;

    internal ulong ReadUlong(IntPtr address) => *(ulong*)address;

    internal IntPtr ReadIntPtr(IntPtr address) => *(IntPtr*)address;

    internal float ReadFloat(IntPtr address) => *(float*)address;

    internal string ReadString(IntPtr address)
    {
        var buffer = ReadBytes(address, 512);
        var ret = Encoding.ASCII.GetString(buffer);

        if (ret.IndexOf('\0') != -1)
            ret = ret.Remove(ret.IndexOf('\0'));

        return ret;
    }

    internal byte[] ReadBytes(IntPtr address, int count)
    {
        var ret = new byte[count];
        var ptr = (byte*)address;

        for (var i = 0; i < count; i++)
            ret[i] = ptr[i];

        return ret;
    }

    internal void WriteBytes(IntPtr address, byte[] bytes)
    {
        var process = Process.GetProcessesByName("WoW")[0].Handle;
        int ret = 0;
        WriteProcessMemory(process, address, bytes, bytes.Length, ref ret);
    }
}

And here's our new DirectXManager class that encapsulates all the hooking and throttling code (comments should help explain the high level):

public class DirectXManager
{
    [DllImport("kernel32.dll")]
    public static extern uint GetCurrentThreadId();

    const int I_SCENE_END_FUN_PTR = 0x005A17A0;

    readonly MemoryManager memoryManager;

    int lastFrameTick;
    int timeBetweenFrame;
    int waitTilNextFrame;

    Direct3D9EndScene endSceneOriginal;
    IntPtr endScenePtr;
    Direct3D9ISceneEnd iSceneEndDelegate;
    IntPtr target;
    List<byte> original;

    internal DirectXManager(MemoryManager memoryManager)
    {
        this.memoryManager = memoryManager;
    }

    // if frames are rendering faster than once every ~16ms (60fps), slow them down
    // this corrects an issue where ClickToMove doesn't work when your monitor has a refresh rate above ~80
    internal void ThrottleFPS()
    {
        GetEndScenePtr();
        endSceneOriginal = Marshal.GetDelegateForFunctionPointer<Direct3D9EndScene>(memoryManager.ReadIntPtr(endScenePtr));
        var endSceneDetour = new Direct3D9EndScene(EndSceneHook);

        var addrToDetour = Marshal.GetFunctionPointerForDelegate(endSceneDetour);
        var customBytes = BitConverter.GetBytes((int)addrToDetour);
        memoryManager.WriteBytes(endScenePtr, customBytes);
    }
    
    int EndSceneHook(IntPtr parDevice)
    {
        if (lastFrameTick != 0)
        {
            timeBetweenFrame = Environment.TickCount - lastFrameTick;
            if (timeBetweenFrame < 15)
            {
                var newCount = Environment.TickCount;
                waitTilNextFrame = 15 - timeBetweenFrame;
                newCount += waitTilNextFrame;
                while (Environment.TickCount < newCount) { }
            }
        }
        lastFrameTick = Environment.TickCount;

        return endSceneOriginal(parDevice);
    }
    
    void GetEndScenePtr()
    {
        iSceneEndDelegate = Marshal.GetDelegateForFunctionPointer<Direct3D9ISceneEnd>((IntPtr)I_SCENE_END_FUN_PTR);
        target = Marshal.GetFunctionPointerForDelegate(iSceneEndDelegate);
        var hook = Marshal.GetFunctionPointerForDelegate(new Direct3D9ISceneEnd(ISceneEndHook));

        // note the original bytes so we can unhook ISceneEnd after finding endScenePtr
        original = new List<byte>();
        original.AddRange(memoryManager.ReadBytes(target, 6));

        // hook ISceneEnd
        var detour = new List<byte> { 0x68 };
        var tmp = BitConverter.GetBytes(hook.ToInt32());
        detour.AddRange(tmp);
        detour.Add(0xC3);
        memoryManager.WriteBytes(target, detour.ToArray());

        // wait for ISceneEndHook to set endScenePtr
        while (endScenePtr == default(IntPtr))
            Task.Delay(3);
    }

    IntPtr ISceneEndHook(IntPtr ptr)
    {
        var ptr1 = memoryManager.ReadIntPtr(IntPtr.Add(ptr, 0x38A8));
        var ptr2 = memoryManager.ReadIntPtr(ptr1);
        endScenePtr = IntPtr.Add(ptr2, 0xa8);

        // unhook ISceneEnd
        memoryManager.WriteBytes(target, original.ToArray());

        return iSceneEndDelegate(ptr);
    }

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate int Direct3D9EndScene(IntPtr device);

    [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
    private delegate IntPtr Direct3D9ISceneEnd(IntPtr unk);
}

This is pretty hairy, but the consolation is that we won't really have to touch this class anymore. Let's take a look at some of the more interesting bits.

First take a look at the last two lines of the GetEndScenePtr method. Because ISceneEndHook is actually responsible for setting the endScenePtr variable, and ISceneEndHook is actually called internally by the WoW client's rendering engine once per frame, we actually need to wait for that variable to get set before letting GetEndScenePtr return. If not, line 40 in the ThrottleFPS method will execute and endScenePtr will be equal to IntPtr.Zero and it'll blow up.

Also notice the comment on lines 20-21. Knowing how the garbage collector works, if we were to make endSceneDetour a local variable on line 41, as soon as the ThrottleFPS method returned, that local variable would go out of scope and would be cleaned up the next time the garbage collector ran. We've detoured the WoW client to that endSceneDetour function, and if it's cleaned up by the garbage collector, its memory space would be returned to the operating system, and the next time that function was called we'd get a read/write error and the bot would crash. Making it a class field will prevent this from happening. As a side note - when we initialize the DirectXManager below in our MainViewModel class, the DirectXManager object goes out of scope as soon as the constructor returns, so I would assume we'd still run into issues with the garbage collector cleaning up the endSceneDetour delegate, but that doesn't seem to be the case. I'm not quite sure why - perhaps it's a quirk of how the garbage collector interacts with WPF UI code. I'll have to investigate that further.

Some of you may be wondering why the techniques we use to hook the two functions look so different. When we hook ISceneEnd we write 6 bytes to memory (0x68, the 4 bytes that represent the memory address of the ISceneEndHook method, then 0xC3). But when we hook EndScene we just write the 4 byte memory address of EndSceneHook. Why?

In the first case, we're using what's called the Push/Retn method (see Method II: Push/Return here for more info). 0x68 is the push instruction in assembly. It should be followed by a 4 byte memory address. 0xC3 is the retn instruction in assembly. The push instruction pushes the 4 byte memory address onto the stack, then the retn instruction pops a 4 byte memory address off the stack and into the Instruction Pointer which will start execution of the function found at the memory address that was provided between the push and retn instructions. If you look at the disassembled WoW client, this is what the instructions look like, so we must follow suit and write our hook this way.

In the case of the EndScene function hook - this function is actually stored in the DirectX virtual function table. We simply have to replace the memory address of the EndScene function stored in that table with EndSceneHook and we're in business. DirectX vtable hooking is actually a pretty common thing to do, so a Google search will yield plenty of good information if you want to learn more. Programs like Fraps hook DirectX for example.

This was pretty hard for me to wrap my head around at first, so if you find this tricky, it's totally fine to copy/paste the code for now and circle back to study it later. It's also possible to totally ignore this chapter if your machine doesn't suffer from the ClickToMove issue. You can also just turn the refresh rate on your monitor down.

Hooking will come up again when we talk about circumventing Warden. As an example, the WoW client has a function that scans your process's memory for any modified bytes. Certain hacks require you to modify the memory of the WoW process, but this is detectable by Warden. You can hook that memory scan function to first revert any modified bytes to their original values, then let the memory scan happen, then change the bytes back to their "hacked" values. Done successfully, Warden will send a clean report back to the server.

The last thing to do is add our new DirectXManager to the rest of our initialization code in the constructor of MainViewModel:

internal MainViewModel()
{
    var functions = new Functions();
    var memoryManager = new MemoryManager();
    objectManager = new ObjectManager(functions, memoryManager);

    // throttle framerate to fix ClickToMove on higher refresh rate monitors
    new DirectXManager(memoryManager).ThrottleFPS();

    ConsoleOutput = new ObservableCollection<string>();
}

That takes care of one issue. If you tried using ClickToMove before before hooking EndScene you'd have noticed that the player looks like he can't turn more than 5 degrees a second. After this fix, you should be able to issue a ClickToMove command anywhere around the player, and he'll move in that direction. But if you play around with ClickToMove you may notice that the player will frequently stop prematurely. This is another issue related to ClickToMove in early versions of the WoW client.

The fix for this second issue is quite easy to implement, but unfortunately I don't have an explanation for how or why it works. You can read a bit about it on this thread at OwnedCore, but there isn't an explanation of why it works. If you look at the decompiled WoW client, you'll see this memory location isn't actually accessed by any functions, so I have no idea what purpose it could serve.

All we have to do is write 0 to 4 bytes starting at memory address 0x860A90. Weird, huh? If anybody has a better explanation for this please let me know. Add some code to the constructor of MainViewModel:

public class MainViewModel : INotifyPropertyChanged
{
  const int CLICK_TO_MOVE_FIX = 0x860A90;

  ...

  public MainViewModel()
  {
    ...

    // enable ClickToMove fix
    memoryManager.WriteBytes((IntPtr)CLICK_TO_MOVE_FIX, new byte[] { 0, 0, 0, 0 });

    ...
  }

  ...
}

With both fixes in place, ClickToMove should work flawlessly. It's a pain in the ass to get it working, but our bot relies heavily on ClickToMove so it's worth the effort.

With all that out of the way, we can finally talk about implementing ClickToMove. While it's possible to manually initiate click to move by writing to 4 memory locations: CtmX, CtmY, CtmZ, and CtmType, the easiest way to implement ClickToMove is by calling the ClickToMove function in the WoW client. First create a new ClickType enum:

enum ClickType
{
    // we'll add more to this later
    Move = 0x4
}

Then add the new ClickToMove function to our Functions class:

// ClickToMove
const int CLICK_TO_MOVE_FUN_PTR = 0x00611130;

[StructLayout(LayoutKind.Sequential)]
struct XYZ
{
    internal float X;
    internal float Y;
    internal float Z;

    internal XYZ(Position position)
    {
        X = position.X;
        Y = position.Y;
        Z = position.Z;
    }
}

[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
delegate void ClickToMoveDelegate(
    IntPtr playerPtr, 
    ClickType clickType, 
    ref ulong interactGuidPtr, 
    ref XYZ xyzPtr, 
    float precision);

static ClickToMoveDelegate ClickToMoveFunction = 
    Marshal.GetDelegateForFunctionPointer<ClickToMoveDelegate>((IntPtr)CLICK_TO_MOVE_FUN_PTR);

internal void ClickToMove(IntPtr playerPtr, Position position)
{
    ulong interactGuidPtr = 0;
    var xyz = new XYZ(position);
    ClickToMoveFunction(playerPtr, ClickType.Move, ref interactGuidPtr, ref xyz, 2);
}

Most of this should look familiar by now. The internal ClickToMove function is at static memory address 0x00611130 so we register a delegate and wire things up. The only difference is that we're defining the new XYZ struct here. The WoW client's ClickToMove function requires this - so we'll simply convert our Position object to an XYZ object before calling the internal function. This function has 5 parameters:

  1. playerPtr - pointer to the local player object.
  2. clickType - there are a bunch of different clickTypes. I'm not sure what they all do. We only care about Move (4) for now.
  3. interactGuidPtr - not sure what this is. Pass a 0 by ref.
  4. xyzPtr - desired destination. The WoW client expects an object that has 3 fields of type int that represent x, y and z coordinates. The fields should be laid out sequentially in memory, hence the StructLayout attribute. Pass it by ref.
  5. precision - not sure what this is. Pass 2.

Then add a new ClickToMove function to WoWPlayer:

internal void ClickToMove(Position position) => functions.ClickToMove(Pointer, position);

Now let's modify our Test method to find the nearest target and issue a ClickToMove command to it's location:

void Test()
{
    objectManager.EnumerateVisibleObjects();

    var player = objectManager.Player;

    var units = objectManager.Units;
    var closestUnit = units.OrderBy(u => u.Position.DistanceTo(player.Position)).First();

    Log($"Closest unit to player: {closestUnit.Name}");
    Log($"Position: {closestUnit.Position}");
    Log($"Distance: {closestUnit.Position.DistanceTo(player.Position)}");

    player.ClickToMove(closestUnit.Position);
}

Fire the client up and give it a test drive. You should see your character moving to the nearest unit:

We've covered a lot of territory in this section and laid the groundwork for a lot of cool stuff coming up later. In the next chapter we're going to start talking about giving our bot a behavior loop using a state machine.

Back to Top
...
Subscribe to the RSS feed to keep up with new chapters as they're released

Comments? Leave me a note: