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

  • Tutorial
Привет, %username%! Покопавшись в статьях хабра, я нашел несколько оных про написание ботов для MMORPG. Несомненно это очень интересные и познавательные статьи, но возможности в них весьма скудны. Что если, например нужно пофармить мобов или руду по заданному маршруту убивая агрессивных мобов, игроков и всех кто будет на Вас нападать по пути, выкрикивая им вслед непристойности, да что б еще и определить не смогли. В общем полная эмуляция среднестатистического MMORPG игрока. Написание макросов для AutoIt, симуляция кликов в окне, анализ пикселей под курсором — это совсем не наш вариант. Заинтриговал? Добро пожаловать под кат!
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 (Кастуем фаерболл)

Итак с места в карьер.
1. Выбор способа реализации внедрения (немного теории)

Определенно нам необходимо внедрить код в процесс игры, который и будет ей управлять. Для это можно модифицировать сам исполняемый файл (это очень легко сделать, но и легко определить и получить бан) или внедрить DLL (это тоже определяется очень просто), но это все не для нас. Наш подход — это внедрение кода, в главный поток процесса, получающего управление и возвращающего его обратно.
Для этого нужно найти/придумать точку внедрения, которая будет не столь очевидна для анти-читов и полезна для нас. Таких точек может быть очень много, но по многим причинам, лучшим решением будет внедрение в отрисовку игры, т.е. создание хука для Direct3D. Опять же по многим причинам лучше всего перехватывать EndScene функцию, потому как до ее вызова, все изменения игрового мира и иные расчеты уже произойдут. Вот происходящий процесс внутри для наглядности:
  1. ...
  2. Отрисовка объектов текущей сцены игры
  3. Вызов подмененной D3D EndScene
  4. Наш код
  5. Вызов оригинальной D3D EndScene
  6. Следующая сцена
  7. ...

Сцена в данном ключе это так называемый frame. Другими словами — наш код будет работать с частотой вашего fps.
Замечание: fps может быть достаточно высоким значением, по-этому не стоит обрабатывать каждый вызов кода. Думаю достаточно будет 10-15 вызовов в секунду


2. Инструментарий

Итак план мы наметили, теперь нужны инструменты. Я (как и большинство надеюсь) люблю использовать все готовое. По сему предлагаю обзавестись следующими вещами:
  1. Любая IDE где будем писать код на C#
  2. IDA — на мой взгляд лучший дебагер
  3. HackCalc — калькулятор для пересчета VA (виртуального адреса) в Offset и обратно
  4. SlimDX — DirectX фреймворк для .NET
  5. FlatAsm Managed — библиотека преобразует мнемокоды ассемблера в байткод



3. Поиск точки внедрения

Итак со способом разобрались, инструментами обзавелись, теперь нам необходимо понять, куда внедрять наш код.
При отрисовке с помощью D3D создается виртуальный объект Direct3D device, который по сути представляет собой VMT(виртуальная таблица методов, которая является указателем на указатель на таблицу методов D3D). Это таблица хранит, опять же, указатели на методы Direct3D, например BeginScene, EndScene, DrawText и т.д. Нас в данном случае интересует только EndScene, т.к. Direct3D device создается в единственном экземпляре, то нам нужно получить указатель на него, а затем получить указатели на таблицу. Опять же нам необходимо определить какой D3D используется в клиенте игры и т.к. вариантов у нас 2 (DX9 и DX11), то решить эту проблему можно простым перебором. Для этого мы и будем использовать SlimDX.
В коде processMemory.Read и processMemory.ReadBytes обертки стандартной ReadProcessMemory из kernel32.dll
Проверка на DX9:
//Создаем устройство D3D9
var device = new SlimDX.Direct3D9.Device(
    new SlimDX.Direct3D9.Direct3D(), 
    0, 
    DeviceType.Hardware, 
    Process.GetCurrentProcess().MainWindowHandle, 
    CreateFlags.HardwareVertexProcessing, 
    new[] { new PresentParameters() });
    using (device)
    {
//Открываем текущий процесс
        var processMemory = new ProcessMemory((uint)Process.GetCurrentProcess().Id);
//Считываем необходимый нам адрес расположения в памяти D3D9 функции по смещению 0xA8 от указателя на Com объект
        _D3D9Adress = processMemory.Read<uint>(processMemory.Read<uint>((uint)(int)device.ComPointer) + 0xa8);
//Считываем опкоды функции
        _D3D9OpCode = (int)processMemory.Read<byte>(_D3D9Adress) != 0x6a ? processMemory.ReadBytes(_D3D9Adress, 5) : processMemory.ReadBytes(_D3D9Adress, 7);
    }

Откуда 0xA8?.. Если открыть d3d9.h и найти EndScene метод, то вы найдете
STDMETHOD(EndScene)(THIS) PURE;
42 ой по счету в интерфейсе IDirect3DDevice9, а одна функция в архитектуре х86 адресуется 4мя байтами, получаем 42*4=168, а это и есть 0xA8.
Все это необходимо обернуть в try/catch и если вылетела ошибка, значит нам нужно пробовать D3D11 и тут все немного сложнее, нам нужен не EndScene, а SwapChain, он находится по индексу 8, т.е. 8 * 4 = 32 = 0х20:
//Создаем форму отрисовки для получения устройства D3D11
using (var renderForm = new RenderForm())
{
    var description = new SwapChainDescription()
    {
        BufferCount = 1,
        Flags = SwapChainFlags.None,
        IsWindowed = true,
        ModeDescription = new ModeDescription(100, 100, new Rational(60, 1), SlimDX.DXGI.Format.R8G8B8A8_UNorm),
        OutputHandle = renderForm.Handle,
        SampleDescription = new SampleDescription(1, 0),
        SwapEffect = SlimDX.DXGI.SwapEffect.Discard,
        Usage = SlimDX.DXGI.Usage.RenderTargetOutput
    };
    SlimDX.Direct3D11.Device device;
    SlimDX.DXGI.SwapChain swapChain;
    var result = SlimDX.Direct3D11.Device.CreateWithSwapChain(
        DriverType.Hardware, 
        DeviceCreationFlags.None, 
        description, 
//Здесь мы получаем устройство
        out device, 
        out swapChain);
    if (result.IsSuccess) using (device) using (swapChain)
    {
//И открыв текущий процесс - считаем адрес функции и опкоды
        var processMemory = new ProcessMemory((uint)Process.GetCurrentProcess().Id);
//Считываем наш SwapChain
        _D3D11Adress = processMemory.Read<uint>(processMemory.Read<uint>((uint)(int)swapChain.ComPointer) + 0x20);
        _D3D11OpCode = processMemory.ReadBytes(_D3D11Adress, 5);
    }
}

Все это опять необходимо опять же обернуть в try/catch. В случае неудач обоих попыток получить адрес функции D3D возможно изменились смещения либо у вас не D3D9 или D3D11.
Итог: У нас имеется адрес функции EndScene D3D и опкоды этой функции.
Что с ними делать и как внедрить свой код я возможно расскажу в следующих частях, а пока голосуйте, осмысливайте код выше, гуглите, яндексуйте, бингуйте, яху..., спрашивайте в коментариях и читайте документацию по ассемблеру, будет полный хардкор и дренейки!

Only registered users can participate in poll. Log in, please.

Писать продолжение?

  • 95.4%Да1738
  • 4.6%Нет84
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 61

  • UFO just landed and posted this here
      +1
      Данный подход сейчас не всегда легок в реализации, чем рисование поверх игры.
      0
      Продолжайте, очень интересно посмотреть на эмуляцию поведения.
        0
        А вот у меня есть вопросик из соседней области: а как бы вывести свои какие-то данные прямо в игровом окне (или поверх) в полноэкранном режиме? Например какую-то картинку. Ибо в полноэкранном режиме может быть только одно окно. Может как-то после того, как игра отрисует свою сцену, можно к сцене добавить, например, какой-то полигончик с текстуркой?
          0
          Насколько я знаю софт который использует OSD(on-screen display)/Screen overlay работает или напрямую с видеобуфером, или инжектится в вызовы direct3d.
            +1
            если речь идет о вмешательстве в игровой процесс, то незаметного способа в полноэкранном режиме нет. Незаметный же способ в общем случае — обычно достают всю информацию о сцене через ReadProcessMemory, и необходимую информацию (например положение остальных игроков) рисуют на втором окне (отдельное приложение), которое располагается поверх основного, имеет прозрачную форму, находится в топе, не имеет границ и фокуса. При этом основное окно, чаще всего можно сделать тоже borderless, приближаясь по картинке к fullscreen.
              0
              Проблема-то как раз в полноэкранном режиме. Если режим не полноэкранный — положить окно сверху не проблема. А вот окно в полноэкранном режиме все равно будет выше.
              0
              В принципе можно, но будет кривовато. Для этого перехватываем например Present, который отвечает за вывод буфера на экран:
              STDMETHOD(Present)(THIS_ CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion) PURE;
              Затем вызываем BeginScene, рисуем, вызываем EndScene и наконец вызываем оригинальный Present.
              0
              Интересно, к чему вся эта интрига с ручным рассчетом адресов и ассемблером, если можно взять Detours или madCodeHook и вешать хуки просто по именам функций или методов интерфейсов.
                0
                Для это можно модифицировать сам исполняемый файл (это очень легко сделать, но и легко определить и получить бан) или внедрить DLL (это тоже определяется очень просто), но это все не для нас
                +2
                Мой знакомый однажды в своих целях спрятал все текстуры в LA2, чтобы можно было видеть игроков сквозь стены и ограды (от них остались только остовные деревья). Поделился с людьми — обвинили в кейлоггерстве или вроде того, расстроился и больше никогда ни с кем не делился =\
                  0
                  Так тут серия статей намечается чтобы вы сами могли себе сделать. Свой код в кейлоггерстве не обвинишь.
                    0
                    Насколько я помню, текстуры легко отключались переключением рендерера в wireframe режим, вполне штатный для движка. Другое дело, что для правки конфига нужно было знать приватный RSA ключ, который, впрочем, на 99% фришек был дефолтный.
                    –7
                    Кто может помочь восстановить алгоритм одной небольшой функции из сишной библиотеки? Самому к сожалению не хватает знаний ASM/C чтобы разобрать псевдокод полученный в Ida. Извините за оффтоп.
                      +3
                      Вам на toster.ru
                        0
                        Попробуйте плагин Hex-Rays
                          0
                          Его и использовал, получил
                          псевдокод
                          //----- (00000CBC) --------------------------------------------------------
                          int __fastcall kEncode(int a1, int a2, int a3, int a4)
                          {
                            int v4; // r3@2
                          
                            if ( a1 == 1 )
                              v4 = crc((const char *)a2, (const char *)a3, (char *)a4);
                            else
                              v4 = 1;
                            return v4;
                          }
                          
                          //----- (00000D0C) --------------------------------------------------------
                          int __fastcall crc(const char *a1, const char *a2, char *a3)
                          {
                            size_t v3; // r3@10
                            char *v5; // [sp+4h] [bp-28h]@1
                            const char *v6; // [sp+8h] [bp-24h]@1
                            const char *v7; // [sp+Ch] [bp-20h]@1
                            signed int v8; // [sp+10h] [bp-1Ch]@1
                            size_t v9; // [sp+14h] [bp-18h]@1
                            unsigned int v10; // [sp+18h] [bp-14h]@12
                            char *s; // [sp+1Ch] [bp-10h]@1
                            signed int v12; // [sp+20h] [bp-Ch]@1
                            unsigned int n; // [sp+24h] [bp-8h]@12
                          
                            v7 = a1;
                            v6 = a2;
                            v5 = a3;
                            v8 = -1;
                            v9 = -1;
                            s = 0;
                            v12 = 0;
                            while ( v12 <= 2 )
                            {
                              if ( (signed int)v9 <= 0 )
                              {
                                ++v12;
                                if ( v12 == 1 )
                                {
                                  s = (char *)v7;
                                  v9 = strlen(v7);
                                }
                                else if ( v12 == 2 )
                                {
                                  s = (char *)v6;
                                  v9 = strlen(v6);
                                }
                              }
                              else
                              {
                                s += v9;
                              }
                              if ( v12 <= 2 )
                              {
                                v3 = v9;
                                if ( (signed int)v9 >= 20480 )
                                  v3 = 20480;
                                n = v3;
                                memcpy(byte_4004, s, v3);
                                v10 = n;
                                while ( v10 )
                                {
                                  --v10;
                                  if ( !(v10 & 1) )
                                    byte_4004[v10] = ~byte_4004[v10];
                                }
                                v8 = make_crc(v8, (int)byte_4004, n);
                                v9 -= n;
                              }
                            }
                            sprintf(v5, (const char *)&unk_25FC, v8);
                            return 0;
                          }
                          
                          //----- (00000EE4) --------------------------------------------------------
                          int make_crc32_table()
                          {
                            unsigned int v1; // [sp+4h] [bp-10h]@2
                            signed int i; // [sp+8h] [bp-Ch]@1
                            signed int j; // [sp+Ch] [bp-8h]@2
                          
                            for ( i = 0; i <= 255; ++i )
                            {
                              v1 = i;
                              for ( j = 0; j <= 7; ++j )
                              {
                                if ( v1 & 1 )
                                  v1 = (v1 >> 1) ^ 0xEDB88320;
                                else
                                  v1 >>= 1;
                              }
                              crc32_table[i] = v1;
                            }
                            return 0;
                          }
                          // EE4: using guessed type int make_crc32_table(void);
                          
                          //----- (00000FC4) --------------------------------------------------------
                          int __fastcall make_crc(int a1, int a2, unsigned int a3)
                          {
                            unsigned int v4; // [sp+4h] [bp-20h]@1
                            int v5; // [sp+8h] [bp-1Ch]@1
                            int v6; // [sp+Ch] [bp-18h]@1
                            unsigned int i; // [sp+14h] [bp-10h]@3
                          
                            v6 = a1;
                            v5 = a2;
                            v4 = a3;
                            if ( !initcrc )
                            {
                              initcrc = 1;
                              make_crc32_table();
                            }
                            for ( i = 0; i < v4; ++i )
                              v6 = crc32_table[(unsigned __int8)(*(_BYTE *)(v5 + i) ^ v6)] ^ ((unsigned int)v6 >> 8);
                            return ~v6;
                          }
                          // EE4: using guessed type int make_crc32_table(void);
                          


                          Но не все в нем понимаю, например что значат ~ перед переменными, что за byte_4004(просто еще одна переменная?) и тд.
                            –2
                            byte_4004 — глобальный массив (похоже, размера 20480 байт), ~ — битовое «не» в С.
                          0
                          Благодаря coperius проблема решена.
                          –2
                          А почему вам так не нравится подход со сканированием пикселей и имитацией нажатий? Грамотная реализация, особенно если заменить сканирование экрана на прослушку трафика (я в свое время через pcap делал), вычисляется только анализом поведения. Напротив, взаимодействие с процессом вычисляется защитой на раз-два, зачастую даже без хуков на уровне драйвера ядра.
                            0
                            Просто потому, что пиксели — это гадание на кофейной гуще (особенно если в игре есть погода и время суток), трафик — несомненно информативен, но read only, а имитация занимает полностью ваш ПК и что бы работать на нем Вам понадобиться виртуальная машина, которая съест дополнительно 2Гб RAM и 40Гб HDD, а если два окна или 3? Даже при грамотной реализации, это все трудоемко и не надежно. А вот вычислить защитой можно все на самом деле и здесь главное уметь хорошо прятать — о чем я тоже буду писать
                              0
                              Позвольте уточнить, трафик read-only только в случае с pcap.
                              Как минимум, трафик можно редактировать посредством прокси(из готовых решений — тот же WPF-или-как-там-его-и-его-аналоги). Защита от подобных программ далеко не всегда работает, взять тот же Hackshield в Archeage. Кроме того, это — довольно универсальный способ.
                              Кроме того, зачастую довольно удобно управлять поведением игры при помощи упомянутого dll-inject'а и перехватом нужных функций. Это, кстати говоря, решает проблему с окнами — dll много памяти не скушает, всю работу, по факту, выполняет клиент. Кроме того, если у клиента нет проверки на checksum, а защита не расчитана на подобные экзерсисы, можно поправить таблицу импорта и подгружать dll уже на старте приложения, а не перетыкать ее инжектором.
                              Еще довольно неплох в плане подхода micromacro — есть базовые функции для работы с памятью, а вся логика строится на lua/xml(код/путевые точки). Главный минус — вряд ли будет работать с адекватной защитой.
                              Со сканированием пикселей — согласен, сам такой подход не пробовал, но для комплексных задач представляется довольно сложным в реализации и несколько ограниченным в функционале.

                              За способ, описанный в статье, отдельное спасибо(особенно — за шарп), интересный подход. Еще вместо расчета офсета вручную можно было бы применить GetProcAddress, но это работает только для экспортированных функций.

                              И по поводу следующих статей — однозначно писать!
                            +8
                            Очень напомнило, как когда-то на 2-ом курсе в качестве курсовика по ассемблеру написали с однокурсником бота, который тоже работает засчёт перехвата функций.
                            Как внедриться в чужой процесс брали здесь: rsdn.ru/article/baseserv/IntercetionAPI.xml#EIEAC
                            А вот описанный в той же статье метод перехвата не подошёл, поэтому DirectX многопоточен. Так что преподаватель подсказал сделать как в Detours (гуглить: file:huntusenixnt99.pdf).
                            Вот видосики, где бот играет, может и про него статью напишем: www.youtube.com/user/thbotproject/videos
                            Сайт бота: thbot.kt15.ru/

                            Теперь насчёт технических деталей: мы тоже перехватывали, руками вытаскивая offset из vtable. А потом, когда всё уже сделали, поняли, что можно было C-шное API перехватывать.
                              +8
                              Посмотрел видосики… Нео и Агент Смит нервно курят, глядя на эту анимешную девочку, как она проскакивает между пуль…
                                0
                                Спасибо) Вот только тактики нашему боту не хватает, он думает про текущий момент и всё.

                                А про Вашего бота, конечно же, жду продолжение, ведь бот для MMORPG — это в разы интереснее!
                                  0
                                  У шариков линейное движение, вы можете знать их траектории, вычислив их скорость по 2м точкам например и найти места где они не пролетают с учетом времени движения до этих мест и сразу двигаться к ним
                                    +3
                                    Эх, если бы всё так просто. Вот, гляньте как движутся пульки на 1:06: youtu.be/6xWvr-TAR1w?t=1m6s
                                    0
                                    Если думает только про текущий момент — как так круто получилось реализовать безумные увороты и сбор бонусов?
                                      0
                                      В каждый момент есть позиция, к которой бот стремится добраться. Когда на экране нет бонусов и врагов, то это заданная позиция. Когда нет бонусов и есть враги, то это позиция напротив врага, а когда есть бонусы, то это позиция одного из них.
                                      Бот пытается в неё добраться, но при этом анализирует все пульки в небольшом радиусе вокруг себя и избегает их. Примерно так.
                              • UFO just landed and posted this here
                                  0
                                  Это всё конечно здорово, но в очередной раз про x86 и DX9.
                                  А современный мир использует x64 и DX11.
                                    +1
                                    DX11 — я тоже описал, а вот x64 очень легко получить из x86, пару бессонных ночей и готово
                                    0
                                    Про пару магических чисел сказали, а об остальных умолчали :) Почему 5 и 7 при чтении?
                                      0
                                      У Вас есть 2 варианта узнать =)
                                      1. Почитать документацию по АСМу
                                      2. Дождаться следующей части
                                        +1
                                        Асм — настолько огромный мир, что читать можно очень долго и не узнать все :)
                                        –1
                                        Видимо автор таким образом пытается определить сколько места в начале функции потребуется для записи инструкций хука, что сделано крайне некошерно
                                          +1
                                          Давайте будем конструктивными, как бы Вы сделали?
                                            0
                                            Для таких целей используют дизассемблер длин, иначе нет никакой горантии что пролог функции не будет повреждён. Вы ведь не имеете 100% представления сколько инструкций будут перезаписаны
                                              +1
                                              Абсолютно с Вами согласен, но написание своего Length Disassembler'а заслуживает отдельной статьи, а так как мы перехватываем известную функцию на известной архитектуре, с которой я естественно предварительно познакомил свой дизасемблер, то возникает резонный вопрос: «А зачем?»
                                                +1
                                                Никто не гарантирует что пролог будет во всех бинарниках одинаковым и будет начинатся именно с конкретной инструкции, это нигде не документировано и не факт что вы не нарвётесь на уже установленный хук. Конечно оно может работать и так, но это всёравно будет не корректная реализация(см. реализацию движков перехватчиков). Что касается готового дизассемблера длин, то на ASM\С\С++ с этим проблем нету, как на дела обстаят на .net я не в курсе.
                                                  0
                                                  Ну я думаю, если кому это понадобится, тот запросто соберет для себя DLL и подключит к своему проекту или портирует в .net. Задача моей статьи показать как это реализовать в черновом варианте и не более чем. Но за замечание спасибо, для универсальности кода, это очень хорошее замечание.
                                        –3
                                        Очень интересно! Как раз на днях задавался таким вопросом, как написать бота для мморпг. Жду продолжения. Попробую реализовать нечто похожее по примерам.
                                          –5
                                          Спасибо большое за хорошую статью. Мне всегда была интересна эта тема, но до стремления изучить её так и не дошло. Благодаря вашим статьям мне удастся изучить принципы и методы в этой области. Буду с нетерпением ждать продолжения.

                                          Спасибо.
                                            +1
                                            Warden в состоянии определить подмену адресов в IAT, будьте осторожны с этим!
                                            Вообще статьи как таковой нет, вопрос «писать или нет» вместился бы в комментарий.
                                              +2
                                              Наносимый «вред» для разработчика игры измеряется, например, в количестве установок бота * средние потери. Допустим, Blizzard как-то терпит последних 3.5 линуксоида с вайном (вот уж где поле для внедрений… подход с внедрением своего графического слоя у форка вайн ака winex прожил пару лет), но любой среднестатистический ботик, подгружающий дллку, будет убит через n инсталлов, да еще и улучшит ситуацию с детектированием последующих версий :) Делать «для себя и красиво» можно, да, даже если оно будет стоить месяца работы, работать на трех машинах в связке и анализировать сразу и трафик, и графику, и еще и менять трафик…

                                              Линкану www.unknowncheats.me/forum/anti-cheat-bypass/60881-gameguard-d3d-vtable-hooks-get-detected.html, даже самая занюханная защита уже умеет сканировать память процесса в поиске очевидных дыр, с учетом проверки окружения (dll, менее важно — драйвера), вот так с ходу не верится, что в 2015 остались недетектируемые способы напакостить в win-процессе.
                                                0
                                                С другой стороны, способов сделать неотключаемую защиту не осталось еще раньше… Хотя, конечно же, снятие защиты требует совсем других познаний, нежели написание бота.
                                                  0
                                                  недетектируемые способы напакостить в win-процессе
                                                  Через hardware breakpoint-ы, например?
                                                    0
                                                    Плюсую, я писал чит для Dota 2 который не обнаруживается VAC именно с HB. Кстати, сам процесс разработки был гораздо более сложен и интересен, чем то, что предлагает автор. Хуки vtable уже давно обнаруживаются. Хуки с EndScene тоже. Мне ещё пришлось написать внедрение shared-библиотек в рантайме, и всё это было под Linux (это тоже заслуживает отдельной статьи). В общем, я бы с радостью поделился, но не могу, ибо чит приватный и писался не для себя :D
                                                      0
                                                      Очень интересно было бы почитать про используемые подходы. Сам чит тут вряд ли кому-то нужен.

                                                      Может быть получится не нарушая условий написать статью?
                                                        0
                                                        Тут дело в том, что приватные читы не привлекают внимания и их не фиксят годами (иногда вообще не фиксят). А как только я напишу статью, появятся желающие повторить это => метод привлечёт внимание => ставит под угрозу чит. И да, читы запрещены и к тому же вредят коммюнити.
                                                          0
                                                          о чем и речь :)
                                                          как только чит из «неизвестного, но теоретически детектируемого» перерастает в «известный, надо детектировать» — становится плохо
                                                      0
                                                      Просвятите, пожалуйста, в чем сложность прочитать debug регистры? Многие упаковщики с легкостью детектят это и успешно снимают железные брейкпоинты.
                                                        +3
                                                        В том, что, фигурально выражаясь, кто раньше встал — того и тапки. В ring3 эти регистры нельзя прочитать напрямую (будет #GP), поэтому в Win32 для usermode-приложения есть только один способ — через структуру CONTEXT, которую можно получить несколькими путями. Самый очевидный — GetThreadContext(), чтобы задетектить отладку, и SetThreadContext(), чтобы ее снять. Второй способ — спровоцировать SEH-исключение, посмотреть CONTEXT *ContextRecord в _except_handler() и поменять при необходимости.

                                                        В первом случае достаточно сесть поверх этих двух функций, и игнорировать Dr#. Во втором надежнее всего будет сесть поверх KiDispatchException и просто возвращать копию контекста вместо самого контекста, с которой потом поступать по усмотрению.

                                                        Что касается обнаружения самого факта отладки — если не садиться внутри кода приложения, а только на экспорты, то анализ времени выполнения ничего не даст — приложение принципиально не может гарантировать, сколько будет выполняться тот или иной вызов DirectX. Есть еще всякие экзотические способы типа недокументированного изменения EAX после вызова OutputDebugString, но это тоже, в принципе, просто обходится.
                                                          0
                                                          А что насчет анализа времени выполнения GetThreadContext()?..
                                                            0
                                                            А сколько она должна выполняться на самом деле, например? С учетом того, что поток в любой момент может переключиться и что операционная система, в принципе, не обязана совсем честно обслуживать все потоки, с учетом того, что кому-то может придти в голову сожрать всю оперативку и начать свопиться, или вообще в системе есть антивирус… Мало ли по какой причине функции могут выполняться неодинаковое время :) Эта защита надежно работает только против трассировки процессов — когда между двумя вызовами должно пройти, скажем, порядка 20 мс, а проходит 5 сек — значит, кто-то сидит и вдумчиво жмет F10 в софтайсе.
                                                            0
                                                            Можно же позвать системный вызов NtGetContextThread через sysenter, и тогда перехватывать его необходимо еще в ядре. Еще мне помнится, некоторые программы сами себя отлаживают, тем самым не давая прицепить к себе еще один отладчик.
                                                              +2
                                                              Насколько мне извесно процесс не может подключиться как отладчик сам к себе, для этого требуется дополнительный процесс
                                                                +1
                                                                Ну так и на KiDispatchException иначе, чем через ядро, не сядешь — потому что в SEH chain встраиваться хоть и можно, но бессмысленно, т.к. у процесса есть контроль над своим SEH chain, и при желании процесс может посмотреть, кто там есть чужой и всех оттуда попросить. С другой стороны — разве сейчас для кого-то проблема сделать простейший драйвер-перехватчик?

                                                                И, как выше верно заметили, сам себя отладить не получится. К счастью, в нашем случае необязательно отлаживать процесс официально — т.к. мы уже в ядре, нам не нужны привилегии отладчика чтобы поменять контекст потока, добавить туда отладочные регистры и словить брекпоинт.
                                                          +2
                                                          Чем параноидальнее защита, тем больше у неё ложных срабатываний. Потому что в процесс постоянно ломится и внедряется огромная куча всего — всякие перехватчики клавиатурных нажатий, антивирусные сканеры, всевозможные «управляющие центры» для видео и звука, компоненты настроек устройств ввода вроде мыши и т.д.
                                                          Запустив процесс в OllyDebug, легко увидеть, сколько в адресное пространство процесса грузится явно посторонних DLL. Особенно много всякой фирменной мелочёвки грузится внутрь процесса при игре на ноутах. Каталогизировать сигнатуры всего этого хлама не так-то просто даже для такой большой компании, как Blizzard.
                                                          В итоге разработчики защит оказываются меж двух огней: сделай мало проверок — и ботоводы будут творить что хотят. Сделай слишком много проверок — и техподдержку завалят жалобами, что игра не запускается, да ещё и на игровых форумах основательно польют грязью.
                                                          В итоге всегда остаётся достаточное количество дыр, через которые можно пролезть. И если вы пишете бот для себя, то его могут никогда не обнаружить, т.к. целенаправленно ловят лишь массовые программы, а одиночки оказываются в позиции Неуловимого Джо.
                                                          0
                                                          Почему сразу не на С#? (Я писал бота без ассемблера и его не палили!)
                                                          Почему нужно именно вставлять ассембелерный код в вставки в С#?
                                                          Увеличена скорость роботы бота? Контроль пакетов или как понять?
                                                          В чем подвох? :)
                                                            0
                                                            почитайте следующие статьи

                                                          Only users with full accounts can post comments. Log in, please.