Пишем бота для MMORPG с ассемблером и дренейками. Часть 4

в 19:43, , рубрики: .net, c#.net, game development, mmorpg, reverse engineering, world of warcraft, боты, игры, Программирование, реверс-инжиниринг

Пишем бота для MMORPG с ассемблером и дренейками. Часть 4 - 1 Привет, %username%! Итак, продолжим написание нашего бота. Из прошлых статей, мы научились находить адрес перехватываемой функции для DirectX 9 и 11, исполнять произвольный ассемблерный код в главном потоке игры скрывая его от различных методов защиты и получать информацию об окружающем мире. Другими словами, мы можем совершать осознанные действия в игре. И для начала я предлагаю научиться передвигаться!

Disclaimer: Автор не несет ответственности за применение вами знаний полученных в данной статье или ущерб в результате их использования. Вся информация здесь изложена только в познавательных целях. Особенно для компаний разрабатывающих MMORPG, что бы помочь им бороться с ботоводами. И, естественно, автор статьи не ботовод, не читер и никогда ими не был.


Для тех кто пропустил прошлые статьи вот содержание, а кто все прочел идем дальше:

Содержание

  1. Часть 0 — Поиск точки внедрения кода
  2. Часть 1 — Внедрение и исполнение стороннего кода
  3. Часть 2 — Прячем код от посторонних глаз
  4. Часть 3 — Под прицелом World of Warcraft 5.4.x (Структуры)
  5. Часть 4 — Под прицелом World of Warcraft 5.4.x (Перемещение)
  6. Часть 5 — Под прицелом World of Warcraft 5.4.x (Кастуем фаерболл)

Так уж сложилось что и взаимодействие с игровым объектом и перемещение по щелчку в World of Warcraft возможно при клике мыши. Но мы не будем симулировать клики, через WinApi, мы сделаем круче. Мы перехватим то место, где уже клик обрабатывается игрой, как клик на экране, причем он уже будет переведен из координат экрана в координаты игрового мира. Для начала получим адреса некоторых функций, они нам очень понадобятся в процессе, это просто сделать с помощью нашего всеми любимого дебагера IDA:

    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,
    }

Объявим класс WorldClick:

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<ulong>(guidAddress, guid);
        Memory.Process.Write<float>(precisionAddress, precision);
        Memory.Process.Write<float>(positionAddress, x);
        Memory.Process.Write<float>(positionAddress + IntPtr.Size, y);
        Memory.Process.Write<float>(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>((int)ClickToMove.CTM_PUSH, true);
    }

    public static Vector3 GetClickPosition()
    {
        return new Vector3(
                Memory.Process.Read<float>((int)ClickToMove.CTM_X, true),
                Memory.Process.Read<float>((int)ClickToMove.CTM_Y, true),
                Memory.Process.Read<float>((int)ClickToMove.CTM_Z, true));
    }
}

Ну все, теперь можно бегать по просторам Азерота с помощью:

WorldClick.ClickTo(x,y,z, 0, ClickType.Move, 0.5f);

На сегодня все. Вы узнали много интересного и практичного. Можете проверить это на какой-нибудь пиратке, а если уверены в своей обфускации кода, то можно даже и на официальном сервере побегать, с какой-нибудь стартовой записью, что бы не рисковать. Ведь вдруг сотрудники Blizzard, тоже читают Хабр.

Автор: 3axap4eHko

Источник

Поделиться

* - обязательные к заполнению поля