Obfuscator — ابزاری که کد منبع را بدون تغییر عملکرد آن به شکلی دشوار برای خواندن تبدیل میکند. Obfuscator برای حفاظت از مالکیت فکری، دشوار کردن تحلیل کد و جلوگیری از مهندسی معکوس استفاده میشود. بر اساس Android Developers Documentation، مبهمسازی از طریق R8 و ProGuard یک مرحله استاندارد در build تولیدی برنامههای Android است.
نکات اصلی
Obfuscator — برنامهای که مبهمسازی را انجام میدهد: تبدیل کد خوانا به کدی معادل از نظر عملکرد، اما غیرقابل خواندن برای انسان. وظایف اصلی مبهمساز — تغییر نام شناسهها (identifier mangling)، حذف اطلاعات اشکالزدایی، پیچیدهسازی جریان کنترل (control flow obfuscation) و رمزنگاری literalهای رشتهای (string encryption).
مبهمسازی رمزنگاری نیست. کد رمزنگاری شده بدون رمزگشایی قابل اجرا نیست. کد مبهمسازی شده مستقیماً توسط JVM، ART یا موتور JavaScript اجرا میشود، اما درک آن برای انسان بسیار دشوار است. مبهمسازی حفاظت مطلق نمیدهد — یک متخصص با انگیزه همیشه میتواند منطق را از طریق ابزارهای مبهمزدایی یا اشکالزدایی در زمان اجرا بازیابی کند.
اولین مبهمساز تجاری ProGuard در سال 2002 به عنوان ابزاری برای اپلتهای Java ظاهر شد. با رشد Android (2008)، ProGuard به استانداردی برای توسعه موبایل تبدیل شد. در سال 2018 Google R8 را به عنوان جایگزین ProGuard برای Android Gradle Plugin 3.4 منتشر کرد. R8 2-3 برابر سریعتر از ProGuard است و کد بایت فشردهتری از طریق بهینهسازی عمیق در سطح SSA (Static Single Assignment) — فرمت نمایش میانی که امکان تحلیل جریان داده را فراهم میکند — تولید میکند.
در توسعه وب، مبهمسازی از جایگزینی ساده (YUI Compressor، 2007) به تبدیلکنندههای پیچیده AST (Obfuscator.io، 2016) تکامل یافت. مبهمسازهای مدرن JavaScript از control flow flattening، opaque predicates (شرطهایی که همیشه true یا false هستند اما برای تحلیلگر آشکار نیستند) و رمزنگاری رشتهها با خودرمزگشایی در زمان اجرا استفاده میکنند. Jscrambler (2012) مبهمسازی را با محافظت از اشکالزدا و مکانیسمهای DRM یکپارچه میکند.
حوزه کاربرد مبهمسازی گسترده است. در توسعه موبایل، مبهمسازها از کد در برابر سرقت از طریق decompilerهای 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 با برچسب) درج میشوند. این کار تحلیل را از طریق decompiler و debugger بسیار دشوار میکند.
// کد منبع
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 از طریق فایل proguard-rules.pro با قوانین استثنا برای کتابخانهها در Android Gradle Plugin تعبیه میشود.
R8 — مبهمساز مدرنتری که از AGP 3.4 در Android Gradle Plugin گنجانده شده است. R8 همان عملکردهای ProGuard را انجام میدهد، اما سریعتر (از ابتدا به زبان Kotlin نوشته شده) و کارآمدتر (کد بایت را برای ART Runtime بهتر بهینه میکند). پیکربندی R8 از طریق همان فایلهای proguard-rules.pro ProGuard انجام میشود. برای فعال کردن R8 کافی است minifyEnabled true را در build.gradle تنظیم کنید.
فایل mapping — نتیجه کار R8/ProGuard، شامل تطابق بین نامهای اصلی و مبهمسازی شده کلاسها، متدها و فیلدها. فایل mapping برای تحلیل گزارشهای crash حیاتی است: بدون آن stack trace شامل a.a.b به جای com.example.app.MainActivity.onCreate خواهد بود. Firebase Crashlytics و Sentry به طور خودکار فایلهای mapping را بارگذاری کرده و نامهای اصلی را در گزارشها بازیابی میکنند.
فایلهای mapping باید در هر انتشار نسخه جدید برنامه در Firebase یا Sentry بارگذاری شوند. اگر فایل mapping گم شود یا بارگذاری نشود، تمام گزارشهای crash پس از مبهمسازی غیرقابل خواندن میشوند. 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.*
# Activityهای عمومی را مبهمسازی نکن
-keep class * extends android.app.Activity {
public protected *;
}
# logها را در محیط تولید حذف کن
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(...);
public static int v(...);
public static int d(...);
}قوانین -keep در proguard-rules.pro حیاتی هستند — بدون آنها R8 کلاسها و متدهای استفاده شده از طریق بازتاب (reflection) (Gson، Retrofit، Room) را حذف یا تغییر نام میدهد. assumenosideeffects فراخوانیهای Log.v و Log.d را از کد تولیدی حذف میکند. کتابخانههایی مانند Gson، Retrofit و OkHttp قوانین آماده را در proguard.txt داخل AAR ارائه میدهند.
Obfuscator.io — محبوبترین مبهمساز متنباز JavaScript با پشتیبانی از تغییر نام شناسهها، رمزنگاری رشتهها، پیچیدهسازی جریان کنترل (control flow flattening) و محافظت از اشکالزدایی (debug protection). پیکربندی مبهمسازی از طریق JSON-config یا CLI انجام میشود. نسخه رایگان روشهای پایه را پشتیبانی میکند؛ نسخه Enterprise کد چندشکلی و خودحفاظتی را اضافه میکند.
Jscrambler — مبهمساز تجاری JavaScript با محافظت گسترده: تبدیلهای چندشکلی (هر اجرا کد مبهمسازی شده جدیدی تولید میکند)، محافظت از اشکالزدا (تشخیص DevTools)، محافظت از اسکرینشات (self-defending) و مکانیسمهای انقضا (کد پس از تاریخ مشخصی از کار میافتد). 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 را حذف میکند. هرچه آستانهها بالاتر باشند، زمان مبهمسازی و اندازه کد بیشتر، اما تحلیل دشوارتر است.
مبهمسازی محافظت نمیکند از تحلیل زمان اجرا. مهاجم میتواند برنامه را در اشکالزدا (Frida، Objection، Xposed) اجرا کرده و متدها را در زمان واقعی رهگیری کند. مبهمسازی از تحلیل ایستا (decompilation APK، خواندن کد بایت) محافظت میکند، اما از تحلیل پویا نه. برای محافظت از تحلیل زمان اجرا اقدامات اضافی مورد نیاز است: SSL Pinning، Root Detection، Integrity Verification.
اندازه برنامه پس از مبهمسازی ممکن است 20-50% افزایش یابد. Control flow obfuscation شاخههای مرده اضافه کرده و شرایط را تکرار میکند — این مقدار کد بایت را افزایش میدهد. String encryption literalهای کوتاه رشتهای را با فراخوانی 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 decompile میشوند که آنها را در برابر سرقت کلیدهای API، تغییر و دزدی آسیبپذیر میکند. اکثر برنامههای بزرگ از R8 یا ProGuard استفاده میکنند.
گزارشهای crash پس از مبهمسازی شامل نامهای مبهمسازی شده (a.b.c به جای com.example.app.MainActivity) هستند. برای بازیابی از فایلهای mapping تولید شده توسط R8/ProGuard استفاده میشود. فایل mapping باید برای ابهامزدایی خودکار stack traceها در Firebase Crashlytics یا Sentry بارگذاری شود.
String Encryption — جایگزینی literalهای رشتهای (کلیدهای API، URLها، پیامها) با دادههای رمزنگاری شده و فراخوانی تابع decrypt در زمان اجرا. این کار از رشتههای محرمانه در برابر خواندن از طریق جستجوی ساده در کد decompile شده محافظت میکند. R8/ProGuard از string encryption از طریق قانون -encryptstrings پشتیبانی میکنند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید