Question #441HardSecurityImportant

What is reverse engineering? How to reverse engineer any app in Flutter? How to protect against reverse engineering in Flutter apps?

#security#reverse-engineering#obfuscation#certificate-pinning#apk#flutter#proguard#secure-storage

Answer

What is Reverse Engineering?

Reverse engineering is the process of analyzing a compiled application to understand its internal logic, extract source code, assets, API keys, or business logic — without access to the original source code.

In mobile apps, reverse engineers use tools to decompile APKs/IPAs, inspect network traffic, analyze binary code, and extract sensitive data.


How to Reverse Engineer a Flutter App

Flutter compiles to native ARM code (AOT compilation), making it harder to reverse engineer than React Native or Cordova apps — but not impossible.

Step 1: Extract the APK

bash
adb shell pm path com.example.app
adb pull /data/app/com.example.app.apk

Step 2: Decompile with APKTool

bash
apktool d app.apk -o output_folder

This extracts:

  • text
    AndroidManifest.xml
    — permissions, activities, deep links
  • text
    res/
    — layouts, drawables, strings
  • text
    smali/
    — Dalvik bytecode (wrapper code only for Flutter)
  • text
    lib/
    — native
    text
    .so
    files (Flutter engine + app code)

Step 3: Analyze the Flutter
text
.so
Binary

Flutter app logic lives in

text
libapp.so
. Use tools like Blutter to analyze it:

bash
# Blutter: Flutter reverse engineering tool
git clone https://github.com/worawit/blutter
python3 setup.py com.example.app/lib/arm64-v8a/libapp.so output/

Blutter can reconstruct Dart class names, method names, and field names from the snapshot.

Step 4: Inspect Network Traffic

Use a proxy like mitmproxy or Charles Proxy to intercept API calls:

bash
mitmproxy --mode transparent

This exposes API endpoints, request/response payloads, and auth tokens (if certificate pinning is not implemented).

Step 5: Extract Hardcoded Secrets

Use the

text
strings
command on the native binary:

bash
strings libapp.so | grep -i "api_key\|secret\|token\|password"

How to Protect Flutter Apps from Reverse Engineering

1. Enable Code Obfuscation (Most Important)

Flutter supports Dart obfuscation during release builds:

bash
flutter build apk --obfuscate --split-debug-info=./debug-info

This renames classes, methods, and fields to meaningless symbols, making tools like Blutter produce unreadable output.

Important: Save the

text
debug-info
directory — you need it to decode crash stack traces in production.

2. Never Hardcode Secrets

dart
// ❌ Wrong — easily extracted from binary
const String apiKey = 'sk-abc123...';

// ✅ Better — use environment variables at build time
const String apiKey = String.fromEnvironment('API_KEY');

Build with:

bash
flutter build apk --dart-define=API_KEY=sk-abc123...

Note: For truly sensitive keys, use a backend proxy —

text
--dart-define
values can still be extracted from the binary by a skilled attacker.

3. Implement Certificate Pinning

Prevent traffic interception by pinning your server's SSL certificate:

dart
import 'package:dio/dio.dart';
import 'dart:io';

final dio = Dio();
(dio.httpClientAdapter as IOHttpClientAdapter).createHttpClient = () {
  final client = HttpClient();
  client.badCertificateCallback = (cert, host, port) {
    // Validate against your pinned certificate fingerprint
    return _isPinnedCertificate(cert);
  };
  return client;
};

4. Root / Jailbreak Detection

dart
import 'package:flutter_jailbreak_detection/flutter_jailbreak_detection.dart';

Future<void> checkDeviceSecurity() async {
  bool isJailbroken = await FlutterJailbreakDetection.jailbroken;
  bool isDeveloperMode = await FlutterJailbreakDetection.developerMode;

  if (isJailbroken || isDeveloperMode) {
    // Block access or warn user
    showSecurityWarningDialog();
  }
}

5. Tamper Detection (Signature Verification)

Verify your APK signature at runtime to detect repackaged apps:

dart
import 'package:package_info_plus/package_info_plus.dart';

Future<bool> isAppTampered() async {
  final info = await PackageInfo.fromPlatform();
  // Compare against your expected package name
  return info.packageName != 'com.yourcompany.app';
}

6. Enable ProGuard / R8 for Android

Minify and obfuscate the Java/Kotlin wrapper layer in

text
android/app/build.gradle
:

groovy
buildTypes {
  release {
    minifyEnabled true
    shrinkResources true
    proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
                 'proguard-rules.pro'
  }
}

7. Use Secure Storage for Sensitive Data

dart
import 'package:flutter_secure_storage/flutter_secure_storage.dart';

const storage = FlutterSecureStorage();

// Store securely using Android Keystore / iOS Keychain
await storage.write(key: 'auth_token', value: token);

// Retrieve
final token = await storage.read(key: 'auth_token');

Protection Techniques Summary

TechniqueProtects AgainstDifficulty
text
--obfuscate
flag
Code analysis, BlutterEasy
No hardcoded secretsString extractionEasy
text
--dart-define
Accidental exposureEasy
ProGuard / R8Java/Kotlin layer analysisEasy
Secure storageData extractionEasy
Certificate pinningMITM, traffic sniffingMedium
Root/jailbreak detectionPrivileged attacksMedium
Tamper detectionAPK repackagingMedium
Backend proxy for secretsComplete key exposureMedium

Key Takeaway: Security is layered. No single technique is foolproof — a determined attacker with enough time can reverse engineer any app. Your goal is to raise the cost of attack high enough that it's not worth the effort.

Learn more at OWASP Mobile Security and Flutter Obfuscation Docs.