Android Software Reverse Engineering & Decompilation

Frida & Ghidra Lab: Dynamic Decryption of AES-256 Encrypted Strings in Android Apps

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: The Challenge of Encrypted Strings

In the landscape of Android application security, developers often employ various techniques to protect sensitive information and obfuscate logic. One common method is string encryption, where crucial data like API keys, URLs, or error messages are encrypted at compile time and decrypted at runtime. This poses a significant challenge for reverse engineers attempting to understand an application’s true functionality, as static analysis tools will only reveal ciphertexts.

This article provides an expert-level guide on how to dynamically decrypt AES-256 encrypted strings in Android applications. We’ll combine the power of static analysis with Ghidra to pinpoint potential decryption routines and then leverage dynamic instrumentation with Frida to intercept and log the plaintext strings at the moment of decryption.

Setting Up Your Android Reverse Engineering Lab

Prerequisites

  • A rooted Android device or an Android emulator (e.g., Android Studio AVD, Genymotion)
  • Android Debug Bridge (ADB) installed and configured on your host machine
  • Python 3 and pip installed on your host machine
  • Frida client installed on your host machine (pip install frida-tools)
  • Frida-server compatible with your Android device’s architecture (ARM, ARM64, x86, x86_64)
  • Ghidra installed on your host machine

Installing Frida-server on Android

Download the correct frida-server binary from the official Frida releases page. Push it to your device and make it executable:

adb push frida-server-<version>-android-<arch> /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"

Static Analysis with Ghidra: Unveiling Encryption Routines

Our first step is to use Ghidra to perform static analysis on the target Android Package Kit (APK). This helps us identify potential decryption functions, their parameters, and how keys or IVs might be generated.

Loading the APK into Ghidra

Open Ghidra, create a new project, and import your target APK file. Ghidra will automatically decompile the DEX bytecode into Java-like pseudo-code. Once loaded, navigate to the Symbol Tree and begin your search.

Identifying the Decryption Function

Common cryptographic operations often involve specific API calls. Search for keywords related to AES encryption:

  • Cipher.getInstance
  • SecretKeySpec
  • IvParameterSpec
  • AES
  • decrypt
  • doFinal

Look for methods that initialize a Cipher object in DECRYPT_MODE. A typical AES decryption setup involves:

  1. Instantiating a Cipher object with a transformation string (e.g., "AES/CBC/PKCS5Padding").
  2. Creating a SecretKeySpec from a byte array (the encryption key).
  3. Creating an IvParameterSpec from another byte array (the Initialization Vector).
  4. Initializing the Cipher in DECRYPT_MODE with the key and IV.
  5. Calling doFinal on the encrypted data.

Consider this hypothetical Java method we might find in Ghidra’s decompilation:

public String decryptSensitiveString(String base64EncodedCiphertext, byte[] keyBytes, byte[] ivBytes) throws Exception {
    SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
    IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
    byte[] decodedCiphertext = Base64.decode(base64EncodedCiphertext, Base64.DEFAULT);
    byte[] decryptedBytes = cipher.doFinal(decodedCiphertext);
    return new String(decryptedBytes, StandardCharsets.UTF_8);
}

From Ghidra, we would identify the class containing decryptSensitiveString (e.g., com.example.app.CryptoUtil) and its full method signature. Pay close attention to how the keyBytes and ivBytes are generated or provided. They might be hardcoded, derived from a salt, or even fetched dynamically. For dynamic decryption, knowing the exact method and its arguments is key.

Dynamic Analysis with Frida: Intercepting Decryption

Once we’ve identified the potential decryption routine statically, we use Frida to hook into the application at runtime, specifically targeting the identified method. This allows us to observe the arguments passed to the decryption function (encrypted string, key, IV) and, most importantly, capture the returned plaintext string.

Crafting the Frida Script

Create a JavaScript file (e.g., decrypt_hook.js) with the following content. Remember to replace com.example.app.CryptoUtil and decryptSensitiveString with the actual class and method names found in Ghidra.

Java.perform(function () {
    // Replace with the actual target class and method found in Ghidra
    var targetClass = Java.use("com.example.app.CryptoUtil");
    var targetMethod = "decryptSensitiveString"; 

    // Hook the implementation of the target method
    targetClass[targetMethod].implementation = function (base64EncodedCiphertext, keyBytes, ivBytes) {
        console.log("n[*] Decryption method '" + targetMethod + "' called!");
        console.log("  Encrypted String (Base64): " + base64EncodedCiphertext);

        // Convert byte arrays to hex strings for easier logging and analysis
        var keyHex = Java.array('byte', keyBytes).map(function(i) {
            return ('0' + (i & 0xFF).toString(16)).slice(-2);
        }).join('');
        var ivHex = Java.array('byte', ivBytes).map(function(i) {
            return ('0' + (i & 0xFF).toString(16)).slice(-2);
        }).join('');

        console.log("  Key (Hex): " + keyHex);
        console.log("  IV (Hex): " + ivHex);

        // Call the original method to get the actual decrypted string
        var decryptedString = this[targetMethod](base64EncodedCiphertext, keyBytes, ivBytes);
        console.log("  Decrypted String: " + decryptedString);

        // Return the original result to not break application functionality
        return decryptedString;
    };
    console.log("[*] Frida hook for '" + targetMethod + "' is active.");
});

Executing the Frida Script

With frida-server running on your device, execute the Frida client on your host machine. We’ll use the -U flag for USB device, -f to spawn the application (replace com.example.app with the target package name), -l to load our script, and --no-pause to let the app start immediately.

frida -U -f com.example.app -l decrypt_hook.js --no-pause

As the application runs and the targeted decryption function is invoked, you will see output in your terminal similar to this:

[*] Frida hook for 'decryptSensitiveString' is active.

[*] Decryption method 'decryptSensitiveString' called!
  Encrypted String (Base64): aGVsbG8gd29ybGQ=
  Key (Hex): 0123456789abcdef0123456789abcdef
  IV (Hex): fedcba9876543210fedcba9876543210
  Decrypted String: This is a secret message!

[*] Decryption method 'decryptSensitiveString' called!
  Encrypted String (Base64): c29tZSBvdGhlciBzZWNyZXQ=
  Key (Hex): 0123456789abcdef0123456789abcdef
  IV (Hex): fedcba9876543210fedcba9876543210
  Decrypted String: Another secret string.

This output clearly shows the encrypted string, the key and IV used for decryption, and most importantly, the plaintext string that the application intended to use. By observing multiple calls, you can gather all sensitive strings used by the application.

Conclusion: Mastering Dynamic Decryption

Combining the static analysis capabilities of Ghidra with the dynamic instrumentation power of Frida provides an extremely effective methodology for tackling encrypted strings in Android applications. Ghidra helps us pinpoint the specific code segments responsible for decryption, while Frida allows us to transparently intercept these operations at runtime and extract the sensitive plaintext data.

This technique is not limited to AES-256; it can be adapted to various other encryption algorithms and obfuscation schemes. By understanding the core principles of identifying cryptographic routines and dynamically hooking them, reverse engineers can overcome a significant hurdle in analyzing Android application security and functionality. Future explorations could involve automating string extraction or building more complex Frida scripts to handle polymorphic decryption strategies.

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner