almasumdev/awesome-mobile-security-agent-skills

code-obfuscation

Code obfuscation on mobile — R8 / ProGuard on Android, SwiftShield and its limitations on iOS, and realistic expectations.

ソースを見る
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Code Obfuscation

Instructions

Obfuscation raises the cost of reverse engineering, it does not prevent it. Treat it as speed bumps, not walls.

1. What Obfuscation Actually Buys You

  • Slows down static analysis with Jadx / Ghidra / Hopper.
  • Strips logging and debug symbols from release builds (real value).
  • Makes automated tooling (Frida scripts pattern-matching on class names) more brittle.

What it does not buy:

  • Protection against dynamic instrumentation (Frida, Objection).
  • Protection for anything the app actually needs to do — a debugger will see it.
  • A substitute for server-side authorization.

2. Android: R8 in Release

R8 is the modern replacement for ProGuard and ships with AGP. Enable both shrinking and obfuscation for release:

kotlin
// app/build.gradle.kts
android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro",
            )
        }
    }
}

Common proguard-rules.pro hygiene:

proguard
# Keep Kotlin metadata for reflection-heavy libs (Moshi, Retrofit)
-keep class kotlin.Metadata { *; }

# Retrofit + OkHttp
-keepattributes Signature, InnerClasses, EnclosingMethod
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response

# Models read via reflection (adjust to your package)
-keep class com.example.app.dto.** { *; }

# Strip logs in release
-assumenosideeffects class android.util.Log {
    public static *** d(...);
    public static *** v(...);
    public static *** i(...);
}

Pitfalls:

  • Serialization libraries (Gson/Moshi/kotlinx-serialization) and reflection: every model read by reflection needs a -keep.
  • JNI / native methods: keep the native method signatures exactly.
  • DI frameworks (Hilt, Koin) usually ship their own consumer rules — don't duplicate.

3. Android: Strip Debug Symbols From Native Libs

kotlin
android {
    packaging {
        jniLibs {
            useLegacyPackaging = false
        }
    }
    buildTypes {
        release {
            ndk { debugSymbolLevel = "none" } // or "symbol_table" for Play upload only
        }
    }
}

Upload symbols to Play separately so your crash reports remain readable.

4. iOS: Symbol Stripping

Swift doesn't have an R8. The standard hardening:

  • In Release build settings:
  • Strip Debug Symbols During Copy = YES
  • Strip Swift Symbols = YES
  • Deployment Postprocessing = YES
  • Symbols Hidden by Default = YES
  • Archive dSYMs separately and upload to your crash reporter.

5. SwiftShield and Friends — Read the Fine Print

SwiftShield renames Swift symbols post-build. Known issues:

  • Can break Objective-C interop and @objc selectors.
  • Can break Codable, NSKeyedArchiver, and any reflection-based serializer.
  • Incompatible with some SDKs that assume class names at runtime.

Use it only if:

  • You have a threat model (financial, DRM, gaming) that justifies the fragility.
  • You have end-to-end UI tests that run against the obfuscated binary on every PR.
  • You accept the maintenance cost.

For most apps, proper symbol stripping + release-mode assert/log removal is enough.

6. React Native / JavaScript

  • Enable Hermes and minify: true in metro.config.js for release.
  • Use babel-plugin-transform-remove-console to strip console.*.
  • Do not rely on JS obfuscators (javascript-obfuscator) for secrets — they're trivially reversible.

7. Flutter / Dart

  • Use flutter build apk --obfuscate --split-debug-info=build/symbols/.
  • Upload the split debug-info directory to your crash reporter (Crashlytics / Sentry) so stack traces remain readable.
  • Dart obfuscation renames symbols but the bytecode structure remains — it is still reverse-engineerable.

8. What to Actually Hide

Rank your concerns:

  1. Nothing sensitive in the binary — no API keys, no hard-coded JWTs, no endpoints that only "security through obscurity" protects.
  2. Strip logs and debug scaffolding from release.
  3. Strip symbols / obfuscate names as a speed bump.
  4. Only then consider paid RASP / commercial obfuscators, and only if a real threat model requires them.

Checklist

  • [ ] R8 (isMinifyEnabled = true) is on for release on Android.
  • [ ] Consumer ProGuard rules for every reflection-heavy library are present.
  • [ ] Native debug symbols are stripped from the release APK / AAB; symbols uploaded to Play.
  • [ ] iOS release strips Swift & debug symbols; dSYMs uploaded to crash reporter.
  • [ ] SwiftShield / similar is only used with a documented threat model and UI tests.
  • [ ] Flutter release uses --obfuscate --split-debug-info; symbols uploaded.
  • [ ] Nothing security-relevant relies on the name of a class remaining hidden.
同じリポジトリから

関連する Skills

すべての Skills