В этой статье я хотел бы поделиться своим видением обфускации, а также рассказать про интересный метод сокрытия бизнес-логики в приложениях с NDK, который нашел относительно недавно. Так что если вас интересуют живые примеры обфусцированного кода в Android — прошу под кат.
Под обфускацией в рамках этой статьи будем понимать приведение исполняемого кода Android-приложения к трудному для анализа виду. Существует несколько причин затруднять анализ кода:
Многие разработчики решают проблему простым форком конфига ProGuard. Это не лучший способ защиты данных (если вы первый раз слышите об этом, то см. вики).
Хочу привести показательный пример, почему предполагаемая “защита” с помощью ProGuard не работает. Возьмем любой простенький пример из Google Samples.
Подключив к нему ProGuard со стандартным конфигом, получим декомпилированный код:
«Ооо, ничего непонятно» – скажем мы и успокоимся. Но через пару минут переключения между файлами найдём подобные кусочки кода:
В этом примере код приложения выглядит затрудненным довольно слабо (логирование данных, создание видео захвата), поэтому некоторые методы, использованные в оригинальном коде, легко понятны и после обработки конфигом ProGuard.
Дальше больше, взглянем на data-классы в Kotlin. Data-класс по умолчанию создает метод “toString”, который содержит в себе названия переменных экземпляра и название самого класса.
Исходный data-класс:
Он может превратиться в лакомый кусочек для реверсера:
(автогенерация метода toString в Kotlin)
Выясняется, что ProGuard прячет далеко не весь исходный код проекта.
Если я все еще не убедил вас в нецелесообразности защиты кода таким способом, то давайте попробуем оставить в нашем проекте атрибут “.source”.
-keepattributes SourceFile
Эта строчка есть во многих opensource проектах. Она позволяет просматривать StackTrace при падении приложения. Однако, вытащив “.source” из smali-кода, мы получим всю иерархию проекта с полными названиями классов.
По определению, обфускация – это “приведение исходного кода в нечитаемый вид для того, чтобы противодействовать разным видам ресерча”. Однако, ProGuard (при использовании со стандартным конфигом) не делает код нечитаемым – он работает как минификатор, сжимающий названия и выкидывающий лишние классы из проекта.
Такое использование ProGuard – это легкое, но не совсем подходящее для хорошей обфускации решение на ”авось”. Хорошему разработчику нужно заставить ресерчера (или злоумышленника) испугаться “китайских символов”, которые трудно деобфусцировать.
Если вам интересно подробнее узнать про ProGuard, то предлагаю следующую познавательную статью.
Теперь давайте посмотрим, что обычно прячут в приложениях.
В коде часто может быть спрятано что-то более неожиданное (наблюдения из личного опыта), например:
Теперь мы знаем, что может прятаться в Android-приложениях и можем переходить к главному, а именно к способам сокрытия этих данных.
В таком случае я просто покажу вам эту картинку 🙂
“Помогите Даше найти бизнес-логику”
Это нетрудозатратное и совершенно бесплатное решение подойдет для:
Это решение все-таки имеет право на жизнь, потому что, в первую очередь, оно является простым и бесплатным. Несмотря на вышеупомянутые минусы, у него есть весомый плюс: при правильной настройке ProGuard-правил приложение может действительно стать обфусцированным.
Однако, нужно понимать, что такое решение после каждой сборки требует от разработчика декомпиляции и проверки, все ли нормально. Потратив несколько минут на изучение APK файла, разработчик (и его компания) могут стать увереннее в безопасности своего продукта.
Как изучать APK-файл
Это решение подойдет для:
К сожалению, реально хороших бесплатных обфускаторов для мобильных приложений я не знаю. А обфускаторы, которые можно найти в сети могут принести вам много головной боли, поскольку собрать такой проект под новые версии API будет слишком сложно.
Исторически сложилось, что существующие крутые обфускаторы сделаны под машинный код (для C/C++). Хорошие примеры:
Например, Movfuscator заменяет все opcodes mov-ами, делает код линейным, убирая все ветвления. Однако, крайне не рекомендуется использовать такой способ обфускации в боевом проекте, потому что тогда код рискует стать очень медленным и тяжелым.
Это решение подойдет для приложений, у которых основная часть кода — NDK.
Это самый грамотный выбор для серьезных приложений, так как проприетарное ПО:
а) поддерживается;
б) всегда будет актуально.
Пример обфусцированного кода при использовании таких решений:
В этом фрагменте кода можно увидеть:
Это решение подойдет для:
Я решил выделить этот пункт, так как написание кроссплатформенных приложений сейчас стало действительно популярным занятием.
Кроме очень большого community, JS имеет очень большое количество открытых обфускаторов. Например, они могут превратить ваше приложение в смайлики:
Мне бы очень хотелось посоветовать вам данное решение, но тогда ваш проект будет работать самую малость быстрее черепахи.
Зато, уменьшив требование к обфускации кода, мы можем создать действительно хорошо защищенный проект. Так что гуглим “js obfuscator” и обфусцируем наш выходной bundle-файл.
Это решение подойдет для тех, кто готов писать кроссплатформенное приложение на React Native.
Xamarin
Мне самому часто приходилось использовать NDK в своем коде. И я знаю, что некоторые разработчики считают, что использование NDK спасает их приложение от реверсеров. Это не совсем так. Для начала нужно понять, как именно работает сокрытие с помощью NDK.
Оказывается, очень просто. В коде есть некоторая JNI-договоренность, что при вызове C/C++ кода в проекте он будет преобразовываться следующим образом.
Нативный класс NativeSummator:
Реализация нативного метода sum:
Реализация нативного статичного метода sum:
Cтановится понятно, что для вызова нативного метода используется поиск функции Java_<package name>_<Static?><class>_<method>
в динамической библиотеке.
Если заглянуть в Dalvik/ART код, то мы найдём следующие строки:
(источник)
Сначала сгенерируем из Java-объекта следующую строку Java_<package name>_<class>_<method>
, а затем попытаемся разрезолвить метод в динамической библиотеке с помощью вызова “dlsym”, который попытается найти нужную нам функцию в NDK.
Так работает JNI. Его основная проблема в том, что, декомпилировав динамическую библиотеку, мы увидим все методы, как на ладони:
Значит, нам нужно придумать такое решение, чтобы адрес функции был обфусцирован.
Сначала я пытался записать данные напрямую в нашу JNI-таблицу, но, понял, что механизмы ASLR и разные версии Android просто-напросто не позволят мне сделать этот способ работающим на всех устройствах. Тогда я решил узнать, какие методы NDK предоставляет разработчикам.
И, о чудо, нашелся метод “RegisterNatives”, который делает ровно то, что нам нужно (вызывает внутреннюю функцию dvmRegisterJNIMethod).
Определяем массив, описывающий наш нативный метод:
И регистрируем наш объявленный метод в функции JNI_OnLoad (метод вызывается после инициализации динамической библиотеки, тыц) :
Ура, мы самостоятельно спрятали функцию “hideFunc”. Теперь применим наш любимый llvm-обфускатор и порадуемся безопасности кода в конечном виде.
Это решение подойдет для приложений, которые уже используют NDK (подключение NDK несет в проект большое количество сложностей, поэтому для не-NDK приложений это решение не так актуально).
На самом деле, в приложении не должно храниться никаких чувствительных данных, либо они должны быть доступны только после аутентификации пользователя. Однако, бывает, что бизнес-логика принуждает разработчиков к хранению токенов, ключей и специфических элементов логики кода внутри приложения. Надеюсь, эта статья поможет вам, если вы не хотите делиться такими чувствительными данными и быть “открытой книгой” для ресерчеров.
Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…
Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…
Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…
С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…
Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…
Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…