We are writing a bot for MMORPG with assembler and draenei. Part 4

  • Tutorial
Hi% username%! So, let's continue writing our bot. From past articles, we learned how to find the address of the intercepted function for DirectX 9 and 11, execute arbitrary assembler code in the main stream of the game, hiding it from various protection methods and obtain information about the world around it. In other words, we can perform informed actions in the game. And for starters, I suggest learning how to move around!

Disclaimer: The author is not responsible for your use of the knowledge gained in this article or damage resulting from their use. All information presented here is for educational purposes only. Especially for companies developing MMORPG to help them deal with bots. And, of course, the author of the article is not a bot driver, not a cheater, and he never was.




For those who have missed past articles, here is the content, and who have read everything, go ahead:
Content

  1. Part 0 - Finding a code injection point
  2. Part 1 - Implementation and execution of third-party code
  3. Part 2 - Hide the code from prying eyes
  4. Part 3 - Under the Sight of World of Warcraft 5.4.x (Structures)
  5. Part 4 - Under the Sight of World of Warcraft 5.4.x (Moving)
  6. Part 5 - Under the Sight of World of Warcraft 5.4.x (Custom Fireball)

It just so happened that interaction with the game object and movement by clicking in World of Warcraft is possible with a mouse click. But we will not simulate clicks, through WinApi, we will do better. We will intercept the place where the click is already processed by the game, like a click on the screen, and it will already be transferred from the screen coordinates to the coordinates of the game world. First we get the addresses of some functions, we will really need them in the process, it's easy to do this with the help of our beloved IDA debugger:
    public enum FunctionWow
    {
        ClntObjMgrGetActivePlayer = 0x39B615,
        ClntObjMgrGetActivePlayerObj = 0x4FC6,
        FrameScript_ExecuteBuffer = 0x4fd12,
        Spell_C_HandleTerrainClick = 0x38f129,
        FrameScript__GetLocalizedText = 0x414267,
        IsOutdoors = 0x414b53,
        UnitCanAttack = 0x41ad3c,
        CGUnit_C__InitializeTrackingState = 0x41fb57,
        CGWorldFrame__Intersect = 0x5eef7b,
        CGUnit_C__Interact = 0x8D01D0,
    }
    public enum ClickToMove
    {
        CTM = 0x420543,
        CTM_PUSH = 0xD0EEBC,
        CTM_X = 0xD0EF2C,
        CTM_Y = CTM_X+4,
        CTM_Z = CTM_Y+4,
    }

Declare the WorldClick class:
public enum ClickType
{
    FaceTarget = 0x1,
    Face = 0x2,
    StopThrowsException = 0x3,
    Move = 0x4,
    NpcInteract = 0x5,
    Loot = 0x6,
    ObjInteract = 0x7,
    FaceOther = 0x8,
    Skin = 0x9,
    AttackPosition = 0xa,
    AttackGuid = 0xb,
    ConstantFace = 0xc,
    None = 0xd,
    Attack = 0x10,
    Idle = 0x13,
}
public static class WorldClick
{
    public static void ClickTo(float x, float y, float z, ulong guid, ClickType action, float precision)
    {
        if (Mathf.Abs(x) < 0.1 && Mathf.Abs(y) < 0.1 && (Mathf.Abs(z) < 0.1 && (long)guid == 0L))
            return;
        //память для 3х координат
        var positionAddress = Memory.Process.AllocateMemory(3 * sizeof(float));
        //guid типа ulong в 8 байт
        var guidAddress = Memory.Process.AllocateMemory(sizeof(ulong));
        //значение точности, до которой продолжать движение, я беру 0.5f
        var precisionAddress = Memory.Process.AllocateMemory(sizeof(float));
        if (positionAddress <= 0U || guidAddress <= 0U || precisionAddress <= 0U)
            return;
        Memory.Process.Write(guidAddress, guid);
        Memory.Process.Write(precisionAddress, precision);
        Memory.Process.Write(positionAddress, x);
        Memory.Process.Write(positionAddress + IntPtr.Size, y);
        Memory.Process.Write(positionAddress + IntPtr.Size * 2, z);
        var asm = new[]
        {
        "call " + Memory.Process.GetAbsolute(FunctionWow.ClntObjMgrGetActivePlayer ),
         //Проверка на наличие активного игрока
        "test eax, eax",
        "je @out",
         //Получаем указатель на объект - понадобится ниже
        "call " + Memory.Process.GetAbsolute(FunctionWow.ClntObjMgrGetActivePlayerObjAddress),
        "test eax, eax",
        "je @out",
        "mov edx, [" + precisionAddress + "]",
        "push edx",
        "push " + positionAddress,
        "push " + guidAddress,
        "push " + (int)action,
        "mov ecx, eax",
        //Вызываем ClickToMove()
        "call " + Memory.Process.GetAbsolute((int)ClickToMove.CTM),
        "@out:",
        "retn"
        };
        Memory.Hook.InjectAndExecute(asm);
        Memory.Process.FreeMemory(positionAddress);
        Memory.Process.FreeMemory(guidAddress);
        Memory.Process.FreeMemory(precisionAddress);
    }
    public static ClickType GetClickTypePush()
    {
        return (ClickToMoveType)Memory.Process.Read((int)ClickToMove.CTM_PUSH, true);
    }
    public static Vector3 GetClickPosition()
    {
        return new Vector3(
                Memory.Process.Read((int)ClickToMove.CTM_X, true),
                Memory.Process.Read((int)ClickToMove.CTM_Y, true),
                Memory.Process.Read((int)ClickToMove.CTM_Z, true));
    }
}

Well that's it, now you can run through the expanses of Azeroth with:
WorldClick.ClickTo(x,y,z, 0, ClickType.Move, 0.5f);

That's all for today. You learned a lot of interesting and practical things. You can check it on some pirate, and if you are confident in your obfuscation of the code, you can even run on the official server with some starting entry, so as not to risk it. After all, all of a sudden, Blizzard employees also read Habr.

Also popular now: