Мы уже не раз рассказывали о взломе приложений для Android. И уже не раз вскрывали приложения практически голыми руками, имея только декомпилятор и дизассемблер, один раз прибегли к помощи фреймворка Frida, но есть и еще один, одновременно очевидный и неочевидный способ взлома Андроид-приложений — использовать отладчик.
РЕКОМЕНДУЕМ:
Использование Android в связке с Linux
Взлом приложений для Андроид с помощью отладчика
Строго говоря, это не то чтобы отдельный способ взлома, а скорее, способ разобраться в поведении приложения, чтобы найти его слабые места. Смысл здесь следующий: представьте, что у вас на руках семпл вредоноса. Его код хорошо обфусцирован, декомпилятор едва переваривает половину кода, и разобраться в его работе почти невозможно. Вам нужен способ проследить ее воркфлоу, разобраться в том, какая цепочка классов вызывается при возникновении определенных событий.
Во все времена лучший способ сделать это состоял в использовании отладчика. Но есть одна проблема: у вас нет исходников, а без них отладчик мало полезен в вашем деле. Зато у вас есть возможность декомпилировать приложение в Java (нередко только частично) или в достаточно высокоуровневый (в сравнении с машинным кодом) код smali, который всегда будет полностью корректным.
Так что в целом алгоритм ваших действий будет выглядеть так:
- Достаем подопытное приложение из устройства.
- Дизассемблируем его, выставляем флаг отладки.
- Собираем обратно и устанавливаем на устройство.
- Импортируем декомпилированный или дизассемблированный код в Android Studio.
- Запускаем отладку, будто это наше приложение.
Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами этой статьи.
Флаг отладки
Андроид устроен таким образом, что не позволит подключиться с помощью отладчика к приложению, которое этого не хочет. А факт «хотения» определяется флагом отладки, который представляет собой простую строку в файле AndroidManifest.xml приложения.
Поэтому первое, что мы должны сделать, — это разобрать приложение, выставить флаг отладки в значение true и собрать обратно. Проще всего это сделать с помощью утилиты apktool. Просто натравливаем ее на подопытное приложение, и готово:
1 |
$ java -jar apktool.jar d app.apk |
В текущем каталоге появится подкаталог app (ну или как назывался пакет с приложением).
Далее переходим в него и видим несколько файлов и каталогов. Нам они еще пригодятся, а пока открываем файл AndroidManifest.xml в текстовом редакторе и находим строку, начинающуюся с <application. Это тег application, который описывает приложение в целом. Именно к нему мы должны добавить атрибут android:debuggable="true". Просто вставь его сразу после application:
1 |
<application android:debuggable="true" ... |
Теперь приложение необходимо запаковать и подписать:
1 2 |
$ java -jar apktool.jar b app $ java -jar sign.jar app.apk |
Утилиту sign можно найти на GitHub.
После этого приложение можно установить на устройство.
Декомпиляция и дизассемблирование
Дизассемблерный листинг приложения у нас уже есть, мы получили его, разобрав приложение с помощью apktool. Мы можем импортировать его в Android Studio и начать отладку. Но лучше все-таки попытаться получить исходники Java, гораздо более легкие в чтении.
Для этого приложение необходимо декомпилировать. Сделать это можно с помощью нескольких различных инструментов, но я предпочитаю использовать декомпилятор Jadx. Он хорошо переваривает код, имеет средства деобфускации и активно развивается.
Скачиваем Jadx, запускаем, выбираем apk-файл приложения. Откроется главное окно приложения. Слева будут пакеты, справа исходники. Лучше сразу проверить их корректность (действительно ли получился читаемый Java-код), а затем можно экспортировать их с помощью меню File → Save as gradle project.
Android Studio и отладка
Теперь экспортированные исходники необходимо импортировать в Android Studio (да, у вас должна быть установлена и настроена среда разработки). Делается это с помощью меню File → Open → New Project → Import Project. Затем выбираем каталог с исходниками и в ответ на все вопросы нажимаем «Далее».
РЕКОМЕНДУЕМ:
Реверс Андроид приложений
Если все пройдет успешно, исходники будут импортированы и вы увидите их в главном окне Android Studio. Опять же лучше сразу пройтись по файлам и проверить их корректность. В 99,9% случаев вы увидите множество подчеркиваний и выделений красным: в понимании среды разработки исходники полны ошибок. Это абсолютно нормально, так как декомпилированный код так или иначе не должен быть пригоден для компиляции.
Теперь активируйте на смартфоне режим отладки по USB (Настройки → О телефоне → восемь тапов по номеру сборки), подключите его к компу. В Android Studio нажмите кнопку Attach debugger to Android process (она находится рядом с кнопками запуска и остановки приложения), чтобы подключиться к смартфону и приложению.
Если снизу появилась панель дебаггера — вы на коне. Следующий шаг — расставить брейк-пойнты в тех местах приложения, которые вы хотите исследовать. Сделаем это на примере простейшего crackme-one, который записывает строку в файл, а вам нужно узнать содержимое этой строки. Он хранит строку в открытом виде, но мы представим, что он расшифровывает ее только во время записи и поэтому мы должны поймать момент этой записи, чтобы вычленить уже расшифрованную, но еще не записанную строку.
Итак, с чего начать? Для начала выясним, с какой активности начинается исполнение приложения. В данном случае это опять же бессмысленно в силу простоты crackme, но в больших приложениях пригодится. Найти исходник нужной активности можно с помощью файла AndroidManifest.xml. Вот как выглядит описание главной активности в crackme:
1 2 3 4 5 6 |
<activity android:label="@string/app_name" android:name="com.reoky.crackme.challengeone.activities.ChallengeActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> |
Обратите внимание на строки, содержащие android.intent.action.MAIN и android.intent.category.LAUNCHER. Они означают, что именно с этой активности начнется исполнение приложения (на самом деле таких активностей может быть несколько, но это редкость). Также обратите внимание на следующую строку:
1 |
android:name="com.reoky.crackme.challengeone.activities.ChallengeActivity |
Можно сказать, это полный «путь» до активности, включая все имена покетов. Именно по такому пути вы найдете ее в исходниках, загруженных в Android Studio.
Открыв исходник этой активности в Android Studio, вы заметите его крайнюю простоту. По сути это окно с переключателем табов, а содержимое табов располагается внутри фрагментов в пакете/каталоге fragments. Переходим в него и видим ChallengeOneFragment. Он как раз и содержит поле ввода ответа и кнопку для записи файла. Внимательно прочитав исходник, замечаем, что в качестве колбэка для этой кнопки используется ChallengeOneFragmentOnClickListener, определенный в одноименном файле в каталоге listeners.
Открываем этот файл и среди прочих видим такие строки:
1 2 3 4 |
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(parent.getContext().openFileOutput("ANSWER", 1)); outputStreamWriter.write("poorly-protected-secret"); outputStreamWriter.flush(); outputStreamWriter.close(); |
Поздравляю, мы взломали crackme, искомая строка: «poorly-protected-secret». Но постойте, мы же договорились, что представим будто строка зашифрована и мы не можем ее увидеть. Вот здесь нам и нужен дебаггер: соль в том, чтобы поставить брейк-пойнт на вторую строку приведенного выше листинга и после этого запустить приложение. Когда исполнение приложения дойдет до этой точки, оно будет остановлено, а мы с помощью консоли дебаггера сможем прочитать аргумент вызова write (который уже должен быть расшифрован перед записью).
Итак, открываем исходник в Android Studio, находим нужную строку и кликаем рядом с номером этой строки. Строка подсвечивается красным, сигнализируя об установке брейк-пойнта. Наверняка вы не сразу сможете установить корректный брейк-пойнт (тот, который будет отмечен галочкой), потому что исходники, скорее всего, не будут соответствовать коду приложения. По этой же причине вам придется действовать вслепую, когда исполнение приложение дойдет до брейк-пойнта. При переходе к следующей строке дебаггер будет кидать вас в совершенно неверные куски кода, постоянно сообщая, что код некорректный.
РЕКОМНДУЕМ:
Инструменты Android-разработчика
Выхода из этой ситуации два: либо продвигаться на ощупь, либо вместо декомпилированного Java-кода загрузить в Android Studio дизассемблированный код smali, который просто технически не может быть некорректным.
Используем дизассемблированный код
Дизассемблированный код приложения у нас уже есть. Мы получили его, когда разбирали приложение с помощью apktool. Проблема только в том, что Android Studio в своем стандартном варианте хоть и умеет подсвечивать код smali, но не способна «работать» с этим кодом. Другими словами, мы сможем прочитать код smali, но не сможем установить брейк-пойнты. Чтобы это исправить, нужен сторонний плагин smalidea, а общая последовательность действий будет выглядеть так:
- Скачиваем плагин smalidea.
- Импортируем плагин в Android Studio: Settings → Plugins → Install plugin from disk.
- Импортируем каталог с разобранным приложением точно так же, как мы это делали в случае декомпилированного кода.
- В боковой панели кликаем на Android и выбираем Project Files, чтобы увидеть все файлы и каталоги проекта.
- Кликаем правой кнопкой на каталоге smali и выбираем Mark directory as → Sources root.
Как и в случае с Java-кодом, нужный нам код находится в listeners/ChallengeOneFragmentOnClickListener. В этот раз код намного длиннее и запутаннее, но, используя ранее полученный Java-исходник, вы легко найдете нужное место, а именно 246-ю строку:
1 |
invoke-virtual {v4, v8}, Ljava/io/OutputStreamWriter;->write(Ljava/lang/String;)V |
Ставьте на нее брейк-пойнт, запускайте приложение и, когда оно остановится, посмотрите на окно дебаггера. В нем будет состояние текущего и других объектов. Нажимая F7, вы заставите дебаггер выполнить следующую строку с переходом внутрь вызываемой функции (в нашем случае outputStreamWriter.write("poorly-protected-secret")). Оказавшись внутри нее, вы сможете просмотреть содержимое ее аргумента, а именно искомую строку. В данном случае ее видно и до перехода в функцию, но, если бы она была зашифрована, этот метод помог бы выяснить реальное значение.
Вместо выводов
Отладчик — очень удобный и весьма полезный инструмент реверсера. Он позволяет установить последовательность исполнения инструкций приложения и обнаружить скрытые данные. С помощью отладчика можно изучать тонкости работы Android и приложений для него.
Кстати, на том же спайсофте выложена отличная подборка инструментов для взлома и реверсинга приложений.