Obfuscator — це інструмент, який перетворює вихідний код у важкочитану форму без зміни його функціональності. Obfuscator використовується для захисту інтелектуальної власності, ускладнення аналізу коду та запобігання реверс-інжинірингу. Згідно з Документацією Android Developers, обфускація через R8 та ProGuard є стандартним етапом продакшен-збірки Android-додатків.
Головне
Obfuscator (обфускатор) — програма, що виконує обфускацію: перетворення читаного коду в еквівалентний за функціональністю, але нечитаний для людини. Основні завдання обфускатора — перейменування ідентифікаторів (identifier mangling), видалення налагоджувальної інформації, заплутування потоку керування (control flow obfuscation) та шифрування рядкових літералів (string encryption).
Обфускація не є шифруванням. Зашифрований код не може виконуватися без розшифрування. Обфускований код виконується безпосередньо JVM, ART або JavaScript-рушієм, але людині зрозуміти його вкрай складно. Обфускація не дає абсолютного захисту — вмотивований фахівець завжди може відновити логіку через деобфускатор або налагодження в runtime.
Перший комерційний обфускатор ProGuard з'явився у 2002 році як інструмент для Java-аплетів. З ростом Android (2008) ProGuard став стандартом для мобільної розробки. У 2018 Google випустила R8 як заміну ProGuard для Android Gradle Plugin 3.4. R8 швидший за ProGuard у 2–3 рази та генерує більш компактний байт-код за рахунок глибокої оптимізації на рівні SSA (Static Single Assignment) — формату проміжного представлення, що дозволяє аналізувати потік даних.
У веб-розробці обфускація еволюціонувала від простих замін (YUI Compressor, 2007) до складних AST-трансформерів (Obfuscator.io, 2016). Сучасні JavaScript-обфускатори використовують control flow flattening, opaque predicates (умови, що завжди true або false, але неочевидні для аналізатора) та шифрування рядків із саморозшифровкою в runtime. Jscrambler (2012) інтегрує обфускацію із захистом від налагоджувача та DRM-механізмами.
Область застосування обфускації широка. У мобільній розробці обфускатори захищають код від крадіжки через APK-декомпілятори (jadx, APKTool, dex2jar). У веб-розробці обфускація JavaScript захищає алгоритми, API-ключі та бізнес-логіку клієнтської сторони. У бібліотеках та SDK обфускація запобігає використанню коду конкурентами.
| Техніка | До обфускації | Після обфускації |
|---|---|---|
| Перейменування класів | NetworkManager | a |
| Перейменування методів | sendRequest() | b() |
| Шифрування рядків | "API_KEY" | decrypt("x9fK2p") |
| Заплутування умов | if (a > b) | if (a > b ? true : false) |
Identifier mangling (перейменування ідентифікаторів) — найпоширеніший метод. Імена класів, методів, полів та змінних замінюються на короткі, неінформативні рядки: a, b, c, aa, ab. Це ускладнює розуміння призначення кожного елемента коду. ProGuard та R8 використовують ідентичні імена для різних типів (клас A, поле A, метод A), додатково заплутуючи аналіз.
Control flow obfuscation (заплутування потоку керування) змінює структуру коду так, що лінійна послідовність стає неочевидною. Додаються мертві гілки (dead branches), інвертуються умови (if (!a) замість if (a)), вставляються goto-подібні оператори (break/continue з мітками). Це робить аналіз через декомпілятор та налагоджувач вкрай трудомістким.
// Вихідний код
function authenticate(token) {
const url = "https://api.example.com/auth";
const headers = { Authorization: "Bearer " + token };
return fetch(url, { method: "POST", headers });
}// Після Obfuscator.io в режимі high
const _0x4f2e = ["https://api.example.com/auth",
"Authorization", "Bearer ", "POST"];
(function(_0x5a3b, _0x4f2e) {
const _0x1c2d = function(_0x3e4f) {
while (--_0x3e4f) {
_0x5a3b["push"](_0x5a3b["shift"]());
}
};
_0x1c2d(++_0x4f2e);
}(_0x4f2e, _0x1c2d));
function _0x1c2d(_0x5a3b, _0x4f2e) {
return _0x4f2e[_0x5a3b];
}
function _0x3e4f(_0x1c2d) {
const _0x5a3b = _0x1c2d(0, "https://api.example.com/auth");
const _0x4f2e = { Authorization: "Bearer " + _0x1c2d };
return fetch(_0x5a3b, { method: "POST", headers: _0x4f2e });
}Obfuscator.io додав масиви рядків, самовикличну функцію для перемішування масиву, перейменував усі ідентифікатори та замінив рядки на індекси масиву. Вихідний код із 5 рядків перетворився на 20+ нечитаних рядків, але функціональність authenticate(token) повністю збережена. Деобфускація можлива через AST-аналіз, але потребує часу.
ProGuard — класичний обфускатор для Java та Android, що використовується з 2002 року. ProGuard виконує три завдання: стиснення (shrinking — видалення невикористовуваних класів та методів), оптимізація (оптимізація байт-коду) та обфускація (перейменування ідентифікаторів). ProGuard вбудовується в Android Gradle Plugin через файл proguard-rules.pro з правилами винятку для бібліотек.
R8 — більш сучасний обфускатор, включений в Android Gradle Plugin починаючи з AGP 3.4. R8 виконує ті ж функції, що й ProGuard, але швидше (написаний на Kotlin з нуля) та ефективніше (краще оптимізує байт-код для ART Runtime). Налаштування R8 виконується через ті ж proguard-rules.pro файли, що й ProGuard. Для включення R8 достатньо встановити minifyEnabled true в build.gradle.
Mapping-файл — результат роботи R8/ProGuard, що містить відповідність між вихідними та обфускованими іменами класів, методів та полів. Mapping-файл критично важливий для аналізу crash reports: без нього стектрейс міститиме a.a.b замість com.example.app.MainActivity.onCreate. Firebase Crashlytics та Sentry автоматично завантажують mapping-файли та відновлюють вихідні імена у звітах.
Mapping-файли необхідно завантажувати в Firebase або Sentry при кожному випуску нової версії додатку. Якщо mapping-файл втрачено або не завантажено, всі crash reports після обфускації стануть нечитаними. Android Gradle Plugin автоматично зберігає mapping-файл в build/outputs/mapping/release/mapping.txt. Для Firebase використовується плагін Crashlytics Gradle Plugin, який завантажує mapping при збірці release-версії.
// app/build.gradle — обфускація через R8
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
"proguard-android-optimize.txt"),
"proguard-rules.pro"
}
}
}# proguard-rules.pro — правила збереження
# Зберегти модель даних для Gson
-keep class com.example.model.** { *; }
# Зберегти класи для Retrofit інтерфейсів
-keep,allowobfuscation interface com.example.api.*
# Не обфускувати публічні активності
-keep class * extends android.app.Activity {
public protected *;
}
# Видалити логи в продакшені
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(...);
public static int v(...);
public static int d(...);
}Правила -keep в proguard-rules.pro критично важливі — без них R8 видалить або перейменує класи та методи, що використовуються через рефлексію (Gson, Retrofit, Room). assumentSideEffects видаляє виклики Log.v та Log.d з продакшен-коду. Бібліотеки, такі як Gson, Retrofit та OkHttp, постачають готові правила в proguard.txt всередині AAR.
Obfuscator.io — найпопулярніший відкритий обфускатор JavaScript з підтримкою перейменування ідентифікаторів, шифрування рядків, керування потоком (control flow flattening) та захисту від налагодження (debug protection). Налаштування обфускації виконується через JSON-конфіг або CLI. Безкоштовна версія підтримує базові методи; Enterprise-версія додає поліморфний код та самозахист.
Jscrambler — комерційний обфускатор JavaScript з розширеним захистом: поліморфні трансформації (кожен запуск генерує новий обфускований код), захист від налагоджувачів (виявлення DevTools), захист від скріншотів (self-defending) та expire-механізми (код перестає працювати після певної дати). Jscrambler використовується в банківських додатках та DRM-системах.
// obfuscate.js — конфігурація Obfuscator.io
const JavaScriptObfuscator = require("javascript-obfuscator");
const fs = require("fs");
const code = fs.readFileSync("app.js", "utf8");
const result = JavaScriptObfuscator.obfuscate(code, {
compact: true,
controlFlowFlattening: true,
controlFlowFlatteningThreshold: 0.75,
numbersToExpressions: true,
simplify: false,
stringArray: true,
stringArrayThreshold: 0.8,
debugProtection: true,
disableConsoleOutput: true,
});
fs.writeFileSync("app.obfuscated.js", result.code);Параметри Obfuscator.io: controlFlowFlattening: 0.75 заплутує потік керування в 75% блоків; stringArray: true виносить рядки в масив; debugProtection запобігає відкриттю DevTools; disableConsoleOutput прибирає console.log. Чим вищі пороги, тим більший час обфускації та розмір коду, але тим складніший аналіз.
Обфускація не захищає від runtime-аналізу. Атакуючий може запустити додаток у налагоджувачі (Frida, Objection, Xposed) та перехоплювати методи в реальному часі. Обфускація захищає від статичного аналізу (декомпіляція APK, читання байт-коду), але не від динамічного. Для захисту від runtime-аналізу потрібні додаткові заходи: SSL Pinning, Root Detection, Integrity Verification.
Розмір додатку після обфускації може збільшитися на 20–50%. Control flow obfuscation додає мертві гілки та дублює умови — це збільшує кількість байт-коду. String encryption замінює короткі рядкові літерали на виклики decrypt(), що також збільшує розмір. Для мобільних додатків це критично, оскільки розмір APK безпосередньо впливає на конверсію в Google Play.
Продуктивність також страждає. Control flow obfuscation додає додаткові перевірки та розгалуження, збільшуючи час виконання методів на 5–15%. String encryption додає виклик decrypt при кожному зверненні до рядка. Для критичних за продуктивністю функцій (onDraw в Android, render в React) обфускацію слід вимикати через -keep правила.
Часті запитання
Шифрування робить код невиконуваним без розшифрування — для виконання потрібен дешифратор. Обфускація робить код нечитаним, але виконуваним безпосередньо. Шифрування дає сильніший захист, але потребує завантажувача-дешифратора, який сам може бути проаналізований.
Деобфускація можлива, але трудомістка. Інструменти типу jadx, JEB Decompiler та UnConfuser відновлюють байт-код із частковою деобфускацією. Повне відновлення вихідного коду з вихідними іменами неможливе — імена втрачаються безповоротно. Сучасні обфускатори (R8, ProGuard) стійкі до автоматичної деобфускації.
Google Play не вимагає обфускації, але наполегливо рекомендує її через minifyEnabled в build.gradle. Додатки без обфускації легко декомпілюються через APKTool та jadx, що робить їх уразливими для крадіжки API-ключів, модифікації та піратства. Більшість великих додатків використовують R8 або ProGuard.
Crash reports після обфускації містять обфусковані імена (a.b.c замість com.example.app.MainActivity). Для відновлення використовуються mapping-файли, що генеруються R8/ProGuard. Mapping-файл має завантажуватися в Firebase Crashlytics або Sentry для автоматичної деобфускації стектрейсів.
String Encryption — заміна рядкових літералів (API-ключі, URL, повідомлення) на зашифровані дані з викликом decrypt-функції в runtime. Це захищає конфіденційні рядки від читання через простий пошук у декомпільованому коді. R8/ProGuard підтримують string encryption через правило -encryptstrings.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також