The native binary challenge: Analyzing Dart code
Unlike React Native, which bundles JavaScript, Flutter compiles Dart code directly into a native library (like libapp.so on Android). If you try to decompile this library with standard tools like JADX, you will only see the native wrapper code, not your Dart logic.
To analyze the app, we use tools like reFlutter or Ghidra. The reFlutter tool patches the Flutter engine inside the APK, allowing us to inspect the Dart class structure and export all API endpoints.
Once we identify the memory offsets for key functions, we use Frida scripts to hook into the runtime. This allows us to view variables, change return values, and extract cryptographic keys in memory.
Frida hook bypasses pin validation in Flutter wallet
On a Flutter-based mobile wallet app, the transaction approval PIN was checked locally before triggering the API. We used a Frida script to hook the validation method inside libapp.so. By replacing the native return value with a hardcoded true, we bypassed the local PIN entry screen and authorized payments without knowing the code.
Defeating Flutter SSL Pinning
Flutter does not use the host operating system's network stack. Instead, Dart compiles its own HTTP library (BoringSSL). Because of this, standard Android proxy settings and system trust certificates are ignored by the app.
To capture the network traffic, we use custom Frida scripts that target the BoringSSL validation offsets. By locating the verification functions in the compiled Dart engine and patching them at runtime, we force the application to send all traffic through our Burp Suite proxy.
// Example Frida logic to bypass BoringSSL check in Flutter (simplified)
var target_function_offset = 0x1a2b3c; // Offset for ssl_verify_peer_cert
Interceptor.replace(Module.findExportByName("libflutter.so", "ssl_verify_peer_cert") ||
Module.getBaseAddress("libflutter.so").add(target_function_offset),
new NativeCallback(function () {
console.log("[SSL Verify] Bypass triggered");
return 0; // Return verification success
}, 'int', [])); Securing your Flutter application
To protect your Flutter application from dynamic analysis, make sure you implement these controls:
- Use cryptographically backed storage: Store all user secrets in the secure systems (Keychain/KeyStore) using verified libraries.
- Implement server-side verification: The client must never make authorization decisions. The server must check the user's role and balance for every transaction.
- Use RASP controls: Detect if the application is running on a rooted device or if a debugger is attached.
Get your Flutter app audited
Flutter apps are complex to audit because of their native compilation. Automated scanners will miss most binary vulnerabilities and logic flaws. You need manual penetration testing.
At Simpa Labs, we specialize in reverse-engineering mobile binaries in Nigeria and globally. We will check your APIs, test your local storage, and audit your Dart code to make sure it is secure.