Bloog Bot

Chapter 7
Drew Kestell, 2018
[email protected]

Opening Our Eyes

In Part 2 we showed a simple example of how to call a function in the WoW client using the IsLoggedIn function. Now we're going to take a look at one of the most useful functions for our bot: EnumerateVisibleObjects.

In Part 1 I mentioned the different calling conventions available in C/C++. Up to this point we haven't had to worry about it because our first function IsLoggedIn didn't take any parameters, and didn't have a return value. The new EnumerableVisibleObjects has both parameters and a return value so we're going to need to think about calling convention here.

Remember that the default calling convention from C# is Stdcall and we can override that with the UnmanagedFunctionPointer annotation. The EnumerateVisibleObjects function uses Fastcall, so we're going to need to deviate from C#'s default behavior. Unfortunately, C# doesn't support Fastcall (check out the API docs on MSDN), so we're going to have to get creative.

The solution is to write a small library in C++ that exposes a method implemented with Stdcall that wraps the call to EnumerateVisibleObjects using Fastcall. So from C# we'll be calling EnumerateVisibleObjects indirectly through our Fastcall library. The library is a DLL project with the following code:

#include "stdafx.h"

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }

    return TRUE;
}

extern "C"
{
    void __declspec(dllexport) __stdcall EnumerateVisibleObjects(unsigned int callback, int filter, unsigned int parPtr)
    {
        typedef void __fastcall func(unsigned int callback, int filter);
        func* function = (func*)parPtr;
        function(callback, filter);
    }
} 

Make sure you set the output directory of the project to ..\Bot\ so that it ends up in the same place as the rest of our bot's dependencies.

The one thing worth noting here is the line extern "C". This post on stackoverflow has a good explanation, but the gist is: the C++ compiler performs "name mangling" to support function overloading, but that makes it difficult to identify the function by name when using DllImport from C#. So extern "C" disables that name mangling.

With that done, let's wire up the EnumerateVisibleObjects method in the Functions class.

class Functions
{
    // IsLoggedIn
    const int IS_LOGGED_IN_FUN_PTR = 0x00468550;

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    delegate bool IsLoggedInDelegate();

    IsLoggedInDelegate IsLoggedInFunction =
        Marshal.GetDelegateForFunctionPointer(
            (IntPtr)IS_LOGGED_IN_FUN_PTR,
            typeof(IsLoggedInDelegate)
        ) as IsLoggedInDelegate;

    internal bool IsLoggedIn() => IsLoggedInFunction();

    // EnumerateVisibleObjects
    const int ENUMERATE_VISIBLE_OBJECTS_FUN_PTR = 0x00468380;

    [DllImport("FastCall.dll", EntryPoint = "EnumerateVisibleObjects")]
    static extern void EnumerateVisibleObjects(IntPtr callback, int filter, IntPtr ptr);

    internal void EnumerateVisibleObjects(IntPtr callback, int filter) =>
        EnumerateVisibleObjects(
            callback,
            filter,
            (IntPtr)ENUMERATE_VISIBLE_OBJECTS_FUN_PTR
        );
}

Notice that unlike IsLoggedIn, instead of using Marshal.GetDelegateForFunctionPointer, we're going through our Fastcall library, so we have to import that using DllImport and specifying the entry point (this would be the failure point if we didn't use extern "C" as mentioned above). Also note that we don't need to specify a calling convention here because the default is Stdcall and that's what our Fastcall library uses to wrap the internal Fastcall function.

So, how does the EnumerateVisibleObjects function in the WoW client work? It takes two parameters: a callback function (IntPtr), and a filter (int). For each object that's in range of the Player, the WoW client will call your callback function passing in the GUID of the object and the filter value that was passed in. The filter argument has to be provided, but it's up to you to implement that in your callback function (or ignore it). You could, for example, only enumerate over the first n objects using that filter argument.

The guid of an object isn't that useful to us. What we really want is a pointer to the object in memory so we can read from offsets from that memory address to find interesting values associated with the object. Thankfully the WoW client exposes a function called GetObjectPtr that takes a GUID as a single parameter and returns a pointer to that game object. Here's the code:

class Functions
{
    // IsLoggedIn
    ...

    // EnumerateVisibleObjects
    ...

    // GetObjectPtr
    const int GET_OBJECT_PTR_FUN_PTR = 0x00464870;
    
    delegate IntPtr GetObjectPtrDelegate(ulong guid);

    static GetObjectPtrDelegate GetObjectPtrFunction =
        Marshal.GetDelegateForFunctionPointer

Notice the lack of [UnmanagedFunctionPointer(CallingConvention.StdCall)] annotating the delegate. In this case, the GetObjectPtr function is implemented using Stdcall in the WoW client so we can defer to C#'s default behavior.

All objects in WoW have a "type". There are obvious differences between an Item and a Player. These different types of objects all have a place in an object inheritance hierarchy. We will explore that hierarchy fully down the road, but for now, let's outline what the different types are using an enum, and define a base class WoWObject that holds a few properties that all objects in the game share.

enum ObjectType : byte
{
    None,
    Item,
    Container,
    Unit,
    Player,
    GameObject,
    DynamicObject,
    Corpse
}
class WoWObject
{
    internal readonly ulong Guid;
    internal readonly IntPtr Pointer;
    internal readonly ObjectType ObjectType;

    internal WoWObject(ulong guid, IntPtr pointer, ObjectType objectType)
    {
        Guid = guid;
        Pointer = pointer;
        ObjectType = objectType;
    }
}

There are 8 different object types in the WoW client, so we define those all in the enum. Every object in the game has a GUID, a Pointer to the object, and an ObjectType, so we add those as properties to the WoWObject class. Eventually we'll inherit from this base class when we create classes for items, players, etc, and those derived classes will have their own specific properties and methods.

There's one more thing to talk about before showing the ObjectManager. Remember that the we pass a callback function to the EnumerateVisibleObjects function, then that function calls our callback passing in the object's GUID as a parameter which we then pass to GetObjectPtr to get a pointer to the object in memory. From there, we'll use the same principles that allowed us to find the Player's health in BloogsQuest from Part 1 to find the WoW object's values in memory near the object's starting address. The first thing we'll find is the object's type, and to do that we need to know the offset of the ObjectType property from the start of the object in memory.

We also need a slightly different approach for reading memory from that location. In previous examples we used ReadProcessMemory, but now that we're in-process that isn't necessary. Instead we can simply dereference the pointer, but in order to do so we'll need to be operating in an unsafe context. Here's an MSDN article that talks about working with pointers from C# if you want to learn more. In our case, we're going to create a new class called MemoryReader that we'll use to do our pointer dereferencing, and we'll also need to turn on the unsafe code flag for our project. The class doesn't have much, but we'll build on it as we move forward:

unsafe class MemoryReader
{
    internal byte ReadByte(IntPtr address) => *(byte*)address;
}

The type of an object is stored as a byte offset 0x14 from the object's starting address, so the MemoryReader class has a single method that reads from an address and parses the value as a byte.

Now we're going to add the ObjectManager class. This class is responsible for defining the callback function and passing that to EnumerateVisibleObjects, then maintaining a list of all the game objects that were found. Here's the code for that class:

class ObjectManager
{
    const int OBJECT_TYPE_OFFSET = 0x14;

    readonly Functions functions;
    readonly MemoryReader memoryReader;
    readonly EnumerateVisibleObjectsCallback callback;
    readonly IntPtr callbackPtr;

    readonly internal IList

Recall that we pass our callback to the EnumerateVisibleObjects function by its pointer, so we have to define a delegate and then wire up the Callback method with the delegate using Marshal.GetFunctionPointerForDelegate. Then we expose an EnumerateVisibleObjects method that first clears the object list, then calls EnumerateVisibleObjects in the WoW client, passing it the callback pointer and a filter of 0 (we'll be ignoring the filter in our callback).

The callback is the interesting part. Like I mentioned earlier, WoW's EnumerateVisibleObjects function will call our callback for every object visible to the Player, so it's up to us to specify what we want to do with that object. For now, we'll just be adding all the objects to a list. But first we need to get the object's pointer which we do by calling GetObjectPtr. From there, we retrieve the ObjectType using our memory reader, reading from a 0x14 offset from the object's pointer.

We're ready for a test. In our ViewModel, let's change the Test button to call EnumerateVisibleObjects then print some results to the log. Here's the new code in the ViewModel:

public class MainViewModel : INotifyPropertyChanged
{
    readonly ObjectManager objectManager;

    public MainViewModel()
    {
        objectManager = new ObjectManager(new Functions(), new MemoryReader());
        
        ...
    }

    #region Commands

    // test command
    ICommand testCommand;
    void Test()
    {
        objectManager.EnumerateVisibleObjects();
        var objectCount = objectManager.Objects.Count;

        Log($"{objectCount} objects found:\n");

        for (var i = 0; i < objectCount; i++)
        {
            Log($"Object {i + 1}");
            Log($"Guid: {objectManager.Objects[i].Guid}");
            Log($"Pointer: {objectManager.Objects[i].Pointer}");
            Log($"ObjectType: {objectManager.Objects[i].ObjectType.ToString()}\n");
        }  
    }

    public ICommand TestCommand => 
        testCommand ?? (testCommand = new CommandHandler(Test, true));

    #endregion

    ...
}

In the ViewModel's constructor we instantiate a new ObjectManager, then wire up the Test button to call EnumerateVisibleObjects. Let's log in and give it a spin:

We found 65 objects! The first few are of type Item, but if you scroll down in that list you'll see others.

We've added quite a bit of new code to our solution in Part 3, so here's an overview of how I have things organized:

In the next chapter we'll create some derived classes that inherit from the base WoWObject class and beef up the ObjectManager class to let us find what we're looking for more easily.

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

Comments? Leave me a note: