Obfuscator — eszköz, amely a forráskódot nehezen olvasható formává alakítja anélkül, hogy megváltoztatná a funkcionalitását. Az Obfuscator a szellemi tulajdon védelmére, a kód elemzésének megnehezítésére és a reverse engineering megakadályozására szolgál. A Android Developers Documentation szerint az R8-on és ProGuard-on keresztüli obfuszkáció az Android-alkalmazások éles build-jének szokásos lépése.
Főbb pontok
Obfuscator — program, amely obfuszkációt végez: az olvasható kód átalakítása funkcionálisan egyenértékű, de ember számára olvashatatlan formává. Az obfuszkátor fő feladatai — az azonosítók átnevezése (identifier mangling), a hibakeresési információk eltávolítása, a vezérlési folyamat összezavarása (control flow obfuscation) és a string literálok titkosítása (string encryption).
Az obfuszkáció nem titkosítás. A titkosított kód nem hajtható végre visszafejtés nélkül. Az obfuszkált kódot közvetlenül a JVM, ART vagy JavaScript-motor hajtja végre, de az ember számára rendkívül nehezen érthető. Az obfuszkáció nem nyújt abszolút védelmet — egy elszánt szakember mindig visszaállíthatja a logikát deobfuszkátor vagy futásidejű hibakeresés segítségével.
Az első kereskedelmi obfuszkátor, a ProGuard 2002-ben jelent meg Java appletek eszközeként. Az Android növekedésével (2008) a ProGuard a mobilfejlesztés szabványává vált. 2018-ban a Google kiadta az R8-at a ProGuard helyettesítésére az Android Gradle Plugin 3.4-hez. Az R8 2-3-szor gyorsabb, mint a ProGuard, és kompaktabb bájtkódot generál az SSA (Static Single Assignment) szintű mély optimalizálásnak köszönhetően — ez egy köztes reprezentációs formátum, amely lehetővé teszi az adatfolyam elemzését.
A webfejlesztésben az obfuszkáció az egyszerű cseréktől (YUI Compressor, 2007) a bonyolult AST-transzformátorokig (Obfuscator.io, 2016) fejlődött. A modern JavaScript obfuszkátorok control flow flatteninget, opaque predicates-eket (mindig true vagy false értékű, de az elemző számára nem nyilvánvaló feltételek) és futásidejű önvisszafejtéssel ellátott stringtitkosítást használnak. A Jscrambler (2012) integrálja az obfuszkációt a hibakereső elleni védelemmel és DRM-mechanizmusokkal.
Az obfuszkáció alkalmazási területe széles. A mobilfejlesztésben az obfuszkátorok védik a kódot az APK-dekompilátorokon (jadx, APKTool, dex2jar) keresztüli lopástól. A webfejlesztésben a JavaScript obfuszkáció védi az algoritmusokat, API-kulcsokat és az ügyféloldali üzleti logikát. A könyvtárakban és SDK-kban az obfuszkáció megakadályozza a kód versenytársak általi használatát.
| Technika | Obfuszkáció előtt | Obfuszkáció után |
|---|---|---|
| Osztályok átnevezése | NetworkManager | a |
| Metódusok átnevezése | sendRequest() | b() |
| Stringtitkosítás | "API_KEY" | decrypt("x9fK2p") |
| Feltételek összezavarása | if (a > b) | if (a > b ? true : false) |
Identifier mangling (azonosítók átnevezése) — a legelterjedtebb módszer. Az osztályok, metódusok, mezők és változók nevei rövid, nem informatív sztringekre cserélődnek: a, b, c, aa, ab. Ez megnehezíti a kód egyes elemeinek céljának megértését. A ProGuard és az R8 azonos neveket használ különböző típusokhoz (A osztály, A mező, A metódus), tovább zavarva az elemzést.
Control flow obfuscation (a vezérlési folyamat összezavarása) úgy változtatja meg a kód szerkezetét, hogy a lineáris sorrend ne legyen nyilvánvaló. Holt ágak (dead branches) kerülnek hozzáadásra, a feltételek megfordításra kerülnek (if (!a) az if (a) helyett), goto-szerű operátorok (break/continue címkékkel) kerülnek beillesztésre. Ez rendkívül munkaigényessé teszi az elemzést dekompilátoron és hibakeresőn keresztül.
// Forráskód
function authenticate(token) {
const url = "https://api.example.com/auth";
const headers = { Authorization: "Bearer " + token };
return fetch(url, { method: "POST", headers });
}// Obfuscator.io után magas módban
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 sztringtömböket, a tömb összekeverésére szolgáló önhívó függvényt adott hozzá, átnevezte az összes azonosítót és a sztringeket tömbindexekre cserélte. Az 5 soros forráskód 20+ olvashatatlan sorrá alakult, de az authenticate(token) funkcionalitás teljesen megmaradt. A deobfuszkáció AST-elemzéssel lehetséges, de időigényes.
ProGuard — klasszikus obfuszkátor Java és Android rendszerekhez, 2002 óta használatban. A ProGuard három feladatot lát el: tömörítés (shrinking — nem használt osztályok és metódusok eltávolítása), optimalizálás (bájtkód optimalizálása) és obfuszkáció (azonosítók átnevezése). A ProGuard a proguard-rules.pro fájlon keresztül, a könyvtárakra vonatkozó kivételszabályokkal épül be az Android Gradle Plugin-be.
R8 — modernebb obfuszkátor, amely az AGP 3.4-től kezdve része az Android Gradle Plugin-nek. Az R8 ugyanazokat a funkciókat látja el, mint a ProGuard, de gyorsabban (a semmiből, Kotlin nyelvben írva) és hatékonyabban (jobban optimalizálja a bájtkódot az ART Runtime számára). Az R8 konfigurálása ugyanazokon a proguard-rules.pro fájlokon keresztül történik, mint a ProGuard esetében. Az R8 aktiválásához elegendő a minifyEnabled true beállítása a build.gradle-ben.
Mapping fájl — az R8/ProGuard munkájának eredménye, amely az osztályok, metódusok és mezők eredeti és obfuszkált nevei közötti megfelelést tartalmazza. A mapping fájl kritikus fontosságú a crash report-ok elemzéséhez: nélküle a stack trace a.a.b-t fog tartalmazni a com.example.app.MainActivity.onCreate helyett. A Firebase Crashlytics és a Sentry automatikusan betölti a mapping fájlokat és visszaállítja az eredeti neveket a jelentésekben.
A mapping fájlokat minden új alkalmazásverzió kiadásakor fel kell tölteni a Firebase-be vagy a Sentry-be. Ha a mapping fájl elvész vagy nem kerül feltöltésre, az obfuszkáció után az összes crash report olvashatatlanná válik. Az Android Gradle Plugin automatikusan elmenti a mapping fájlt a build/outputs/mapping/release/mapping.txt helyre. A Firebase-hez a Crashlytics Gradle Plugin használatos, amely a release verzió buildelésekor tölti fel a mapping-et.
// app/build.gradle — obfuszkáció R8-on keresztül
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
"proguard-android-optimize.txt"),
"proguard-rules.pro"
}
}
}# proguard-rules.pro — megőrzési szabályok
# Adatmodell megőrzése Gson számára
-keep class com.example.model.** { *; }
# Osztályok megőrzése Retrofit interfészekhez
-keep,allowobfuscation interface com.example.api.*
# Nyilvános aktivitások ne obfuszkálódjanak
-keep class * extends android.app.Activity {
public protected *;
}
# Logok eltávolítása éles környezetben
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(...);
public static int v(...);
public static int d(...);
}A -keep szabályok a proguard-rules.pro fájlban kritikusak — nélkülük az R8 törli vagy átnevezi a reflexió (Gson, Retrofit, Room) révén használt osztályokat és metódusokat. Az assumenosideeffects eltávolítja a Log.v és Log.d hívásokat az éles kódból. Az olyan könyvtárak, mint a Gson, Retrofit és OkHttp, kész szabályokat szállítanak az AAR-on belüli proguard.txt fájlban.
Obfuscator.io — a legnépszerűbb nyílt forráskódú JavaScript obfuszkátor azonosítók átnevezésének, sztringtitkosításnak, control flow flattening-nek és hibakeresés elleni védelemnek (debug protection) a támogatásával. Az obfuszkáció konfigurálása JSON-konfiguráción vagy CLI-n keresztül történik. Az ingyenes verzió az alapvető módszereket támogatja; a Enterprise verzió polimorf kódot és önvédelmet ad hozzá.
Jscrambler — kereskedelmi JavaScript obfuszkátor kiterjesztett védelemmel: polimorf transzformációk (minden végrehajtás új obfuszkált kódot generál), hibakeresők elleni védelem (DevTools észlelése), képernyőképek elleni védelem (self-defending) és lejárati mechanizmusok (a kód egy adott dátum után leáll). A Jscrambler banki alkalmazásokban és DRM-rendszerekben használatos.
// obfuscate.js — Obfuscator.io konfiguráció
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);Paraméterek Obfuscator.io: controlFlowFlattening: 0.75 a blokkok 75%-ában zavarja össze a vezérlési folyamatot; stringArray: true sztringeket tömbbe helyezi; debugProtection megakadályozza a DevTools megnyitását; disableConsoleOutput eltávolítja a console.log-ot. Minél magasabbak a küszöbértékek, annál hosszabb az obfuszkációs idő és a kód mérete, de annál nehezebb az elemzés.
Az obfuszkáció nem véd a futásidejű elemzés ellen. A támadó futtathatja az alkalmazást egy hibakeresőben (Frida, Objection, Xposed) és valós időben elfoghatja a metódusokat. Az obfuszkáció a statikus elemzés (APK dekompilálása, bájtkód olvasása) ellen véd, de a dinamikus elemzés ellen nem. A futásidejű elemzés elleni védelemhez további intézkedések szükségesek: SSL Pinning, Root Detection, Integrity Verification.
Az alkalmazás mérete az obfuszkáció után 20-50%-kal nőhet. A control flow obfuscation holt ágakat ad hozzá és duplikálja a feltételeket — ez növeli a bájtkód mennyiségét. A string encryption a rövid sztring literálokat decrypt() hívásokra cseréli, ami szintén növeli a méretet. Mobilos alkalmazásoknál ez kritikus, mert az APK mérete közvetlenül befolyásolja a konverziót a Google Play-ben.
A teljesítmény is szenved. A control flow obfuscation további ellenőrzéseket és elágazásokat ad hozzá, 5-15%-kal növelve a metódusok végrehajtási idejét. A string encryption minden sztringhez hozzáféréskor decrypt hívást ad hozzá. A teljesítmény szempontjából kritikus függvényeknél (onDraw Androidban, render Reactben) az obfuszkációt -keep szabályokkal ki kell kapcsolni.
Gyakran Ismételt Kérdések
Titkosítás a kódot visszafejtés nélkül végrehajthatatlanná teszi — a végrehajtáshoz visszafejtő szükséges. Obfuszkáció a kódot olvashatatlanná, de közvetlenül végrehajthatóvá teszi. A titkosítás erősebb védelmet nyújt, de olyan betöltő-visszafejtőt igényel, amely maga is elemezhető.
Deobfuszkáció lehetséges, de munkaigényes. Az olyan eszközök, mint a jadx, JEB Decompiler és UnConfuser, részleges deobfuszkációval állítják vissza a bájtkódot. A forráskód teljes visszaállítása az eredeti nevekkel lehetetlen — a nevek visszavonhatatlanul elvesznek. A modern obfuszkátorok (R8, ProGuard) ellenállnak az automatikus deobfuszkációnak.
Google Play nem követeli meg az obfuszkációt, de erősen ajánlja a minifyEnabled-en keresztül a build.gradle-ben. Az obfuszkáció nélküli alkalmazások könnyen dekompilálhatók az APKTool és jadx segítségével, ami sebezhetővé teszi őket API-kulcsok ellopásával, módosítással és kalózkodással szemben. A legtöbb nagy alkalmazás R8-at vagy ProGuard-ot használ.
Crash report-ok obfuszkáció után obfuszkált neveket tartalmaznak (a.b.c a com.example.app.MainActivity helyett). A visszaállításhoz az R8/ProGuard által generált mapping fájlokat használjuk. A mapping fájlt fel kell tölteni a Firebase Crashlytics-be vagy a Sentry-be a stack trace-ek automatikus deobfuszkációjához.
String Encryption — a sztring literálok (API-kulcsok, URL-ek, üzenetek) cseréje titkosított adatokra a decrypt függvény futásidejű meghívásával. Ez védi a bizalmas sztringeket a dekompilált kódban való egyszerű kereséssel történő kiolvasástól. Az R8/ProGuard támogatja a string encryption-t az -encryptstrings szabályon keresztül.
Összefoglalás
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is