Пишем бота для MMORPG с ассемблером и дренейками. Часть 4.5
Привет %username%! Сделаем небольшую остановку, что бы расставить все точки над «и», понять что к чему и как работает. За последнее время, я получил очень много вопросов связанных с офсетами для различных версий World of Warcraft, множество предложений по способам реализации инъекции сторонних инструкций в игровой процесс и теперь настало время это все обсудить. Если есть вопросы или предложения, добро пожаловать под кат! Disclaimer: Автор не несет ответственности за применение вами знаний полученных в данной статье или ущерб в результате их использования. Вся информация здесь изложена только в познавательных целях. Особенно для компаний разрабатывающих MMORPG, что бы помочь им бороться с ботоводами. И, естественно, автор статьи не ботовод, не читер и никогда ими не был. Содержание Часть 0 — Поиск точки внедрения кода Часть 1 — Внедрение и исполнение стороннего кода Часть 2 — Прячем код от посторонних глаз Часть 3 — Под прицелом World of Warcraft 5.4.x (Структуры) Часть 4 — Под прицелом World of Warcraft 5.4.x (Перемещение) Часть 5 — Под прицелом World of Warcraft 5.4.x (Кастуем фаерболл) 1. Вопросы и ответыСамый часто задаваемый вопрос, это «почему ассемблер?». Складывается ощущение, что у людей при слове ассемблер начинается паническая атака, закладывает уши и отключается мозг. Не нужно его боятся на столько, он не так страшен. Ведь что может быть проще, чем попросить саму программу выполнить свою же функцию и результат поместить по указателю который вы знаете? Не нужно изобретать велосипед, я использую, то что есть. Смотрите как просто вызвать внутреннюю функцию DoAnythingFunction имея на нее указатель DoAnythingFunctionPointer на ассемблере «call » + GetAnyGameObjectFunctionPointer, «push » + argument3Pointer, «push » + argument2Pointer, «push » + argument1Pointer, «push » + argument0Pointer, «mov ecx, eax», «call » + DoAnythingFunctionPointer, «retn» GetAnyGameObjectFunctionPointer — возвращает нам какой-либо объект, указатель на него будет помещен в eaxargument[N]Pointer — указатель на N-ый аргумент для DoAnythingFunction.Затем идет вопрос, о DLL injection. На battle.net сервере, это потенциально опасный способ, т.к. ваша DLL может уйти на исследование в отдел по защите. Ведь это самый распространенный подход как в читерстве так и в ботоводстве внедрить свою DLL. И помните, популярность подхода, прямо пропорциональна вероятности попасться.
Следующий вопрос, это «почему не c++?». Это уже дело вкуса. Если вы можете сделать лучше и быстрее на c++ — делайте, но на данный момент, я кроме неконструктивной критики подхода ничего не увидел. Ни одного примера реализации на c++. Ведь странно, когда все могут и никто не делает.
Еще интересный вопрос «почему не x64?». Если поднять 4 виртуалки с win7×86 и запустить бота на каждой, при этом ресурсов потреблять от хост-машины они будут меньше чем win7×64. Ну и вообще мне индифферентно на какой платформе бегает бот, да и ему тоже. Этот вопрос актуален будет при написании читов, когда сам играешь.
На все остальные вопросы ответ будет следующим: «За последние 4 года на battle.net сервере, я ни разу не получил бан или предупреждение»
По поводу ошибок в коде и постах. Да, у меня возможны опечатки и ошибки, как и у всех вас и если вы заметили их, то не стоит ими размахивать, как флагом на красной площади на параде, просто напишите мне личное сообщение и я обязательно исправлю ее. Это продемонстрирует не только ваши внимательность и знание, но и вежливость и воспитанность. Это как если во время еды у человека на бороде остался соус или сама еда. Вы считаете это правильно за столом обсудить, на сколько концептуально она повисла?
2. Поиск офсетовДля поиска офсетов, я предпочитаю поиск по маске. Естественно, что самый лучший способ найти указатель на функцию — это отладка и только она даст 100% гарантию, что вы нашли именно то, что нужно. Но это процесс может быть очень утомительным и долгим, да и как для новичков в 5 статей объяснить про отладку, брейкпоинты и дизассемблер, когда и так почти ничего не понятно, как я заметил. Поэтому я использую в статье поиск по маске. Давайте рассмотрим все на примере. Для начала скачаем 2 разных файла Wow.exe, одной мажорной версии (я взял 5.4.7 17898 и клиент WowCircle-32.exe 5.4.7 18019). Наша задача найти офсеты для Морфера при этом офсеты для 17898 нам известны:
CGUnit_C__UpdateDisplayInfo = 0×42E3A8, CGUnit_C__OnMountDisplayChanged = 0×42E193, CGUnit_C__UpdateScale = 0×424AE3, Открываем в IDA Wow.17898.exe, ждем пока он загрузится, скроллим в самый верх, смотрим Imagebase = 400000 и запоминаем это значение.Затем жмем G и переходим по адресу CGUnit_C__UpdateDisplayInfo + Imagebase = 0×82E3A8Открываем его в Hex-View нажатием Alt+3, копируем первых 15 байт — это первые 8 инструкций в функции, которые не изменятся при компиляции, т.е. избегаем команд типа jxx xxxx, push offset xxxx, call xxxx, xxx dword_xxxx и т.д.После всего запускаем новый инстанс IDA уже для исследуемого файла, жмем Alt+B, вводим скопированную последовательность байт и ищем Ctrl + B, если что-либо нашлось, то нужно сравнить c оригиналом.Как видите, эти функции идентичны, за исключением указателей на них. Для точности нажмем еще раз Ctrl+B и если ничего не выдало, значит офсет найден. Все может быть гораздо сложнее, когда первые 15–20 байт в функции изменились при компиляции. В этом случае можно поискать в ней статический кусок, который не содержит меток и переходов либо придется заменить некоторые байты вопросами в шаблоне поиска. Вот выдержка из справки по поиску в IDA: CD 21 — bytes 0xCD, 0×2121CD — bytes 0xCD, 0×21 (the order depends on the endiannes)«Hello», 0 — the null terminated string «Hello«L"Hello» — 'H', 0, 'e', 0, 'l', 0, 'l', 0, 'o', 0B8??? 90 — byte 0xB8, 4 bytes with any value, byte 0×90
Например для поиска CGUnit_C__UpdateScale я возьму не первые 15 байт, а с 21-го по 35-ый, т.к. там нет нежелательных инструкций. Можно для этих целей так же использовать IDA плагин MakeSig.P.S. Если у кого-нибудь есть силы и желание написать статью про то, как искать адреса функций отладкой, буду признателенP.P. S. Ошибки и опечатки в личное сообщение