Модификация исходного кода android-приложения с использованием apk-файла

в 13:09, , рубрики: android, reverse engineering, декомпиляция, информационная безопасность, Разработка под android

Так уж получилось, что приложение для чтения комиксов и манги, которое я использую на своем android-смартфоне, после обновления стало показывать рекламу в конце каждой главы комикса. Данное приложение пару лет назад было доступно на Google Play (платная версия которого и была мной куплена), но было удалено в силу «нарушения авторских прав», после чего ушло в подполье и стало распространятся через сайт разработчика. Увы, достойных альтернатив этому приложению на android и iOS я не нашел, но и смотреть рекламу особо не было желания, тем более я уже покупал версию без рекламы. Сам разработчик почему-то не сделал возможности отключить ее, а на просьбы добавить такую возможность не отозвался. Поэтому пришлось искать альтернативные методы ее отключения. Первое, что пришло в голову, это то, что android-приложения пишутся на java, а значит есть вероятность, что автор не обфусцировал свое приложение и его можно попытаться декомпилировать. Немного подумав, я приступил к работе.

Для начала был загружен сам apk-файл приложения. Затем недолгий поиск по интернету привел меня на сайт http://www.decompileandroid.com/. С его помощью можно было загрузить apk-файл с приложением и на выходе получить набор исходников. Увы, декомпиляция в java-классы происходит не совсем идеально и поэтому восстановить полностью сам проект приложения в IDE(Idea) у меня не получилось, но это позволило проанализировать саму структуру проекта и разобраться как он примерно работает. После проведения анализа, было найдено два перспективных метода в классе BaseReaderFragment.javaplaceAdViewIfNeeded и removeAdViewIfNeeded.

Код метода placeAdViewIfNeeded:

if (adViewPlaced || adView == null)
{
    return;
} else
{
    viewgroup.setVisibility(0);
    android.widget.RelativeLayout.LayoutParams layoutparams = new android.widget.RelativeLayout.LayoutParams(-2, -2);
    layoutparams.addRule(13, -1);
    viewgroup.addView(adView, layoutparams);
    adViewPlaced = true;
    AdsHelper.requestNewAd(adView);
    return;
}

Самое простое, что пришло на ум после чтения кода, это убрать все лишнее, и оставить лишь вызов return;

Но, как уже было сказано, даже если бы я изменил в java-классе что-либо, я бы не смог в итоге скомпилировать приложение в IDE. Поэтому пришлось искать альтернативу. Оказалось, что smali-файлы, которые создаются в процессе декомпиляции, позволяют также после внесения нужных изменений, вновь собрать модифицированное приложение. Увы, сайт, что был приведен выше, позволял лишь получать исходники, но не собирать новые. Поэтому пришлось искать способы сделать это самостоятельно.
Была найдена утилита ApkTools, которая позволяла декомпилировать и компилировать apk-файлы. Кроме того, потребовалась утилита aapt.exe, которая была взята мной из стандартного SDK под андроид в папке android-sdkbuild-tools20.0.0.

Для удобства вызова утилиты из под windows был создан скрипт apktool.bat:

@echo off
set PATH=%CD%;%PATH%;
java -jar "%~dp0apktool.jar" %1 %2 %3 %4 %5 %6 %7 %8 %9

Для декомпиляции приложения были выполнены команды:

apktool if <appName>.apk
apktool d <appName>.apk

После чего, в полученных исходниках был найден файл BaseReaderFragment.smali и нужные нам методы были изменены следующим образом:

.method protected placeAdViewIfNeeded(Landroid/view/ViewGroup;)V
    .locals 3
    .param p1, "layoutAds"    # Landroid/view/ViewGroup;

    .prologue
    const/4 v2, -0x2
	return-void
.end method

.method protected removeAdViewIfNeeded(Landroid/view/ViewGroup;)V
    .locals 1
    .param p1, "layoutAds"    # Landroid/view/ViewGroup;

    .prologue
    .line 149
    const/16 v0, 0x8
	return-void
    
.end method

Далее пришла очередь сборки apk-файла из исходников.

Сделать это можно cледующей командой:

apktool b <AppSourceDir>

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

Распаковываем архив, выполняем команду:

java -jar signapk.jar certificate.pem key.pk8 <path-of-the-folder-contaning-the-apk>.apk <path-of-the-new-signed-apk>.apk

Полученный apk-файл можно загружать на телефон, чтобы проверить наше модифицированное приложение. Однако в процессе тестирования изменений оказалось, что объявления больше не показываются, однако сама страница для их показа создается, что не очень приятно. Снова был проанализирован код приложения, найден класс BaseSeamlessReaderFragment, а в нем метод appendPages.

В нем было видно, что строчка:

addPage(new MangaPage(i - 1, null, chapteritem, true), false);

создает дополнительную страницу, помимо тех, что есть в главе манги, с параметром, отвечающим за показ объявлений. Было решено удалить эту строчку и посмотреть результат. Снова заглядываем в аналогичный smali-файл(BaseSeamlessReaderFragment$4) и удаляем строчку:

invoke-virtual {v6, v7, v10}, Lorg/mangawatcher/android/fragments/BaseSeamlessReaderFragment;->addPage(Lorg/mangawatcher/android/fragments/BaseSeamlessReaderFragment$MangaPage;Z)V

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

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

Автор: Kelheor

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js