Вступление Холодным зимним вечером, начитавшись статей об исследовании различного ПО и просмотрев разного рода видео о взломе игр и так далее, у меня вдруг тоже появилось желание повозиться под отладчиком с чем-нибудь интересным.
Я занимаюсь взломом сравнительно давно, поэтому практический опыт имею.
Сначала я, как и многие, просто искал в Интернете различные CrackME и взламывал их с целью обучения, затем перешел на взлом платных приложений (поиск/выбор ключей) и написание разного рода KeyGens. На данный момент я совершенствуюсь в этом и пытаюсь отточить свои хакерские навыки.
Ну да ладно, это лирическое отступление от сути.
Теперь определимся с некоторыми деталями.
В этой статье основное внимание будет уделено компьютерная игра «Сапер» .
Исследование и последующая отладка приложения происходит под Windows 7 x64 (реализация игры «Сапер» отличается в разных версиях ОС Windows).
В качестве дизассемблера будем использовать встроенный отладчик.
Мне он нравится своей простотой и элегантностью; некоторые вещи в нем делаются гораздо проще, чем, например, в OllyDBG. Ладно, с деталями разобрались, приступим к делу! Наверняка практически каждый, кто пользуется Windows, когда-либо имел дело с игрой Сапер.
В «семерке» игра выглядит так:
Это стандартное поле (в данном случае 16х16 ячеек).
Мы будем ломать игру на среднем уровне сложности, т.е.
на уровне «Любитель».
В целом данная статья будет актуальна и для уровней сложности «Новичок» и «Профессионал»; ничего, кроме времени, количества мин и размеров поля не изменится.
Этап 1
Итак, открываем наш CheatEngine (в дальнейшем будет использоваться аббревиатура CE) и присоединяем к игровому процессу:
Хорошо, мы присоединились.
Поиск нужных нам значений будет основан на поиске открытых в данный момент ячеек.
Поэтому ищем нужное нам значение текущего количества открытых ячеек.
В CE это делается довольно просто: 1) Введите в поле «Значение» исходное количество открытых ячеек, т.е.
ноль, нажмите «Первое сканирование»
2) Заходим в игру и кликаем на случайную ячейку, затем переходим в CE и в поле «Тип сканирования» выбираем «Увеличенное значение» (значение увеличилось), жмем «Следующее сканирование»
Подобные действия совершаем до тех пор, пока не найдём тот самый заветный смысл:
Мы нашли адрес, в котором хранится другой адрес, который, в свою очередь, хранит статическое значение.
Добавьте значение в «AddressList».
Теперь если вы немного поиграете в игру, то заметите, что значение изменилось.
Теперь ищем ассемблерную инструкцию, которая как-то взаимодействует с этим значением (меняет, читает):
Открываем игру и играем еще немного, затем видим такую картину:
Ага, очень интересно :) Особый интерес представляют инструкции 1 и 3, так как, во-первых, они пишут в память, а во-вторых, они похожи и в общем-то выполняются целых 3 раза! Итак заходим в отладчик, выбрав 1 или 3 инструкции и нажав на кнопку «Показать дизассемблер».
Ну-ну-ну, всё становится всё интереснее! Особый интерес представляет вот эта цепочка с итоговым сравнением (cmp):
Если к сравнению «cmp edx,eax» прикрепить точку останова, то при возврате в игру и попытке кликнуть по ячейке точка останова сработает. Причём как при нажатии на ячейку с миной, так и при нажатии на обычную клетку.
Что это значит? Это значит, что где-то здесь происходит «узнавание» того, что находится внизу в закрытой ячейке: пустота, мина или номер.
Пробуем изменить это значение на какое-нибудь бессмысленное сравнение, например на такое:
Здесь, как некоторые понимают, основная задача — активация флага процессора «Z», которая возникает, если оба операнда инструкции CMP эквивалентны.
Возвращаемся в игру и нажимаем на любую закрытую ячейку.
В конце концов:
Ха, круто! Оказывается, это была проверка «на победу в игре», которая происходит каждый раз при нажатии на ячейку поля, что в целом логично.
Это неплохо, но мы же хотим играть в обход всех мин, а не тупо выигрывать игру при открытии первой клетки поля, верно? Итак, продолжим наше исследование.
Этап 2 На втором этапе взлома мы попробуем выяснить, что является «переломным моментом» в распознавании «внутренности» ячейки, по которой был сделан щелчок.
Хорошо, давайте снова перейдем в CE и проделаем те же операции, что и на первом этапе, за исключением того, что нам не нужно отлаживать код. Видим уже знакомую последовательность инструкций:
Попробуем найти границы функции (блока инструкций), в которой мы в данный момент находимся (где находится инструкция):
Да, и что мы видим:
Нас визуально перенесли в начало текущей функции.
Ничего важного нам это пока не говорит, но если задуматься, рассмотреть все, что у нас есть, то можно прийти к выводу, что эта функция может быть как-то связана, например, с графикой игры.
К такому выводу можно прийти на основании этапа 1, где мы нашли проверку игры «на победу», которая, скорее всего, влияет на отрисовку поля.
Хорошо, давайте проверим эту теорию.
Спустимся немного вниз от начала функции, где вскоре найдём очень интересную инструкцию:
Это первое сравнение в этой функции.
Хм, попробуем поставить точку останова на инструкции, а потом зайдём в игру:
Мы не можем зайти в игру.
Почему? Да, потому что наша теория подтвердилась! Эта функция действительно связана с графикой игры.
При каждой активации окна и другом взаимодействии с интерфейсом игры вызывается эта функция, и в ней устанавливается BreakPoint для сравнения => мы не сможем активировать окно и «Сапера», пока не удалим точку останова.
Давайте снимем это.
Есть вероятность, что это сравнение сыграет ключевую роль в последующем поведении всей функции.
Попробуем изменить это сравнение на бессмысленное сравнение, чтобы активировался флаг процессора «Z», как это было сделано на первом этапе:
Размер новой инструкции (2 байта) в 2 раза меньше первоначального (4 байта), поэтому были добавлены инструкции «nop» по 1 байту, чтобы оставшееся пространство в 2 байта было «задрано».
Заходим в игру и пробуем поиграть.
Тыкаем в ячейки, натыкаемся на мину, и.
ничего не произошло! Хм, неудивительно.
Хорошо, попробуем не включать, а выключать флаг процессора «Z».
Для этого нужно заменить сравнение на такое, чтобы два сравниваемых элемента никогда не были равными.
Для этого восстанавливаем оригинальную инструкцию:
cmp dword ptr [rax+38],01
и измените сравнение содержимого указателя, например, на сравнение с отрицательным числом (от -1):
Теперь возвращаемся в игру и начинаем добывать.
При нажатии на некоторые области они прорисовываются с некоторой задержкой, либо их «внутри» прорисовывается только со второго раза.
Это действительно так, потому что мы нагло полезли в графическую функцию и нещадно ее отлаживали :).
А вот при нажатии на «опасные зоны» с минами вообще ничего не происходит!
Результат:
Профит :) Мы взломали игру Сапер из стандартного набора игр от Microsoft.
В следующей статье я расскажу о том, как можно использовать переполнение буфера для взлома игры, и о других не менее интересных функциях.
Теги: #Ассемблер #Ассемблер #обратное проектирование #игры #Ассемблер #отладка
-
Intel ❄ 2017. Итоги Года
19 Oct, 24 -
Чтение Прошивки Stm32
19 Oct, 24 -
По Следам Msi Master Overclocking Arena 2012
19 Oct, 24 -
Запуск Кода По Расписанию В Spring Framework
19 Oct, 24 -
Работа С Файлами В Dyalog Apl
19 Oct, 24 -
Стоит Ли Ит-Работникам Уходить В Декрет?
19 Oct, 24