Статья Мобильный моддинг: Minigore 2

Наверняка многие, кто в детстве любил вместе с одноклассниками играть в игры на смартфонах, знают, что практически на любую оффлайн-игру существует модифицированная версия. Обычно это мод на бесплатные покупки, много игровой валюты, открытые премиальные функции, бесконечная жизнь персонажа, иногда убийство рекламы. В каждой игре - по разному, в зависимости от того, что та предлагает. Самый широкий оплот-пристанище для таких модов - 4pda. Форум этот древнее автора данной статьи, но, безусловно, наиполезнейший для тех, кто имеет дело со внутренней кухней смартфонов в той или иной сфере. И мобильный геймхакинг - одна из них: существует даже отдельный "Клуб Читеров" - группа людей, которые по запросам модифицируют игры. Вроде как даже до сих пор туда шлют запросы, но вот сколько активных членов их круга осталось - загадка.

С детства я всегда мечтал, если не попасть в их круг, то хотя бы тоже научиться взламывать игры. И если первое - не совсем от меня зависит, то вот второе - именно то, чем мы и будем заниматься в сегодняшней мини-статье. В качестве подопытного мы возьмём одну из моих любимых игр детства - . Классный зомби-шутер, почему-то давно пропавший с просторов Google Play. Игра старая, последнее обновление выходило в районе 2019 года. На 4PDA присутствует уже модифицированная версия 1.25, а так же последняя 1.28, нетронутая. Её мы, ввиду актуальности, и вскроем!

Для начала нам необходимо вообще понять, что эта игра предлагает, и что мы можем с ней сделать.

1741469436184.webp


Суть игры проста: мы должны отсреливать волны зомби, чертей, крокодилов, дровосеков, кроликов и прочей нечести, дабы пройти уровень. Из важного - у нас есть полоска HP в виде трёх сердец, у каждого подбираемого оружия есть полоска с количеством оставшихся патронов, а с убитых нами падают монеты, за которые мы можем открывать и улучшать оружие/персонажей. Кроме того, в игре есть таблица с квестами - за квесты дают опыт, за опыт мы повышаем уровень, а с новыми уровнями появляется возможность открывать всё новое.

1741469732198.webp
1741469742345.webp


Как человек, который довольно много играл в эту игру в своё время, скажу - вариаций оружия и персонажей для разблокировки много, и на высоких уровнях деньги и опыт копятся ну очень долго. Так что сегодня мы это дело исправим!

Для начала откроем наш APK в любом Java-декомпиляторе (я буду использовать JEB) и сразу глянем в манифест приложения:

1741470646351.webp


Название интересующего нас пакета - net.mountainsheep.minigore2zombies. Так же в метаданных была указана отдельная библиотека - minigore2, запоминаем. Если мы посмотрим на содержимое нашего пакета, то станет немножко неуютно:

1741471350040.webp


Изобилием классов этот пакет не блещет. Заглянем в главное активити - xtPlay:

1741471532965.webp


Кроме как загрузкой вышеупомянутой библиотеки, этот активити ничем не интересен, дальше идут какие-то махинации с рекламой. В остальных активити тоже нет ничего, что походило бы на логику игры. А значит, лезем в библиотеку!

1741471665307.webp


Так как игра очень старая, в ней всего две версии библиотеки: armeabi-v7a и x86. Так как с этой библиотекой дальше мы будем работать в дизассемблере, то стоит отдать предпочтение той версии, которую будет удобнее понимать, для многих - это, конечно, x86/x86-64. Я же, имея на борту IDA hex-rays для ARM, сразу начну с этой версии. Всё равно, если будет нужно, мы будем патчить все доступные версии.

1741473808100.webp


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

1741474541657.webp


Здесь каждая группа функций выделена на отдельную секцию магазина: уровни, оружие, персонажи - соответственно, StoreLevels, StoreWeapons и StoreCharacters. Начнём с последней - а именно с StoreCharacters::getCharacterPrice():

1741474971303.webp


По какой-то причине, каждая функция - просто вызов другой, уже настоящей. Причём в IDA отображаются обе, с одинаковым именем. Так что мы просто залезем во вторую:

1741475060104.webp


А здесь тело уже соответствует названию: наглядно видна кривая повышающейся цены, что отвечает за прокачку персонажа. Поэтому, в теории, если мы сделаем так, чтобы эта функция всегда возвращала 0, мы не должны тратить ни монеты на покупку и прокачку. Чтобы сделать конструкцию вида return 0, в ARM понадобится 2 инструкции - обнулить r0 и прыгнуть по адресу:

Код:
mov r0, #0    ; 00 00 A0 E3 (Little-Endian)
bx lr         ; 1E FF 2F E1 (https://shell-storm.org/online/Online-Assembler-and-Disassembler/)

В режиме Thumb это будет выглядеть так:

Код:
movs r0, #0      ; 00 20
bx lr            ; 70 47

Одноимённая функция, которая вызывает настоящую, тоже состоит из 2-х инструкций:

1741475851872.webp


Тем более, что ADRL занимает 8 байт, мы можем её заменить на нужные нам две четырёхбайтовые инструкции! Трогать вторую инструкцию смысла нет, после патча наша функция должна выглядеть так:

1741477735568.webp


Теперь попробуем собрать обратно нашу игру с новой библиотекой. Я для этих целей использую ApkTool-GUI, предварительно заменив оригинальную библиотеку на патченную:

1741478097155.webp


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

1741478668996.webp


Получилось! Монеты не тратятся, улучшения остаются, а мы - в шоколаде! Осталось то же самое провернуть и для остальных секций магазина, там всё точно так же, по аналогии. Теперь к уровням.

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

1741480163194.webp


А так же есть отдельные функции вида getNeededLevelFor<section>(). Что патчить - триллион полей в списке предметов или пару инструкций в нескольких функциях - думаю, для читателя очевидно.

1741480279589.webp


Алгоритм тот же самый: вместо первых нескольких инструкций просто возвращаем ноль, ничего нового. Казалось бы, раз мы такие молодцы, всё же заработает? А вот хрен там!

1741481321325.webp


Бесплатная прокачка == бесконечные деньги, поскольку их тратить больше не на что, тут всё прекрасно. Но вот с уровнями произошла неувязочка: видимо, предметы открываются только тогда, когда твой уровень не просто больше getNeededLevel(), а именно равен. А так как теперь нужный уровень - нулевой, то фактически я запорол все новые предметы, сделав их неразблокируемыми))

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

Хорошенько порывшись в функциях, я наткнулся на это чудо:

1741482507462.webp


Можно сделать так, чтобы эта функция возвращала 0. Если в игре есть ограничение на максимальный уровень - в теории, должно сработать. Но если максимального уровня нет... Лучше на этот счёт не думать.

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

1741484014302.webp


На этапе входа в магазин идёт отрисовка доступных для взаимодействия предметов. На этом этапе количество доступных проверяется функцией getVisible<section>Count(). Причём в разных секциях разный механизм проверки предмета на доступность, но все они, в конечном итоге, смотрят на наш уровень. Интересно, что в секциях с оружием и уровнями, требуемые уровни захардкожены в память:

1741484634619.webp
:

1741484557582.webp


А вот уровни для одноразовых абилити и персонажей прописаны в is<section>Visible(), которое под капотом вызывает get<section>Info():

1741485030251.webp


Объясняется сиё решение разработчика наличием двух функций add<section>Item(). Но нам всё равно: всё, что нам нужно сделать - это либо заединичить захардкоженные значения, либо занопить проверяющий переход из цикла с инкрементом. После этого заменяем библиотеки, собираем игру, устанавливаем и скрипим зубами в потугах выдержать весь груз своей офигенности:

1741486861063.webp
1741486870663.webp
1741486884918.webp


Всё получилось, мы воплотили задуманное! Можно с гордостью зваться начинающим мобильным крякером =]

По аналогии сделал всё то же самое с x86-библиотекой, удалил лишние файлы, оставил небольшое пасхалко - и мод готов! Конечно, сегодняшний пример - довольно старая игра без каких-либо особых средств защиты, держащая всю логику в одной библиотеке. Но мне всё равно было интересно - это вам не ModMenuMaker'ом пользоваться по трёхминутному гайду от школьника! Какие-никакие, но серьёзные знания тут всё-таки нужны. Попробую добиться открытия темы на 4pda и таки выложить всему миру мод этой богом забытой игры...

Надеюсь, эта небольшая статья поможет вам в нашем, несомненно, нелёгком деле.
Удачного крякинга!
made 4 @rev_with_da_boys
 

Вложения

  • 1741475568193.webp
    1741475568193.webp
    15,1 КБ · Просмотры: 16
  • Нравится
Реакции: Edmon Dantes
А так пособие классное, молодец👍🏻

Чё, был заинтересован в ModMenuMaker и не разобрался?
Ida высер, с кучу временем на дамп и лишней инфой
Недавно сделал дампер для всех игровых движков, с хорошей фильтрацией и выводов грязных и чистых имён и всё это под андроид
100 мб дампит за 4 секунды , так что быстро сделать анализ экспортов пока едешь в машине или летишь в самолёте имба, более ребята знают как юзать экспорты в хуках через dlSym

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

Обучение наступательной кибербезопасности в игровой форме. Начать игру!