In the realm of Android application penetration testing and reverse engineering, static analysis often serves as the initial reconnaissance phase. Tools like JADX or Ghidra allow us to decompile APKs and inspect their source code. However, relying solely on static analysis has significant limitations: code obfuscation can render decompiled code almost unreadable, dynamic loading of classes might be missed, and runtime behaviors, such as method calls and parameter values, remain opaque. This is where dynamic analysis, particularly with a powerful framework like Frida, becomes indispensable.
The Limitations of Static Analysis
Static analysis provides a snapshot of an application’s potential execution paths but struggles with several dynamic aspects:
- Obfuscation: ProGuard, DexGuard, and similar tools rename classes, methods, and fields, making decompiled code hard to follow.
- Dynamic Loading: Classes or even entire DEX files might be loaded at runtime based on certain conditions or fetched from remote servers, which static analysis tools cannot predict.
- Runtime Behavior: It’s impossible to know the exact values of variables, the order of method calls, or the outcomes of conditional logic without executing the application.
- Environment-Dependent Logic: Some security checks or functionalities might only activate under specific device conditions (e.g., rooted vs. non-rooted device).
Frida bridges this gap by allowing us to inject custom JavaScript code into running processes, enabling real-time inspection and manipulation of an application’s execution.
Prerequisites for Dynamic Analysis with Frida
Before diving into class and method discovery, ensure you have the following setup:
- Rooted Android Device or Emulator: Frida requires root privileges to inject into arbitrary processes.
- ADB (Android Debug Bridge): For pushing files and interacting with the device shell.
- Frida Server: Running on the target Android device.
- Frida Tools: Installed on your host machine (
pip install frida-tools).
Setting Up Frida Server on Android
1. Download the appropriate Frida server binary for your device’s architecture (e.g., frida-server-*-android-arm64) from Frida’s GitHub releases.
2. Push the server to your device:
adb push frida-server-*-android-arm64 /data/local/tmp/frida-server
3. Set execute permissions and run it:
adb shell "chmod +x /data/local/tmp/frida-server"adb shell "/data/local/tmp/frida-server &"
Confirm it’s running by executing frida-ps -U on your host. You should see a list of running processes on your device.
Understanding Android Runtime (ART) and Frida’s Interaction
Android applications primarily run on the Android Runtime (ART), which executes Dalvik Executable (DEX) bytecode. Frida interacts with the ART by injecting a JavaScript engine (Duktape or V8) into the target process. This allows your JavaScript code to directly interact with the application’s Java objects, classes, and methods through Frida’s provided APIs, notably Java.* functions.
Core Concepts: Java.enumerateClasses() and Java.use()
Frida provides two primary mechanisms for runtime class and method discovery:
Java.enumerateClasses(callbacks): This function iterates through all currently loaded Java classes in the target process’s ART environment. It’s incredibly useful for broad reconnaissance.Java.use(className): This function allows you to obtain a JavaScript wrapper around a specific Java class. Once you have this wrapper, you can inspect its methods, fields, and even instantiate new objects of that class.
Step-by-Step: Enumerating All Loaded Classes
Let’s start by listing every class currently loaded by an Android application. This provides a comprehensive overview of the application’s components and potentially third-party libraries.
Frida Script: enumerate_classes.js
Java.perform(function () { console.log("[+] Enumerating all loaded classes..."); var foundClasses = []; Java.enumerateClasses({ onMatch: function(className) { foundClasses.push(className); }, onComplete: function() { console.log("[+] Total classes found: " + foundClasses.length); // Sort for easier readability (optional) foundClasses.sort(); foundClasses.forEach(function(name) { console.log(name); }); console.log("[+] Enumeration complete."); } });});
Running the Script
First, identify the package name of your target application (e.g., com.example.myapp). You can get this using adb shell pm list packages or frida-ps -Uai. Then, attach Frida:
frida -U -f com.example.myapp -l enumerate_classes.js --no-pause
The --no-pause flag ensures the application starts immediately and the script runs. You’ll see a long list of classes printed to your console. You might want to filter this output, perhaps focusing on classes belonging to the application’s package name (e.g., com.example.*).
Refining Class Enumeration with Filtering
To make the output more manageable, we can add a filter to our script:
Java.perform(function () { console.log("[+] Enumerating classes with filter 'com.example'..."); var foundClasses = []; var filter = "com.example"; // Or any other package/name you're interested in Java.enumerateClasses({ onMatch: function(className) { if (className.startsWith(filter)) { foundClasses.push(className); } }, onComplete: function() { console.log("[+] Total filtered classes found: " + foundClasses.length); foundClasses.sort(); foundClasses.forEach(function(name) { console.log(name); }); console.log("[+] Filtered enumeration complete."); } });});
This script will only log classes whose names start with “com.example”, significantly narrowing down your search.
Step-by-Step: Discovering Methods of a Specific Class
Once you’ve identified an interesting class, the next step is to discover its methods and fields. This helps in understanding its functionality and potential attack vectors.
Frida Script: enumerate_methods.js
Let’s assume we are interested in a class named com.example.myapp.SomeSecurityClass.
Java.perform(function () { var targetClassName = "com.example.myapp.SomeSecurityClass"; try { var targetClass = Java.use(targetClassName); console.log("[+] Class found: " + targetClassName); console.log(" [+] Listing methods:"); var methods = targetClass.class.getDeclaredMethods(); methods.forEach(function(method) { console.log(" - " + method.getName() + "(" + method.getParameterTypes().map(function(type){ return type.getName(); }).join(", ") + ")"); }); console.log(" [+] Listing fields:"); var fields = targetClass.class.getDeclaredFields(); fields.forEach(function(field) { console.log(" - " + field.getType().getName() + " " + field.getName()); }); } catch (e) { console.log("[-] Could not find or process class: " + targetClassName + " Error: " + e.message); }});
Running the Script
frida -U -f com.example.myapp -l enumerate_methods.js --no-pause
This script uses Java.use() to get a reference to the SomeSecurityClass. Then, it leverages the class property (which gives you the underlying java.lang.Class object) to call getDeclaredMethods() and getDeclaredFields(). These methods provide java.lang.reflect.Method and java.lang.reflect.Field objects, from which you can extract names, return types, and parameter types.
Practical Use Cases and Further Exploration
With these foundational techniques, you can:
- Bypass Security Controls: Identify methods related to root detection, SSL pinning, or anti-tampering, then use Frida to hook and modify their return values.
- Understand Obfuscated Code: Even with obfuscated names, enumerating methods at runtime can reveal their true signatures (parameters, return types), helping to infer functionality.
- API Reconnaissance: Discover hidden or undocumented APIs within an application, which might expose sensitive data or functionality.
- Event Listener Discovery: Find dynamically registered broadcast receivers, content providers, or event listeners.
The ability to dynamically discover classes and methods is a cornerstone of advanced Android penetration testing. It allows you to move beyond the static limitations and truly interact with the application as it behaves in its natural environment. From here, you can extend your Frida scripts to hook these discovered methods, inspect arguments, modify return values, and even call arbitrary methods, unlocking a vast array of possibilities for runtime analysis and manipulation.
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 →