Мини-исследование, улучшение и бот для игры Shadow Worlds

Жёлтый заголовок: исследуем MMORPG, добавляем читерских фич и пишем неуловимого бота.
Картинка для привлечения


Что это за игра и что меня с ней связывает


На Shadow Worlds я наткнулся лет 10 назад, и она меня полностью захватила. Мы играли в неё с друзьями по ужасному модемному соединению, мочили крысок и зомбиков, убегали от орков, вскрывали сундучки, радовались победам над боссами и кричали «туууупая игра!» когда умирали из-да дисконнекта.

Что в ней такого особенного, трудно объяснить. Даже по тем временам в ней была достаточно примитивная графика, почти никакого разнообразия, но что-то нас к ней притягивало. Может быть дело в синдроме утенка.

С тех пор много воды утекло, но время от времени я скачивал клиент игры, создавал новый аккаунт и играл пока не надоест, после чего удалял все следы пребывания игры с винта. Всякий раз убеждался, что в ней годами ничего не меняется — те же локации, те же монстры, все та же ругань в глобальном чате, ничего нового, разве что баланс туда-сюда подкручивается.

Месяц назад, разгребая тонны спама в старом почтовом ящике, я обнаружил письмо от SW Team. В нём говорилось, что недавно запущен новый сервер с новыми картами, предметами и прочими приятностями. В моём мозгу сработал триггер, что пришло очередное время окунуться в ностальгию.
Регистрируюсь, качаю клиент, и вот я в игре.

Первое, что видит каждый новый игрок.


Можно опять убивать мышек в подземельях, зомбей на кладбище, собирать денежки, купить рыболовную сеть… всё как раньше, в общем.

Ощутив себя снова молодым, наигравшись, ближе к вечеру воскресенья у меня возникло любопытство: как игра общается с сервером? Ведь, судя по примитивности интерфейса (который я упомяну далее) и отсутствию прогресса за много лет, вполне можно ожидать простоту и в используемом протоколе. Запустил Wireshark, и увидел в логах кашу из байт, ничего осмысленного. Окей, значит будем смотреть изнутри.

Открываю MUDClient.exe, главный ехе-шник игры в IDA, DeDe и OllyDBG. В IDA — чтобы почувствовать себя крутым, в Olly — потому что лучше отладчика не сыскать, в DeDe — потому что игра написана на Borland C++Builder.
Всё проходит на ура, ехе не запакован и не защищён сверху никаким протектором.

В DeDe мы видим любопытные названия процедур, это вселяет уверенность, что можно будет наскоком получить много полезной информации (никакой защиты).

В Olly возникает небольшая проблема: дело в том, что игра при старте всякий раз проверяет обновление, выкачивает его и перезапускается.


Таким образом, запустив в отладчике MUDClient.exe, он сразу завершается, а вместо него стартует run.exe, да ещё и с запросом администраторских привилегий, и мне приходится вводить админский пароль. Разбираемся.

Открываем в OllyDbg run.exe и ставим точку останова на CreateProcessW. Получаем такое:

Волшебный параметр E8DA81A3FFE9B1. Не будем выяснять, является ли он какой-то контрольной суммой или просто секретом, а возьмём и создадим bat-файл
start MUDClient.exe E8DA81A3FFE9B1

Игра запускается, ввод админского пароля не требует, обновляться не пытается. Победка.

Теперь мы можем в Olly указать аргумент командной строки E8DA81A3FFE9B1, игра будет думать, что проверка обновлений не требуется, и мы спокойно можем приступить к отладке. Да и когда просто хочется поиграть, удобнее запустить этот bat-файл. Волноваться о том, что мы пропустим важное обновление не стоит: всё равно в них не бывает свежих ехе-шников, обновляется игра примерно раз в неделю (фишка данного сервера), но обновление содержит только новые файлы игровых локаций.

Чем займёмся? А давайте поохотимся на тех, кто охотится на игроков, то есть на администраторов, которые в игре зовутся Game Master'ами (GM). Их задача отслеживать нарушителей правил, особенно AFK+Macros, то есть когда игрок запускает простенькую программку, которая копает за него руду или рыбачит, а сам уходит по делам. Это запрещено правилами.

Сам я никогда это правило не нарушал (честное слово), и если видел подобное, старался привести к такому игроку зомбиков, а потом обчищал труп. Это весело. Зачем же мне ловить GM'ов? А затем, что проверку довольно легко проморгать если ты на работе, игра на втором мониторе, и ты её случайно перекрыл другим окном, при этом игроки активно троллят друг друга в общем чате. А если то и дело отвлекаться на игру, то работать не получается. Значит надо сделать сигнализацию: как только нас пытаются проверить, посылать какой-то сигнал, например вспупыживать окно игры и мигать им, проигрывать мелодию, высылать СМС.

Как происходит проверка? Администратор телепортируется к тебе, при этом он выглядит как обычный игрок нулевого уровня, только гильдия у него GAME MASTERS, и задает простой вопрос «тут?» или «проверка на afk». Если не последует ответа в течение пары минут, а персонаж продолжает прокачку навыка, значит его нет за клавиатурой и он отправляется в бан.

К отладчику! Входим в игру персонажем Imthebot, потом долго ищем живую душу — в данный конкретный момент в игре онлайн всего 19 человек (не тысяч и не миллионов), что не так мало, бывает и меньше. В итоге натыкаемся на какого-то Bank.

Bank и не подозревает, что он особенный


Быстро хватаем отладчик, пока Bank не сбежал, и осуществляем поиск строки «Bank» по памяти процесса. И находим.


Рядом с именем ещё занятные циферки, а чуть выше видим свой ник в окружении таких же циферок. Боевой дух повышается, любопытство и желание копать растёт.

Но тут огорчение: довольно скоро на месте этого блока данных появляется мусор, а строка «Bank» обнаруживается уже по множеству других адресов. То есть мы нашли не статичный адрес, что было бы удобно, а просто фантом, продукт жизнедеятельности каких-то процедур. Значит будем искать исток, где эти данные формируются.

Ставим бряк на функции recv в модуле WSOCK32.dll. Происходит множество срабатываний, например, такое:

В буфере, который заполняет recv пока что что-то бессмысленное, как в результатах логирования Wireshark. Поднимаемся по стеку вызовов выше и видим, что в результате обработки одним из call'ов в памяти образуется человекочитаемая строка


Жаль, Bank ушёл по делам


Если продолжить трассировку, окажемся в ядре системы. Дело в том, что вызов recv осуществляется в потоке, задача которого сформировать строку в памяти, а её потом заберёт и обработает главный поток игры. Можно поставить hardware breakpoint на сформированную строку и оказаться в обработчике главной нити.

Некоторое время, сидя на работе, я развлекался тем, что логировал приходящие сообщения в файл средствами OllyDbg. Делается это установкой условной точки останова (Shift+F4)


В результате у меня формировались гигантские портянки данных.
Пример портянки, когда мимо прошёл игрок Zloy
0042A8CC New thread with ID 00000418 created
0044A435 COND: 0:448:P222:Imthebot:1014:46:133:1000:1000:0:0:0:0:0:0:;G0000000
Thread 00000418 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 000007F0 created
0044A435 COND: 0:448:P212:Imthebot:1014:47:133:1000:1000:0:0:0:0:0:0:;G0000000
Thread 000007F0 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 00000288 created
0044A435 COND: 0:493:P322:Imthebot:1014:48:134:1000:1000:0:0:0:0:0:0:;P610:Zloy:1009:56:138:1000:1000:0:0:0:0:0:0:;G0000000000
Thread 00000288 terminated, exit code 4BDFF7C (79560572.)
0042A8CC New thread with ID 000009B0 created
0044A435 COND: ???
Thread 000009B0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 000008E0 created
0044A435 COND: 0:493:P222:Imthebot:1014:50:134:1000:1000:0:0:0:0:0:0:;P620:Zloy:1009:55:138:1000:1000:0:0:0:0:0:0:;G0000000000
Thread 000008E0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 000007A0 created
0044A435 COND: Ok

0044A435 COND: 0:493:P202:Imthebot:1014:50:134:1000:1000:0:0:0:0:0:0:;P710:Zloy:1009:54:137:1000:1000:0:0:0:0:0:0:;G0000000000
Thread 000007A0 terminated, exit code 4FFFF7C (83885948.)
0042A8CC New thread with ID 00000C3C created
0044A435 COND: ???
Thread 00000C3C terminated, exit code 51FFF7C (85983100.)
0044A435 COND: Ok



И вдруг мне в персональный чат приходит сообщение от пользователя GM с текстом «GM-GM — check for AFK // ГМ — проверка на АФК». При этом рядом со мной никого не было. Я аж подпрыгнул от неожиданности. Поприветствовав таинственного GM я полез на форум и обнаружил свежайшую тему, в которой говорилось о реформе GM-проверок. Коротко: теперь GM не прилетает к тебе, а вводит специальное заклинание, в результате чего он начинает видеть персонажа, но не знает кто перед ним — ни ник, ни уровень, ни даже внешний вид определить нельзя. Видны только действия которые ты совершаешь и противники рядом (которые заменены на мышек для большей анонимности). Таким вот образом решалась проблема нечестных гейм мастеров.

Это был дополнительный вызов: мало того, что надо отслеживать игроков рядом с собой, так ещё и персональный чат. Вопрос от GM, который мне прилетел, попал в лог, вот он:
0044A435 COND: 48:457:01GM-GM - check for AFK // ГМ - проверка на АФК;I41:46:35;P002:Imthebot:1001:53:35:1000:1000:0:0:0:0:0:0:;G00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


Пора писать патч. Что от него требуется? Он должен передавать строку, которая прилетает с сервера, в моё внешнее приложение, которое уж разберётся, что с ней делать. Для этого буду использовать посылку сообщения WM_COPYDATA моему окну.

Быстрое гугление дало такой код:

var
   aCopyData: TCopyDataStruct;
   hTargetWnd: HWND;
begin
   with aCopyData do
   begin
     dwData := 0;
     cbData := StrLen(PChar(Edit1.Text)) + 1;
     lpData := PChar(Edit1.Text)
   end;
   // Search window by the window title 
  // Fenster anhand des Titelzeilentext suchen 
  hTargetWnd := FindWindowEx(0, 0, nil, PChar('WM_COPYDATA-Receiver'));
   if hTargetWnd <> 0 then
     SendMessage(hTargetWnd, WM_COPYDATA, Longint(Handle), Longint(@aCopyData))
   else
     ShowMessage('No Recipient found!');
end;



В импорте MUDClient.exe нет функции FindWindowEx, зато есть FindWindowA, поэтому буду пользоваться ей.

Прыжок на патч будет в этом месте:

Здесь есть сформированная строка, и три NOP пойдут на пользу


Я просто запишу поверх трёх завершающих команд в процедуре свой «JMP 00515080»
При этом следует помнить, что съев две команды POP, мы должны их восстановить.

Патч-ретранслятор, который я разместил в большом блоке памяти заполненном нулями, выглядит так:


Код приёмника ещё проще:

procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;

------

procedure TForm1.WMCopyData(var Msg: TWMCopyData);
begin

  try

  StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);

  DoProcessNewLine(sText);  //можно обрабатывать её как мы хотим

  except end;

end;



Не патч, а лоадер
На самом деле, ехе-файл я не изменяю. Вместо этого, моё приложение запускает MUDClient.exe и с помощью команды WriteProcessMemory модифицирует код в памяти. Это позволяет распространять «бота» в виде одного ехе, а не двух, а ещё позволяет избежать нарушения пункта в лицензионном соглашении (Grammar Nazi, к бою)
13. Вход в игру через старый клиент, а так же любая модификация exe-файла игры.



Тут следует рассказать, что за строку мы получаем, что в ней ценного. Например, информация о персонажах на экране, включая себя и мобов:


Выше приводил часть строки, в которой было сообщение в приват от персонажа GM.

Этой информации достаточно для того, чтобы отслеживать как персонажей, которых мы видим, так и GM-невидимок, которые пишут нам в приват. На скриншоте видно одно из окошек «бота», отображающее юнитов на экране.


Типичный пример нарушения правил. Качает щит на мышках AFK, и даже не обратил внимание, что я попортил одной из них здоровье

Продолжение в картинках








Правда, весело?


Раз уж исследовал приём данных с сервера, надо бы поглядеть хоть одним глазком, что клиент передаёт со своей стороны. Не стану обременять читателя листингами, расскажу вкратце, что точно так же как с recv, я поставил брейкпоинт на send, поднялся по стеку вызовов и нашёл место, где строка ещё не закодирована. Примеры команд:

VR — шлётся непрерывно, этим клиент говорит о том, что он жив и хочет обновить состояние
QR — выход персонажа из игры
UI4:0 — использовать 4 ячейку инвентаря. Например, выпить бутылочку, съесть рыбу, прочитать свиток
MB012:4:0 — переместить предмет инвентаря из 12 ячейки в 4
ACПривет0000 — сказать «Привет» в локальный чат
PC*Vasya*Привет — написать «Привет» в приватный чат игроку Vasya
SP3 — сесть (когда персонаж сидит, у него быстрее восполняется здоровье и магическая энергия)
ACesani gre olam1003 — прочесть заклинание огненного шара на персонажа с ID1003

и так далее.

Каждая команда обязательно завершается символами 0x13 и 0x10


Как этим воспользоваться? Подделывать команды, конечно же. Можно написать более удобный чат, научить персонажа автоматически съедать рыбу, автоматически садиться. Конечно, многое можно сделать эмуляцией нажатий клавиш и кликов мышкой. Многое, но не всё.

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

Тут можно рассказать про казус
Пока я тестировал новый патч, мне написал друг «слушай, это не ты там балуешься?». Я не понял его вопрос, а потом обратил внимание на глобальный чат, в котором игроки массово возмущались перезапуском сервера. При этом их выкидывало из игры, происходил небольшой откат игрового состояния, и все мобы, включая боссов, появлялись заново. Я не поверил сначала, но сделал несколько проверок, и в результате одной сервер рестартовал. В чате началась полная истерика, но винили все какого-то Elf (если не перепутал ник) и просили его прекратить безобразие. Не знаю почему Elf стал козлом отпущения, то ли он заслужил славу местного хакера, то ли потому что он параллельно админит игру и помогает себе используя служебное положение, а потому имеет дурную славу. Не знаю.

В общем, я сделал в своей проге кнопку «уронить сервер», которую потом ещё пару раз успешно проверил (потому что не верил, что уронить сервер так просто), что нас с другом весьма веселило «кто-то держит этот сервер за яйчишки». А через несколько часов она перестала работать, видимо, залатали на сервере ошибку, и это хорошо, так как я написал в личку главному админу письмо, в котором признался, что это был я, как я это сделал, и вообще какими делами я занимаюсь, но перед нажатием «отправить», решил проверить последний раз и… обнаружил, что сервер не сломался.

Да, дело было в том, что я не завершал команды завершающей последовательностью 0x13 0x10.


Из прикольного. В игре есть возможность «посмотреть» на персонажа рядом, для этого надо щёлкнуть по нему с зажатым ALT.


Любопытно, что за команда отсылается при этом. Щёлкнем по самому себе, в логе отобразится такое:
00515160   COND: ID1006

Попробуем изменить 1006 на 1001, получаем


А ведь этот игрок не просто не рядом с нами, и даже не в другой локации, а вообще в другом мире (куда уходят все, кто прокачался до 21 уровня, так как в первом мире дальше опыт не идёт).
Это даёт возможность знать, кто сейчас онлайн. Но стоп, ведь список игроков онлайн доступен на сайте игры. Это так, но в нём не видны администраторы. Также, для сокрытия админов, при попытке послать им в приват сообщение, даже если админ онлайн, отобразится «не удалось отправить сообщение», как если бы он был не в игре. А благодаря найденной возможности, мы можем всегда знать, что тот или иной GM сейчас работает. Задача облегчается тем, что достаточно делать перебор с ID1000 до… ID1100, чего хватит за глаза. Когда кто-то выходит из игры, и освобождает ID, следующий игрок забирает ID себе. Таким образом, нам не надо угадывать ID гейм мастера. И в качестве бонуса мы можем следить за состоянием здоровья любого игрока, даже если он от нас убежал.

Например, пошлю команды
ID1020

Результат:
Osiris:0:Invictus:53:0:705:705:1:124:190:189:0:139:187:131:184:186:0:0:0:


ID1021

Результат:
Admin:0::80:0:1880:1880:0:0:136:555:171:138:167:133:155:163:0:0:0:


Довольно вкусно.

Инвентарь


Дальше мне захотелось сделать что-то действительно полезное. В игре очень неудобная работа с инвентарём. Открыть мешок на ходу и пользоваться им нельзя, нам доступны только три быстрые ячейки, и если нужный нам предмет находится далеко, то придётся очень долго листать.

При этом стоит упомянуть весьма тормозной курсор, который не привязан к аппаратному, а рисуется игрой самостоятельно, то есть тыкать им в мелкие стрелки листания быстрого инвентаря — пытка, щёлкать по предметам тоже.

Решил сделать свой инвентарь в виде отдельного окна, который можно будет использовать так же как игровой. Результат:


Сначала я выяснил, по какому адресу игра хранит начало инвентаря. Тут я прибёг к помощи ArtMoney. Обычным отсеиванием неизвестных значений и перекидыванием руки зомби между первой и нулевой ячейкой, я быстро нашёл такое место в памяти:

Начиная с 00550AC4 хранятся четырёхбайтовые значения, которые определяют предмет. Правда, просто взять и сопоставить эти значения изображениям в папке tga/items, не получилось. Рука зомби, как мы видим, имеет индекс 151, но в папке с картинками руке соответствует другое значение.


Простым вычитанием 151-149 не отделаться, так как для других предметов разница может быть совсем другой.

Открываем в WinHex файл ITEMS.DAT, который лежит всё в той же папке tga/items.


Как видим, он не похож на зашифрованный, хоть на первый взгляд из полезного в нём только описание предметов на русском и альтернативном языках (в немецкой версии будет немецкий). Немного копошимся в файле чтобы найти какую-то структуру, и находим довольно быстро: если посчитать расстояние между началом каждого описания предмета в байтах, то оно каждый раз равно 0x2E2 (738) байт. То есть каждый блок информации об N-предмете начинается с 738*N.

Но что такое 151, который соответствует руке зомби? Очень просто: умножаем 151*738, отступаем от начала файла на эту величину (0x1B34E) и видим:

Как из этой информации получить название графического файла, в котором нарисована рука? Лично я увидел глазами, но можно и воспользоваться поиском по значению, что по адресу 1B5A4 лежит искомое 149 (149.bmp000.png). Вывод: через 0x256 байт от начала блока данных идёт число 2 байта, равное номеру PNG файла с картинкой.

Инвентарь почти готов. Но простого отображения мало, надо ещё использовать предметы из него. Как мы выяснили ранее, если послать серверу команду UIX:0, где Х это номер ячейки, предмет будет использован. Теперь можно бегать своим магом и пить бутылки маны на ходу, воином пить бутылки восполнения здоровья, не открывая внутриигровой инвентарь, а тем более неудобный «быстрый».

Как ловить рыбу
Одна из мирных профессий в игре это рыбак (он же кузнец, но это не важно). Нужен он не просто для ловли рыбы, а магической рыбы «магички». Обычно ловля автоматизируется макросом, который щёлкает правой кнопкой по рыболовной сети, а потом левой кнопкой мыши по водоему. Магичка ловится редко, в основном попадаются бесполезные мелкие рыбёшки, которых надо съедать (это прокачивает навык лечения). Наловишь полный рюкзак рыбы, а потом нудно щёлкаешь по ненужным рыбёшкам, оставляя магичек.

Но так как у нас теперь есть такой мощный инструмент, можно запросто автоматизировать ловлю магички. Для этого в цикле пробегаемся по инвентарю и даём команду на съедение всех плохих рыбёшек. В итоге за рыбаком нет надобности следить — он сам наловит драгоценную рыбу.

Немного технического извращения: если просто послать серверу команду UIX:0, он действительно её съест, и даже пошёл клиенту «OK», вот только клиент понятия не имеет, к чему этот OK относится, ведь он ничего не посылал, и проигнорирует его. Поэтому клиент будет продолжать думать, что рыбка/бутылочка всё ещё цела. Решил проблему очень грубо: после отсылки команды UIX:0 надо записать при помощи WriteProcessMemory в соответствующую ячейку нули. И рыба сразу пропадёт.


Для большей функциональности инвентаря осталось сделать элементарное: при клике по предмету «нашего» инвентаря, быстрый инвентарь игры должен прокручиваться на этот предмет, а двойной клик передаваться в игру. Тоже было сделано элементарно: ArtMoney нашел адрес по которому хранится позиция быстрого инвентаря, и старой доброй WriteProcessMemory пишу туда нужное мне число. Двойной клик посылается командой
PostMessageW(h, WM_LBUTTONDBLCLK,wParam,lParam);


Никогда ещё работа с инвентарём в Shadow Worlds не была такой удобной, скорость выбрасывания предметов (очень полезно рудокопам) увеличилась в разы, я почувствовал себя изобретателем рычага и колеса.

Авто исчезновение и бан


Немного скучных соплей и внутриигровых перипетий

Сделав свою прогу весьма функциональной, я отвлёкся от неё, и увлёкся игрой. С другом стали качать кузнецов — в рабочее время на них не надо особенно отвлекаться: копай себе руду, потом изредка куй из неё простые предметы, а прога упрощала взаимодействие с интерфейсом.

Беда первая, иностранцы. На сервере довольно много иностранных игроков, которые ведут себя агрессивно по отношению к русским. Очень скоро мы с другом поняли, что копать руду просто так не получится — в шахты то и дело залетают боевые персонажи, часто группой, гораздо выше тебя по уровню и устраивают бойню. В результате опыт, накопленный потом и кровью, улетает в трубу, да ещё и деньги приходится искать на покупку новой кирки и молота. На иностранцев мы не обижались, воспринимали скорее как враждебных мобов, только костерили их между собой и радовались, когда получалось от них улизнуть.

Беда вторая, наши. Русские игроки высоких уровней сначала нас не трогали, так как пытались воевать с «америкосами», как их зовут на сервере, хотя там не только американцы. Но очень скоро иностранцы заняли доминирующее положение, а «наши» поняли, что противопоставить сильной гильдии ничего не могут, и стали отрываться на нейтральных персонажах новичков. Попытки договориться ни к чему не приводили — им в кайф было нас (новичков) мочить, в ответ на вопрос «зачем?» только неадекватная нецензурщина. Администрация же никак регулировать вопрос не хотела — «это PvP-сервер», таков их ответ. В моём понимании, PvP это когда у игроков дуэль. А то, что происходило в шахтах — просто живодёрство.

Возникла дилемма: либо перестать играть, либо найти способ выживания. И я решил, что выбора нет, и пошёл на нарушение пункта правил
8. Использование [...] программ использующих автоматический процесс входа/выхода персонажа из игры, багов игры.



Итак, пишем автоматизацию выхода из игры в случае опасности. Для этого имеется всё необходимое: как только появляется персонаж на экране, мы получаем извещение об этом. Сделать выход из игры можно минимум двумя способами: послать команду QR на сервер, или же проэмулировать нажатие мышкой на кнопку «выход». Нажимать надо быстро, так как если персонажа один раз ударят, в течение минуты он должен оставаться в игре.

Сделано. Также добавлен белый список, в который можно добавлять тех, кому доверяешь. И началось веселье: мы добывали руду, в шахту врывались злые иностранцы или «наши» с целью окропить свой меч нашей кровью, но успевали увидеть только моментально исчезающие фигуры нубо-кузнецов. Устраивали засады, но мы как появлялись, так и исчезали, этакие ниндзя.

Ещё мне пришла в голову преступная мысль (только ради науки): сделать полностью автоматического Imthebot, который будет рыбачить или копать руду, при этом избегать опасностей и отвечать на простенькие вопросы GM'ов. Сколько дней/месяцев он бы продержался, какой уровень навыков бы смог прокачать? Несколько дней я вынашивал идею, но отказался, так как моё увлечение прокачкой кузнеца было слишком сильно, и не хотелось тратить время на «глупости».

И тут случилось ужасное: как всегда я копал руду, и ко мне телепортировался Admin. Сработал автовыход, я тут же быстро нажал «войти» и поздоровался. Но было поздно

Персонаж получил 999 дней бана за использование программ автоматически выходящих из игры при виде персонажа.

Ох, как же я себя корил за то, что вовремя не добавил Admin в белый список, ведь постоянно думал на эту тему, но руки не доходили. Я попытался поговорить с админом, но натолкнулся на стену молчания. Похоже, он уже привык к нытью тех, кто забанен. После долгих приглашений к диалогу он только добавил, что «на вас поступила жалоба, я проверил, и подозрение подтвердилось». Забавная жалоба: не могу убить своим крутым магом/воином голого ремесленника низкого уровня, поэтому попрошу админа разобраться.

Потеря кузнеца стала для меня очередной логической точкой «тууууупая игра», после которой игра вновь перемещается в воспоминания.

Немного правды и рассуждения о добре и зле


Очевидны вопросы: не пострадает ли игра после статьи, не нахлынут ли читеры и багоюзеры? Нет. Во-первых, конкретного кода в статье почти нет, одни идеи. Во-вторых, все эти идеи не совсем актуальные, то есть они будут работать, но коряво. К примеру, код, которым я подделываю команды, уходящие на сервер, нестабилен. Так как он перетирает нормальные запросы клиента, через какое-то время клиенту становится дурно, и происходит дисконнект. Как делать правильно, я не буду рассказывать — статья от этого лучше не станет. Улучшение алгоритма автовыхода также никому ничего не даст: админ легко может создать другого персонажа для проверки, и месяцы прокачки пойдут в null, ибо админы весьма суровы — никакой демократии и презумпции невиновности.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 16

    +2
    Очевидны вопросы: не пострадает ли игра после статьи, не нахлынут ли читеры и багоюзеры?

    Более интересен вопрос, как повлияет данная статья на этот показатель:
    в данный конкретный момент в игре онлайн всего 19 человек
      0
      Так как это игра «моего детства», мне будет приятно, если статья повлияет на игру положительно. Не думаю, что кого-то в 21 веке сильно может заинтересовать такой олдскулл, но чем чёрт не шутит.

      А если помечтать, и предположить, что часть волны пользователей Хабра останется в игре, это будет вообще прекрасно, я верю, что общая адекватность происходящего в игре от этого явления поднимется.
        0
        Если все же произойдут какие-либо изменения, думаю многим будет любопытна сводная информация по этому поводу :)
      +5
      Ага, мы любим читать теги;)
        0
        Очень жаль что вы решили совместить хакерство и прокачку «честного» персонажа. Было бы веселее оставить все попытки играть честно и пытаться бы изучить все возможности взлома сервера. Ведь самое веселое — находить те вещи, которые с помощью обычного клиента сделать нельзя, но возможно сделать с помощью дырявости протокола. Т.е., например, самое простое и, обычно, бесполезное — крашить сервер с помощью неправильно сформированного пакета. Чуть веселее — находить любые вычисления, которые происходят на стороне клиента, и обманывать их. Заменять генератор случайных чисел на константу, убивать людей одной командой, получать админские права и т.д. и т.п. :)
          +2
          Я не
          решил совместить хакерство и прокачку «честного» персонажа

          Мне было любопытно сунуть нос во внутренности игры, а дальше само как-то получилось.

          Было бы веселее
          убивать людей одной командой, получать админские права

          Согласен, было бы веселее, но чисто интуитивно думаю, что в протокол игры, доступный на клиентской стороне, не заложены такие возможности. Да и о таких вещах мне бы сразу пришлось рассказать администрации, и статьи бы не было.
            0
            Насколько я знаю, клиентские мморпг, где на стороне сервера проверяют все-все-все данные от клиента на правдободобность, крайне редки. И всегда можно найти какую-нибудь дырку, позволяющую, например, дублировать предметы, заменять шансы с 1% на 100% и пр.
          +5
          … вот только клиент понятия не имеет, к чему этот OK относится, ведь он ничего не посылал,…


          У «проблемы» есть очень элегантное и проверенное временем решение — инжекты. Если объяснять в двух словах — вам надо подняться в отладчике чуть выше чем отсылка клиентом пакета и найти функцию-обработчик щелчка на предмете, которая и инициирует посылку пакета серверу.
          Эта функция, вероятнее всего, будет принимать один параметр — номер ячейки, предмет из которой следует использовать.

          Дальше начинается «колдовство»:
          1) вам необходимо изготовить процедуру, которая будет вызывать эту найденную функцию с нужным параметром, так, чтобы клиент не крашился.:
          Например, вот так (пример взят для другой игры, просто для иллюстрации)
          procedure AutoAttackCall(targetWid : dword); stdcall;
          var
            Address: pointer;
          begin
            // адрес нужной нам функции
            Address := Pointer(AutoAttackAddress); 
              asm
                pushad
                // готовим "окружение" для нашего вызова
                mov   esi, dword ptr [BASE_ADDRESS] 
                mov   esi, dword ptr [esi + PW_ROLE_BASE_OFFSET]
                // кладём в стек параметры для функции
                push  $0
                push  $80000000
                push  targetWid
                mov   ecx, esi
                // вызываем
                call Address
                popad
              end;
          end;
          



          2) Вызвать эту функцию в контексте процесса игры, так как будто это сделала она сама:

          procedure THostPlayer.StartAutoAttack(targetWID : dword);
          begin
              InjectFunc(@AutoAttackCall, @targetWID, SizeOf(targetWID));
          end;
          


          Код функции InjectFunc
          procedure TProcess.InjectFunc(Func: Pointer; aParams: Pointer; aParamsSize: DWORD);
          var
            hThread: THandle;
            lpNumberOfBytes: cardinal;
            lpThreadId: DWORD;
            ThreadAddr, ParamAddr: Pointer;
          begin
            if self.processHandle<>0 then
            begin
              // ---- Выделим место в памяти процесса и запишем туда нашу функцию
              ThreadAddr := VirtualAllocEx(self.processHandle, 0, 256, MEM_COMMIT, PAGE_READWRITE);
              WriteProcessMemory(self.processHandle, ThreadAddr, Func, 256, lpNumberOfBytes);
          
              // ---- Также запишем параметры к ней
              ParamAddr := VirtualAllocEx(self.processHandle, 0, aParamsSize, MEM_COMMIT, PAGE_READWRITE);
              WriteProcessMemory(self.processHandle, ParamAddr, aParams, aParamsSize, lpNumberOfBytes);
          
              // ---- Создадим поток, в котором это всё будет выполняться
              hThread := CreateRemoteThread(self.processHandle, 0, 0, ThreadAddr, ParamAddr, 0, lpThreadId);
          
              // ---- Ожидаем, пока функция отработает
              WaitForSingleObject(hThread, INFINITE);
          
              // ---- Подчищаем за собой
              CloseHandle(hThread);
              VirtualFreeEx(self.processHandle, ParamAddr, 0, MEM_RELEASE);
              VirtualFreeEx(self.processHandle, ThreadAddr, 0, MEM_RELEASE);
            end
          end;
          

            0
            Не сочетается с последним тегом к статье. ) Без шуток: очень красиво, но слишком круто для целей, которые я перед собой ставил. Спасибо.
            +1
            Эх… своей статьей вы напомнили мне про игрушку Сфера 2 Арена, в которую я когда-то играл. Онлайн там был жухлый, геймплей однообразный, куча багов — но народ все равно до последнего играл в неё, пока сервер не закрыли)
              +1
              Ностальгические скриншоты.
                +1
                Так вот как она называется! Играл в нее после первой установки Gentoo. Никогда не забуду как попросил какого-то парня русского показать где сундуки стоят, и он меня завел в какую-то пещеру, где pvp включается. Успел убежать, и вслед мне летела фраза «ты куда? там сундуки...». По ощущениям очень напоминает UO.

                Интересно, если админы живые, что же они ее не пилят? Какими чувствами руководствуются, администрируя полу-живую игру?
                  0
                  тоже помню одну подставу
                  Самый первый раз, когда я вошёл в игру, задавал вопросы в городе «что тут как», какой-то парень позвал меня поближе к охранникам и попросить ударить его. Я доверчиво это сделал, и тут же меня насмерть сразило заклинание стражника.

                  — и что ты этим хотел показать?
                  — а то, что здесь нельзя никому доверять!

                  Это было очень смешно и пафосно.



                  если админы живые, что же они ее не пилят?

                  Я так понял, что благодаря открытию этого сервера, игра как раз и начала потихоньку пилиться — нужный человек получил исходники и ресурсы в свои руки. Но занимается он игрой в свободное от работы время, денег она ему не приносит.

                  Какими чувствами руководствуются, администрируя полу-живую игру?

                  Часть админов остаются в игре из патриотизма, привязанности к ней. Некоторые — чтобы иметь игровое преимущество.
                  +1
                  плюсую)
                    +1
                    Спасибо за статью. Сам рубался в эту игру на диалапе. =)
                      +1
                      Картинки и описание, причём и описание автоматизации очень напоминает Ultima Online с его Yoko Injection и EasyUO :)

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                      Самое читаемое