
Основы статического анализа Android-приложений
Введение
Современные мобильные приложения хранят и обрабатывают персональные данные, используются для финансовых операций и предоставляют доступ к критическим сервисам. Поэтому они становятся всё более частой целью атак. Одновременно растёт и число приложений, собранных с использованием сторонних библиотек, безопасность которых разработчик напрямую не контролирует. Проверить, что действительно попало в релизную сборку, без анализа готового приложения не получится.
В статье разберем, зачем нужен статический анализ Android-приложений, из чего состоит сборка приложения, как его декомпилировать, что искать в конфигурации и ресурсах, как читать Smali-код и когда потребуется отдельно анализировать нативные библиотеки.
Что такое статический анализ Android-приложений
Статический анализ Android-приложений включает в себя распаковку, декомпиляцию и изучение того, как приложение устроено изнутри.
Цели статического анализа
Статический анализ направлен на достижение нескольких целей.
Поиск hardcode секретов. Разработчики иногда оставляют в коде или ресурсах приложения API-ключи, токены, пароли и адреса внутренних сервисов. Если такой секрет попадает в релизную сборку, его может извлечь любой, кому она доступна.
Оценка безопасности конфигурации. Проверяется, насколько безопасно настроено само приложение: не открывает ли оно лишний доступ извне, не отключены ли базовые механизмы защиты.
Поиск используемых криптографических примитивов. Анализируется, какие алгоритмы и режимы шифрования применяет приложение, нет ли среди них устаревших или ненадежных алгоритмов.
Поиск вредоносного или отладочного кода. Статический анализ позволяет проверить приложение на скрытую функциональность. Это актуально как для анализа вредоносных приложений, так и для проверки легитимных SDK и библиотек, добавленных в проект.
Оценка встроенных механизмов защиты. Проверяется, использует ли приложение обфускацию кода, защиту от модификации, обнаружение механизмов отладки или динамической инструментации, а также другие защиты.
Поиск небезопасного использования системных API. Некоторые стандартные возможности Android легко применить небезопасно. Например, к ним относятся WebView с включённым JavaScript, слабый генератор случайных чисел или незащищённые механизмы межпроцессного взаимодействия.
Структура APK-файла
Любой анализ начинается с понимания структуры анализируемого объекта, исключением не являются и Android-приложения. Распространение их происходит через файлы формата APK. По сути это ZIP-архив: если сменить расширение на .zip, содержимое можно распаковать и посмотреть напрямую.
Внутри APK можно выделить несколько основных элементов:
- AndroidManifest.xml — главный конфигурационный файл, который содержит компоненты приложения, список требуемых разрешений, настройки безопасности;
- resources.arsc — скомпилированные ресурсы приложения;
- META-INF/ — содержит цифровой сертификат приложения и контрольные суммы файлов пакета;
- classes.dex — байт-код приложения, то есть код, скомпилированный на Java или Kotlin;
- lib/ — нативные библиотеки, находящиеся в поддиректориях для конкретной архитектуры процессора:
armeabi,armeabi-v7a,x86; - assets/ — произвольные файлы, необходимые приложению; иногда сюда добавляют дополнительные нативные библиотеки или dex-файлы, чтобы скрыть часть кода;
- res/ — различные ресурсы, которые приложение использует в своей работе, например изображения, декларативное описание интерфейса и другие данные. В отличие от resources.arsc, res хранит ресурсы в исходном формате.
Декомпиляция APK-файла
Перед анализом приложения необходимо его декомпилировать. Для этого чаще всего используют утилиты apktool и jadx.
apktool разбирает APK на составные части и возвращает Smali-код (человекочитаемое представление байт-кода), ресурсы и AndroidManifest.xml в редактируемом виде. Это подходящий вариант для тех случаев, когда приложение нужно не просто посмотреть, а изменить и собрать его заново. Кроме того, вид, представляемый с помощью apktool, удобен для автоматизированного анализа и парсинга.
apktool d app.apk -o app_decompiled
jadx, в свою очередь, превращает classes.dex в код на Java, максимально приближенный к тому, что писал разработчик. Читать такой код в разы удобнее, чем Smali. При этом jadx не предназначен для обратной сборки и модификации приложения, он создан для чтения и понимания логики.
jadx app.apk -d app_jadx
Результат работы apktool представляет собой директорию с исходными файлами приложения, содержащую Smali-код, ресурсы, AndroidManifest.xml (рис. 1).

Рисунок 1. Результат декомпиляции apktool
Отдельным плюсом jadx является графический интерфейс jadx-gui (рис. 2). В нём можно открыть APK, переходить между классами, искать использование метода или строки и заглядывать в манифест и ресурсы, не открывая терминал. На рисунке отлично видна структура APK-файла, описанная выше.

Рисунок 2. Декомпилированный код в jadx-gui
На практике их инструменты используют не вместо друг друга, а вместе. Сначала быстрый обзор в jadx, затем точечная работа с apktool.
Анализ приложения
После декомпиляции можно переходить к самому анализу — искать проблемы в конфигурации, ресурсах и коде приложения.
Анализ файлов конфигурации и ресурсов приложения
В первую очередь стоит обратить внимание на файлы AndroidManifest.xml и strings.xml. Их можно посмотреть даже без глубокой декомпиляции, так как они лежат в открытом виде и часто сразу выдают проблемы.
В AndroidManifest.xml стоит проверить:
- debuggable="true". Если этот флаг остался включён в релизной сборке, к приложению можно подключиться отладчиком и на лету менять его поведение. Это сильно упрощает и анализ, и взлом приложения;
- android:allowBackup. Если атрибут явно не выставлен в
false, данные приложения (базы данных, файлы, настройки) можно скопировать черезadb backupпри подключении устройства с включённой отладкой по USB — даже без root-доступа; - networkSecurityConfig. Ссылается на отдельный XML-файл, где можно разрешить приложению передавать трафик по HTTP вместо HTTPS для конкретных доменов или ослабить проверку сертификатов сервера. Стоит проверить, не сделано ли такое исключение без необходимости;
- exported-компоненты. Activity, Service, Broadcast receiver и Content provider с атрибутом
exported="true"доступны любому другому приложению на устройстве. Их можно вызвать напрямую, в обход экрана авторизации или других проверок внутри самого приложения; - minSdkVersion и targetSdkVersion. Минимальная и целевая версии Android, которые поддерживает приложение. Чем ниже minSdkVersion, тем больше вероятность, что приложение всё ещё работает на старых версиях системы с давно известными и незакрытыми уязвимостями.
В примере на рисунке 3 у приложения включены флаги android:debuggable="true" и android:allowBackup="true". Сами по себе они не являются уязвимостью, так как далеко не факт, что ими можно воспользоваться. Но они снижают порог входа для атакующего, поэтому их стоит зафиксировать и проверить, оправдано ли такое решение.

Рисунок 3. Флаги android:debuggable и android:allowBackup в AndroidManifest.xml
Отдельно стоит изучить res/values/strings.xml (рис. 4), разработчики нередко оставляют там API-ключи, адреса тестовых серверов, URL-схемы и просто заметки для себя. Всё это может пригодиться атакующему так же, как и секреты в коде.

Рисунок 4. Файл strings.xml
В этом примере файл, наоборот, ничем не примечателен. Он содержит стандартные строки и название приложения, без ключей, адресов серверов или прочих данных, которые могли бы быть интересны потенциальному злоумышленнику.
Анализ Smali-кода
Основным представлением кода Android-приложения, с которым удобнее всего работать при парсинге и модификации является Smali.
Smali представляет собой человекочитаемый формат байт-кода виртуальной машины, под которую компилируется код на Java или Kotlin. Получить Smali-код можно с помощью apktool, который разбирает файлы формата .dex и раскладывает каждый класс приложения в отдельный smali-файл, повторяя структуру пакетов.
Внутри такого файла нет привычных циклов и условий в обычном виде — вместо этого используются инструкции виртуальной машины: invoke-virtual и invoke-static вызывают методы, move-result забирает значение, которое метод вернул, if-eqz и другие условные переходы реализуют ветвление, а регистры вида v0, p1 играют роль переменных. Синтаксис непривычный, но у него есть плюс: каждая инструкция соответствует ровно одной операции байт-кода, поэтому по Smali всегда можно точно понять, что именно выполнит приложение, в отличие от декомпилированного в Java кода, который иногда восстанавливается некорректно.
Читать Smali-файл целиком слишком трудозатратно, поэтому на практике сначала находят интересующий метод, класс или строку через jadx, где код читается гораздо быстрее, а затем открывают этот же участок в Smali, если нужно понять детали реализации или что-то изменить. Через правку Smali-кода и последующую пересборку apktool можно внести изменения в приложение — например, чтобы обойти проверку лицензии или отключить проверку на root.
В приложении на рисунке 5 есть несколько методов, проверяющих наличие в системе бинарных файлов su, magisk и busybox. Их присутствие обычно указывает на то, что у пользователя есть root-доступ на устройстве.

Рисунок 5. Проверка на root: метод checkForSuBinary в Java и Smali
Такие проверки могут не дать приложению запуститься на устройстве с root-правами и тем самым помешать динамическому анализу. Но так как они найдены статически, их можно обойти, например, путем модификации Smali-кода и пересборки приложения.
Когда одного анализа Smali недостаточно: нативные библиотеки
Часть логики, особенно защиту от анализа или тяжёлые вычисления, разработчики выносят в библиотеки на C/C++, файлы .so в папке lib/. Ни Smali, ни jadx их не декомпилируют, поскольку для анализа бинарного кода нужны отдельные инструменты, например, дизассемблеры Ghidra или IDA. Подробный анализ такого кода оставим для отдельной статьи, но учитывать его важно, иначе легко пропустить часть функционала приложения.
Заключение
Статический анализ лежит в основе оценки безопасности мобильного приложения. Прежде чем переходить к динамическому тестированию, фаззингу или поиску способов обойти защиту в во время работы приложения, нужно понимать, из чего приложение состоит и как устроена его логика изнутри. Без этого дальнейшая работа строится вслепую.
Анализ конфигурации, ресурсов и кода позволяет находить нежелательные данные, выявлять небезопасные настройки и разбираться в механизмах защиты приложения. Понимание Smali или Java-кода помогает увидеть его внутреннюю логику и не упустить важные детали при дальнейшей оценке безопасности.
Важно понимать, что описанный в статье подход отражает ручную работу эксперта и подходит для глубокого разбора конкретного приложения. Если же требуется систематически проверять большое количество приложений, часть рассмотренных проверок можно переложить на инструменты статического анализа, которые автоматически находят типовые проблемы конфигурации, секреты в коде и небезопасные вызовы API — освобождая эксперта для более сложных и нетривиальных случаев.
Автор:
Ржевский Владислав, инженер по безопасности мобильных приложений Свордфиш Секьюрити.