20150309

Можно ли при помощи Cheat Engine взломать сетевую игру?

...задал мне вопрос один из зрителей на youtube. Попробую ответить.

Во-первых, давайте немного разберемся, как работают сетевые игры. Есть клиент (К), в который играет игрок, где рисуется вся красивая графика, леса, монстры бегают и рейды на боссах вайпаются. Есть сервер (С), к которому подключаются игроки, который управляет авторизацией, чатом, игровым миром и всем таким прочим. На нем графики обычно нет вообще, часто - даже консольного окна. Представим, что у нас два клиента и один сервер:

(К0)
  ^
  |---------(С)
  v
(К1)


К0 бежит себе по полю, видит монстра. Потому что сервер ему об этом сказал. К0 берет и убивает монстра. Говорит серверу - "Я убил монстра!". Сервер думает и отвечает - вот тебе 3 золотых. К0 видит на экране появившиеся 3 золотых и подбирает их. Говорит серверу - "Я подобрал золото! 3 штуки!!". Сервер открывает у себя блокнотик и записывает - у К0 золота стало на 3 больше. Далее К0 открывает инвентарь (спрашивая у сервера, "Что у меня в инвентаре?"), а сервер ему и отвечает - столько-то золотых, половинка дарницкого и литр кефира.

Бывают такие невообразимо глупые игры, где можно заморозить здоровье на стороне клиента и он просто будет сообщать серверу, что здоровье полное. То есть на стороне сервера не будет никаких проверок на это. Бывает, например, World of Tanks, к которому толком нет читов, потому что весь клиент игры - это большой слайд-проектор, которому сервер выдает картинки, а всю информацию считает у себя. Чуть что не сошлось - клиента выкидывает из игры. Бывает так, что на одни показатели в игре косвенно влияют другие. Скажем, как это было у моих друзей в LineAge - если сильно нагрузить персонажа багажом, у него не будет со временем восстанавливаться здоровье. Если после этого умереть и в момент возрождения разорвать соединение клиента с сервером, то игрок возрождается с 0 здоровья и становится бессмертным. Баг был быстро исправлен, но было весело. (:

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

(К0): Я нашел 3 золота!
(С): Положил тебе в инвентарь 3 золота.

Берем пакет, отправляемый клиентом, исправляем в нем цифру 3 на 10000, а затем отправляем опять. Смотрим, что ответит сервер:

(С): Положил тебе в инвентарь 10000 золота.

Вуаля. Взломали? Вполне. Но это - самый простой случай, обычно все куда сложнее.

20150303

ESP. Часть 2. Выводим найденное в консольку.

А вот и видео!


В нем я вслух и почти построчно разбираю 100 строк кода на Си, которые делают следующее:

0. Находят окно игры
1. Получают хэндл процесса с правами чтения из памяти
2. Читают количество игроков на карте
3. Бегут в цикле, читая адрес каждой структуры игрока
4. Выводят в консоль его здоровье и координаты

В следующем уроке я расскажу, как именно мы будем выводить данные в окно игры (напомню, что это будет НЕ через DirectX-хук - мы легких путей не ищем!) и что для этого понадобится (не бойтесь, одна-единственная, но очень важная переменная в памяти игры. Будет еще немного СЕ), а через урок - покажу, какой код это делает.

ESP. Часть 1. Ищем нужные нам данные.

Сначала - видео:


А теперь, для начала, я приведу наш небольшой план еще раз:
0. Получение данных из игры
1. Чтение данных из игры программно
2. Рисование в окне игры
3. Вывод данных из игры в окно


Итого, руководствуясь видео выше и своей смекалкой, мы получаем следующие данные:

0. Указатель на коллекцию структур игроков
1. Смещения до здоровья и координат в структуре игрока
2. Адрес общего количества игроков
3. Смещения между структурами игроков в коллекции из п. 0

Конечная наша цель - вывести над каждым игроком в игре его текущее здоровье, как вы уже, думаю, догадались.

В следующей части мы напишем небольшую программу, которая будет уметь читать все нужные нам данные и выводить. Пока что - в консоль, а потом - уже на экран игры.

20150110

ESP. Часть 0. Теория.

Всем привет!

Пожалуй, я достаточно много часов проработал, чтобы работа порядком поднадоела. Еще с самого первого момента, когда я заикнулся про 3D-графику в блоге, в ответ уважаемые читатели стали хором спрашивать, когда мы сделаем простенький ESP. Ну, скоро сделаем! Правда, для начала разберемся немного с теоретической частью, потому что эта тема - не поиск значений и даже не дизассемблирование, а нечто чуть более сложное. Итак!

ESP - это сокращение от английского Extrasensory Perception, проще говоря - шестое чувство. Это когда мы знаем что-то такое, чего в нормальных условиях знать не должны. Например, сколько до какого-то объекта метров. Точно, а не на глазок. Или сколько у такого-то монстра здоровья. Выглядит это примерно вот так:














Мы немного разобрались с темой 3D-графики, научились перехватывать функции рисования, так что теперь мы можем рисовать геометрические фигуры, выводить текст а так же заменять или убирать текстуры по нашему выбору. Это-то круто, конечно, но 3D-движок, который просто рисует кучу цветных треугольничков на экран, ничего не знает об имени, здоровье, оружии или чем-то таком. Он просто знает, как рисовать треугольнички. Вот (очень) примерная схема работы игры:

While( true ) {
    Fight();
    Draw();
    PlaySound();
}

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

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

Начнем с первого, а дальше я буду объяснять по ходу дела и рассказывать все более детально.

20140803

D3D: Chameleon Wallhack

Привет, ребята! Для начала, вот видео:


А [вот] ссылка на репозиторий с исходным кодом.

Значит, скачали вы исходники, взяли любимый компилятор и любимую IDE, скомпилировали это дело и ничего не поняли. В видео я, отсылась к предыдущим статьям и урокам, рассказываю о том, что мы написали логгер, позволяющий нам узнать некоторую необходимую для нашей бурной деятельности информацию. А именно - stride, или номер куска экрана, который мы сейчас будем рисовать, и NumVertiles, аргумент функции [DrawIndexedPrimitive], который показывает нам количество вершин того куска, который мы будем рисовать. Мир игры состоит из моделек, модельки - из полигонов. Полигон - это треугольник, то есть у него три вершины. На модельки, собственно, сверху натянуты текстуры, чтобы было видно, что металл - это металл, оружие - это оружие, а враги - это враги. Нашли мы, значит, нужную нам текстуру. Дальше видим такой кусок кода:

pDev->SetRenderState(D3DRS_ZENABLE, false);
pDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
pDev->SetTexture(0, red);
oDrawIndexedPrimitive(pDev, PrimType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
pDev->SetRenderState(D3DRS_ZENABLE, true);
pDev->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
pDev->SetTexture(0, green);

Что мы тут делаем? А всё просто, как выяснилось. Функция [SetRenderState] позволяет нам задать параметры отображения того, что мы сейчас будем отображать. Тут важно помнить, что мы ещё пока ничего рисовать не пытались. У этой самой функции всего два аргумента - что мы меняем и на что мы меняем. Первым вызовом мы утверждаем, будто нужно выключить буфер глубины. Что это такое? Это перспектива, если вдруг так будет понятнее. Экран монитора - он плоский, а игра хочет казаться трёхмерной, так что ей нужно как-то трёхмерность симулировать. Вот эта вот штука отвечает за то, что мы видеть должны, а что - нет. Например, когда какой-то объект находится за стеной. Игра при отрисовке постоянно думает, что и как ей рисовать. Вот небольшая схемка:

0            1             2
             |                   
X -----------|-------------O
             | 

X - это мы. Палочки - это стена перед нами, а O - это нечто за этой стеной. Циферки обозначают позицию одного относительно другого в этом самом буфере глубины. У кого цифра 1 - того мы и видим, у кого больше - значит он чем-то от нас закрыт. Если товарищ О выйдет из-за стены, то у него Z-order (позиция в буфере глубины) станет равным 1, так что игра должна будет его перед нами отрисовать и в результате мы его увидим.

Если отключить буфер глубины, то игра решит, что все игровые объекты в поле нашего зрения находятся перед нами - то есть нету никаких препятствий и никто никого не перекрывает. Получится каша, потому что одновременно будут рисоваться стены, мебель, пол, потолок, оружие, гранаты, люди, враги, союзники, словом - всё. Для того, чтобы вычленить из этой каши то, что нам нужно, мы и использовали наш логгер. В общем-то, только у нужных нам объектов мы буфер глубины и отключили. Давайте вернёмся к коду, а то я заболтался. Отключив буфер глубины, мы говорим, что текущий объект мы будем рисовать сплошным цветом. Сразу после этого мы вызываем функцию [SetTexture], которая указывает, что мы будем использовать текстуру по имени red - красненькую. Напомню, что пока мы ещё ничего не рисовали и не пытались даже. Что мы делаем следующей строчкой? Верно, рисуем, вызвав оригинальную функцию рисования вместо нашей перехваченной.

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

Что получается в результате таких жутких махинаций? А получается так, что выбранная нами моделька рисуется через стены красным, а перед нами - зелёным. Эта фича отличает Chameleon wallhack от обычного. В обычном мы просто выключали бы буфер глубины и всё, а тут удобно, с подсветочкой.

Тэк-с. Я упоминал текстуры. Собственно, чуть ниже, в перехваченной функции [EndScene], мы их и создаём. Тут я в подробности вдаваться не буду, так как этот кусок кода я нашёл и честно скопировал себе, скажу только, что цвет мы задаём вот в этой строчке:

((PDWORD)d3dlr.pBits)[xy] = 0xFF00FF00;

Тут нас интересуют последние три пары чисел шестнадцатиричного значения. Догадливые уже поняли, что это RGB, он же - Red Green Blue, то есть три цветовых канала. Первые две цифры в том числе выше - это прозрачность, которая не используется в данном случае, как я понял. Собственно, FF0000 - это красный, 00FF00 - это зелёный, а 0000FF - это синий. Значения лежат в пределах от 00 до FF. Подходящий цвет можно выбрать в любом графическом редакторе или же в интернете найти.

В той же перехваченной EndScene мы первой строчкой узнаём ViewPort - это, собственно, то, куда мы смотрим, когда играем. Узнаём мы его затем, чтобы выводить туда текст, так что после создания текстур мы создаём шрифт и пишем им на экран, в данном случае - включён ли наш wallhack.

На этом, в принципе, можно бы было и остановиться, но есть ещё одна важная деталь. В комментариях некоторые люди жаловались, что игра вылетает с ошибкой при попытке сделать alt+tab или поменять разрешение экрана. Это чинится перехватом функции [Reset], в которой мы уничтожаем наши текстуры и шрифт, чтобы они не засоряли видеокарту и пересоздались, дабы игра снова могла их рисовать.

Вот теперь - всё. До новых встреч! (:

20140124

Про гитхаб.

Ребята, как вы заметили, я теперь в постах привожу ссылки на свой [репозиторий] на гитхабе. Я не знаю, как лучше организовать процесс, т.к. я там тоже что-то пишу и что-то может не работать или работать не так, как задумано, в тот или иной момент времени. Хранить 100500 папок\архивов с исходниками мне банально негде. Так что варианта два - тащим себе весь репозиторий и разбираемся, попутно глядя в блог и поставив себе 2013-студию, или же смотрим нужный кусок кода и копируем себе в проект. Сейчас это, по сути, песочница для экспериментов, так как я, как и вы, с темой знаком довольно поверхностно.

20140122

D3D: Рисуем прицел.

Привет!

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


Что-то объяснять, кроме комментариев в самом коде, я не буду - всё и без этого понятно. (:

Полный исходник - на [гитхабе]!

20140119

[Опрос] Перепись видеоуроков.

В комментах к этому посту я прошу вас указать те видеоуроки с [ютуба], которые хорошо бы переписать. Пока что я займусь приведением канала в порядок.

Новости по блогу.

Итак, уважаемые! Раз уж у меня нашлись время и силы возобновить публикации своих скромных потуг, то возникает пара вопросов:

Редизайн блога. Переписывание всех старых видео (одно из которых зачем-то удалил гадкий гугл), заливка видео на какой-нибудь другой хостинг или обменник (торренты!). Были мысли понаписать серию статей с красивыми картинками из какого-нибудь abode illustrator, потому что в виндовом пейнте рисовать как-то не слишком солидно. Так же были мысли создать три отдельных секции - Ассемблер, C\C++, C#.

Дело тут всё в том, что си ришотка (C#) достаточно фигово подходит для геймхакинга - он слишком высокоуровневый со всякими своими ООП и прочими классами, не даёт вменяемый доступ к WinAPI, потому что safe code и всё такое, но он красивый-быстрый-удобный для создания GUI, интерфейса, т.е.

Вообще, лично я придерживаюсь такого мнения, что ООП в целом плохо подходит для наших задач, поэтому предпочтение отдаю чистому Си и Ассемблеру. Почему? Потому что я их учил давно, когда компьютеры были медленные и оперативки было мало, так что я люблю компактность и скорость. Если моё с вашим мнение расходится - то извините, я верю что читатель достаточно умён чтобы взять мои исходники и переписать под свой язык. Нет, правда. Я изначально учу не код писать, а общим концепциям, алгоритмам и принципам.

Посему, на данный момент, мой инструментарий:

1. MSVS Express 2013. Потому что привык. Планирую перелезть на GCC.
2. Cheat Engine. Потому что удобно инжектить DLL-ки и в будущем понадобится для прочих исследований относительно взлома.
3. Olly Debugger. Использую в качестве основного отладчика, так же в силу привычки. Могу написать серию уроков по нему, но не вижу большого смысла - поменяются полторы кнопки на клавиатуре, да и уроков\статей по этому отладчику в сети есть куча.

Ещё один момент - игры на заказ мне ломать не очень хочется. Отчасти потому, что игра (чаще всего) стоит денег, а в основном потому, что это "повторение пройденного", но в другой обёртке. Прошу не обижаться, но это всё ещё не блог "кенг! сломай мне игру N!". Исключение - если попадётся какая-то нестандартная опция (это не повод с сего момента присылать мне по 9000 писем с предложениями).

Но всё ещё - feel free to присылать мне письма, любые. Общие рекомендации я дать могу, что-то подсказать или объяснить.

Вектор развития блога на данный момент:
Мы занялись D3D и графикой в целом. Мне не нравится текущий подход, используемый большинством - а именно конкретные адреса или указатели, поиски по сигнатурам и всё в том же духе. Я решил покопать поглубже и стремлюсь к автоматизации - как вы помните, нашему хуку нужны только смещения функций в табличке указателей интерфейса. Этот подход работает на DX 9, которая до сих пор используется крайне широко. В дальнейшем мы проапгрейдимся до поддержки DX 10 и 11, а затем и до OpenGL - это не так сложно. Так как наше приложение всё ещё очень хочет быть движком для трейнеров, добавим поддержку записи в память, codecave-ов, поиска сигнатур. Может, какой-то конструктор напишем, чтобы из файла брать адреса и названия функций и сразу пихать их в собранный exe. А, да, объединим загрузчик и хук в один исполняемый файл, чтобы не таскать их оба с собой.

Пока что - вот так.