Защита мобильного приложения от реверс-инжиниринга
APK — это zip-архив. apktool d app.apk разбирает его за секунды, jadx превращает байткод в читаемый Java. Без обфускации злоумышленник видит имена классов, методов, логику проверки лицензии, URL API-эндпоинтов, захардкоженные строки. На iOS ситуация не сильно лучше: class-dump восстанавливает заголовки Objective-C классов, Ghidra дизассемблирует нативный код.
Обфускация кода
Android. R8 (встроен в Android Gradle Plugin) — минимальная точка входа. Включается через minifyEnabled = true в build.gradle и переименовывает классы, методы, поля в a, b, c. Но R8 не шифрует строки. Серьёзная строковая защита требует отдельного инструмента.
ProGuard правила нужно писать вручную для каждой используемой библиотеки — иначе сломается рефлексия, Gson-сериализация, Firebase. Типичная боль: @SerializedName на полях модели не помогает, если сам класс переименовался в a, и Gson не может создать его через рефлексию. Решение — @Keep или явные -keep правила для data-классов, передаваемых через API.
Коммерческие обфускаторы — DexGuard, iXGuard (от Guardsquare) — идут дальше: шифрование строк, обфускация control flow (вставка ложных ветвей и goto), защита ресурсов, anti-tamper проверки. DexGuard интегрируется в Gradle как плагин, конфиг похож на ProGuard, но возможностей на порядок больше. Используется в банковских приложениях.
iOS. Бинарник Swift/Objective-C обфусцировать сложнее — нет промежуточного байткода. Варианты: obfuscator-llvm (LLVM pass для control flow flattening) или коммерческий iXGuard. Имена Swift-функций в публичном API нельзя скрыть без последствий для ABI, но приватную логику обфусцировать можно.
Flutter. Dart компилируется в машинный код, что само по себе даёт некоторую защиту. reFlutter — инструмент для обратного инжиниринга Flutter-приложений — умеет восстанавливать имена из snapshot, если приложение скомпилировано без strip symbols. Включаем в сборке: flutter build apk --obfuscate --split-debug-info=./symbols. Debug symbols уходят в отдельный файл, который не включается в релиз.
Защита строк
Захардкоженные URL, ключи, имена серверов — первое, что ищет strings на бинарнике. Минимум — не хранить их в коде совсем (вытащить в удалённую конфигурацию, например Firebase Remote Config). Если строка всё же должна быть в приложении — шифровать и расшифровывать в runtime через нативный код (JNI/NDK на Android), чтобы plaintext не светился в dex.
Паттерн: строки хранятся как XOR-зашифрованный массив байт, ключ XOR разбит на части в разных нативных функциях. Не криптографически стойко, но поднимает порог входа.
Антиотладочные техники
ptrace(PTRACE_TRACEME, 0, 0, 0) на Android/Linux — процесс сам становится tracee, второй ptrace attach от отладчика вернёт ошибку. На iOS аналог — PT_DENY_ATTACH. Frida это обходит (не через ptrace, а через task_for_pid), но добавляет сложность.
Проверка через /proc/self/status на Android: TracerPid != 0 означает, что к процессу прицеплен отладчик. Фрейморк appdome автоматически добавляет такие проверки без изменения кода.
Anti-Frida: Frida внедряет frida-agent в процесс через frida-gadget. Детектируем через проверку загруженных библиотек (/proc/self/maps), наличие frida-server порта (27042), имён файлов в /proc/self/fd. Но детектирование в userspace Frida же и обходит — гонка вооружений.
Проверка целостности приложения
На Android верифицируем подпись APK в runtime: PackageManager.getPackageInfo() возвращает список signatures. Хэшируем их и сравниваем с захардкоженным значением. Если подпись изменилась — приложение перепаковано. Саму проверку нужно прятать в нативном коде, иначе её тривиально патчат в smali.
Google Play Integrity API (замена SafetyNet Attestation) — более надёжный вариант: сервер получает от Google подписанный токен с вердиктом MEETS_DEVICE_INTEGRITY, MEETS_STRONG_INTEGRITY. Нельзя подделать без Google-серверов. Недостаток — требует сетевого запроса и API-квоты.
На iOS — DCAppAttestService (App Attest). Аналогичный механизм: устройство получает аттестацию от Apple, сервер проверяет через Apple API.
Нативный код и React Native
Бизнес-логику, которую нельзя раскрывать, выносим в NDK (Android) или нативный фреймворк (iOS). Нативный код сложнее реверсировать, чем Kotlin/Java или JavaScript bundle.
React Native хранит весь JavaScript в index.android.bundle — обычный файл внутри APK, читается любым JS-форматтером. Защита: Hermes bytecode (компиляция через --hermes), шифрование bundle через нативный модуль, который расшифровывает его в памяти перед загрузкой в движок. Metro Bundler поддерживает кастомный serializer для этого.
Сроки
Базовая настройка R8/ProGuard со строковой защитой и anti-tamper проверками — 3–5 дней. Интеграция DexGuard/iXGuard с тонкой настройкой конфигурации — до 2 недель (много итераций, чтобы не сломать runtime). Нативный перенос критичной логики — отдельная оценка по объёму.







