Как стать автором
Обновить

Модификация игр на примере Arcanoid

Время на прочтение4 мин
Количество просмотров3.6K
Доброго времени суток!

Введение


На протяжении некоторого времени я наблюдал за блогом Assembler на хабре в виду того, что там начали появляться более чем отличные статьи по анализу различных keygen'ов и «reverse engineering». Я давно хотел заняться чем-то подобным и модифицировать какую-нибудь игру на J2ME. Я долго бродил по интернету в поисках хорошей, но в тоже время лёгкой для понимания (в плане анализа) игры. Однажды, я копался на сайте моего друга программиста (кстати, он тоже пишет программы для J2ME. Кто использовал ProPaintMobile — тот знает, о ком я говорю. И я нашёл её — это был простенький Арканоид. Видимо, это было чьё-то домашнее задание, или же он писался просто «just for fun», но тем не менее эта игра оказалась именно тем, чем нужно.


Что нам потребуется


Для того, чтобы произвести модификацию нам необходимы следующие компоненты:
  1. Декомпилятор, который выдал бы нам исходный код, чтобы было легче ориентироваться в процессе изменения байт-кода. Лично я использую для этих целей Java Decompiler, который вы можете стянуть здесь;
  2. Дизассемблер байт-кода виртуальной машины, который нам этот самый байт-код будет выдавать — JavaByte, который можно скачать здесь;
  3. Сам подопытный — Arcanoid;
  4. Ну и ещё неплохо было бы ознакомиться со спецификацией, в которой изложены инструкции виртуальной машины, которую вы можете почитать здесь.

Пожалуй всё. На самом деле, выбор софта дело индивидуальное, и, я бы сказал интимное. Поэтому вы можете с лёгкостью использовать, например, Jasmin вместо Java Decompiler.

Что нам теперь делать


Шаг первый

Сперва нам следует открыть вражескую машину эту самую игру и посмотреть, что там можно модифицировать. Так как исходного jar файла у нас нет, но есть преверифицированные классы и MANIFEST.MF, то давайте запакуем нашу игру. Сказано — сделано.


Структура нашего архива должна выглядеть именно так, как указанно выше. Осталось только переделать расширение — с zip на jar и…

Шаг второй

И запустить нашу игру. Давайте чуть-чуть поиграемся.


Продолжаем играть…


Упс. Ладно, ещё две жизни осталось. Играем дальше.


Всё, доигрался. Надо с этим что-то поделать.

Шаг третий

Делаем с этим что-нибудь, а именно декомпилируем. Нет проблем. Роемся в исходниках и понимаем, что самое интересное хранится в файле GCanvas.java. Давайте посмотрим внимательнее, что тут к чему:



Здесь мы с вами видим конструктор, переменные и методы.

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


int m_m_LivesII;


Комментарии излишни. Это и есть счётчик наших жизней. Осталось лишь найти где эта переменная уменьшается. А вот где, как указывает декомпилятор, это строка 170:


this.m_m_LivesII -= 1;


Давайте изучим данный участок кода:


while (this.m_m_goGameZZ)
    {
      if (!this.m_m_playGameZZ)
      {
        if (this.m_m_boomZZ)
        {
          this.m_m_ballcBallcBall.setVisible(false);
           _AnimateBoomcGraphicsV(g);
             this.platform.setVisible(false);
               this.m_m_LivesII -= 1;


Тут, можно сказать, нам даётся полная инструкция: если переменная m_m_goGameZZ, которая является булевой находится в значении true, то игра идёт. При столкновении же, тоже булевая переменная под именем m_m_playGameZZ становится в значение false, то у анимируется взрыв платформы (_AnimateBoomGraphics), далее платформа становится невидимой (platform.setVisible(false)) и уже затем у нас отнимается жизнь, что вы можете увидеть в коде, который находится выше. Конечно же, первое, что приходит на ум — это вместо того, чтобы из переменной m_m_LivesII вычитать единицу, надо видоизменить код, чтобы её прибавить. Но это не по феншую как-то. Тогда уж просто приравняем переменную к единице. И перекомпилируем? Это нужно перекомпилировать, преверифицировать классы, а затем заново собрать всё в архив. Нет уж, мне лень. Но есть способ просто поправить байт-код класса и тогда нам не нужно будет выполнять первых два шага. Движемся дальше…

Какой там у нас шаг? Ах да, четвёртый

Для того, чтобы править байт-код у нас есть JavaBite. Что же воспользуемся им.
Откроем наш класс (Classes -> Add Java Class и увидим вот такую картину:



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



Давайте пробежим глазами инструкции с самого начала. Стоп! Ничего не напоминает?



Да, это именно то, что нам нужно. Через инструкцию вниз вы заметите инструкцию isub — это оно!
Вообще, выражаясь русским языком эта инструкция означает, что мы что-то вычитаем из переменной m_m_LivesII. Нам же нужно, чтобы данная переменная всегда была равна единице. Этого очень легко добиться. Давайте изменим инструкцию isub на nop. Щёлкаем правой кнопкой мыши и выбираем Edit Instructions. Тут даже скринов не надо. В итоге инструкции будут выглядеть так:



Закинем наш уже неоригинальный класс с заменой старого и попытаемся открыть нашу игру, но вместо того, чтобы в неё поиграть мы получим вот такое сообщение об ошибке:



Разбор полётов

Из данного сообщения мы можем с вами понять, что в классе содержится ошибка. Да, кэп!
Но какая? Всё дело в том, что nop в данном случае вызовет дисбаланс желудка стека и, чтобы нам решить проблему нам также нужно заnopить и инструкцию iconst_1. Почему? Это я вам оставлю в качестве домашнего задания. Эта инструкция находится на расстоянии вытянутой руки сразу же после нашего nop. Кстати, вот и она:



Собственно, делаем то, что должны были сделать с ней ещё вчера. Вуаля!



Теперь пересоберём нашу игру. И наконец в неё поиграем.




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



Нам не страшен серый волк. Теперь можно смело играть в игру до потери пульса (хотя она наскучит вам после минуты, т.к. там всего два уровня) и не бояться смерти!

Заключение


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

До скорых встреч!

P.S. При проведении данного опыта ни один арканоид не пострадал!
Теги:
Хабы:
Всего голосов 6: ↑4 и ↓2+2
Комментарии13

Публикации

Истории

Работа

Java разработчик
350 вакансий

Ближайшие события