Bloog Bot

Chapter 6
Drew Kestell, 2018
[email protected]

Into the Fray

In the last section, we went over some fundamental skills that are important to understand before diving into bot development, so if you haven't done so, go read it now. Moving forward, we're going to spend less time in Cheat Engine digging for memory addresses, and more time working on our bot. The WoW 1.12.1.5875 Info Dump Thread at Ownedcore has just about everything we could want, so we're going to save ourselves some time and build on the work of others. I encourage you to read through that thread carefully if you're interested in the inner workings of the WoW client (warning: it's long).

At this point, let's take a step back and recap how the solution is organized. We have two projects: BloogBot (the WPF application and the heart of our bot) and Bootstrapper (responsible for creating the WoW process and injecting Loader.dll, which in turn pulls in BloogBot and starts execution at its entry point).

Remember, the Bootstrapper project has a WinImports class that imports all the necessary functions from kernel32.dll, and its entry point looks like this:

namespace Bootstrapper
{
    class Program
    {
        const string PATH_TO_GAME = @"F:\WoW\WoW.exe";

        static void Main()
        {
            ... // create Wow process and inject Loader.dll
        }
    }
}

The only change we've made to the Bootstrapper project is to point to WoW.exe instead of BloogsQuest.exe. Beyond that, we won't be making any other significant changes. Our work is going to be focused on BloogBot now. In that project, we have two folders: Game (this is where we'll put code specific to the WoW domain), and UI (our WPF code). Our Game folder has a single Functions class. This is where we're going to define all the functions we'll be importing from the WoW client. To get things started, I've removed the yell function that we had previously imported from BloogsQuest, and replaced it with our first function from the WoW client.

namespace BloogBot.Game
{
    class Functions
    {
        const int IS_LOGGED_IN_FUNCTION_POINTER = 0x00468550;

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

        IsLoggedInDelegate IsLoggedInFunction;

        internal Functions()
        {
            IsLoggedInFunction = Marshal.GetDelegateForFunctionPointer(
                (IntPtr)IS_LOGGED_IN_FUNCTION_POINTER,
                typeof(IsLoggedInDelegate))  as IsLoggedInDelegate;
        }

        internal bool IsLoggedIn() => IsLoggedInFunction();
    }
}  

This function determines whether the Player is currently logged in. The pattern you see above will become very familiar:

  • Declare memory offset of function as a const
  • Declare delegate with optional CallingConvention annotation
  • Declare field with type of said delegate
  • In the constructor, wire up delegate with unmanaged function using memory offset
  • Wrap delegate with a nice clean method

You may have also noticed that we're not adding the base address of the process to the function offset like we did in our previous example with BloogsQuest. This is due to the fact that older versions of the WoW client did not have ASLR (Address Space Layout Randomization) enabled. BloogsQuest did (it's enabled by default in new Visual C++ projects), so we had to add the base address of the process to the static offset of the function, but things will be considerably more straightforward when working with version 1.12.1 of the WoW client due to ASLR being disabled. We can assume a lot of important things will be in the same location every time - the IsLoggedIn function is one of them. There are times we'll have to get more creative, but we'll attack those as they come up.

Now let's take a look at MainViewModel.cs, the backing file for our GUI:

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
using BloogBot.Game;

namespace BloogBot.UI
{
    public class MainViewModel : INotifyPropertyChanged
    {
        readonly Functions functions;
        
        public ObservableCollection

This may look a little weird if you aren't familiar with WPF. Our class implements the INotifyPropertyChanged interface. The public event PropertyChangedEventHandler and OnPropertyChanged(string name) stuff is there to satisfy the implementation of that interface. Basically this is what allows us to notify the UI when some property changes in our ViewModel so the UI knows to rerender itself. Here's a good article on the topic.

You'll also see we're maintaining an ObservableCollection of strings. This is what we're going to use to print debug statements to a log in the UI. We could invoke a proper console but I think it's easy to work in a single window. We have a Log method that adds some text to the log and sends an OnPropertyChanged event to trigger the UI to update.

Last, we have a Functions object that we initialize in our constructor. This works exactly like it did with our previous test in BloogsQuest. We'll use this class to call functions from the WoW client now. We're going to be registering a bunch of commands down the road, so I'll keep those organized in the commands region. Commands are what we use to wire up buttons in the UI with behavior in our code. The first command calls functions.IsLoggedIn() and prints some debug text to the log.

The XAML that makes up our UI has two elements: a Button wired up with the IsLoggedIn function we imported from the WoW client, and a ScrollViewer that displays our log. Without further ado, it's time for a demonstration:

I close the debug prompt because I don't need interactive debugging at this point. Calling IsLoggedIn before entering the game returns false, and returns true once we enter the world. Just as you'd expect!

This demonstration serves as a proof-of-concept for our ability to call functions from the WoW client. In Part 3 I'll introduce the ObjectManager which allows us to enumerate over all objects visible to the Player. We'll also implement the object hierarchy from which every object in the game is derived.

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

Comments? Leave me a note: