Android Dynamic Analysis
Overview
Android dynamic analysis examines an app while it runs — hooking methods at runtime with Frida, monitoring logs and filesystem changes, intercepting network traffic, and manipulating app behavior. This complements static analysis by revealing runtime secrets, server-side logic, and behaviors that are obfuscated or loaded dynamically.
Frida Basics
Frida injects a JavaScript engine into a running process, allowing you to hook any function, read memory, and modify behavior in real time.
Listing Processes and Apps
# Frida
# https://github.com/frida/frida
# List running processes on USB-connected device
frida-ps -U
# List only applications (not system processes)
frida-ps -Ua
# List installed applications (including not running)
frida-ps -Uai
Attaching to a Running App
# Frida
# https://github.com/frida/frida
# Attach to a running app by package identifier
frida -U -N com.example.app
# Spawn an app (start it with Frida attached from the beginning)
frida -U -f com.example.app
# Attach and load a script
frida -U -N com.example.app -l hook_script.js
# Spawn and load a script
frida -U -f com.example.app -l hook_script.js
Basic Frida Scripts
Hook a Java method and log arguments:
// hook_login.js — hook a login method to capture credentials
Java.perform(function () {
var LoginActivity = Java.use('com.example.app.LoginActivity');
LoginActivity.login.implementation = function (username, password) {
console.log('[+] Username: ' + username);
console.log('[+] Password: ' + password);
// Call the original method
return this.login(username, password);
};
});
Hook an overloaded method:
// hook_overloaded.js — hook a method with specific parameter types
Java.perform(function () {
var MyClass = Java.use('com.example.app.MyClass');
MyClass.process.overload('java.lang.String', 'int').implementation =
function (str, num) {
console.log('[+] String: ' + str + ', Int: ' + num);
return this.process(str, num);
};
});
Enumerate loaded classes:
// enum_classes.js — list all loaded classes matching a pattern
Java.perform(function () {
Java.enumerateLoadedClasses({
onMatch: function (className) {
if (className.includes('example')) {
console.log(className);
}
},
onComplete: function () {
console.log('[*] Done enumerating classes');
}
});
});
List methods of a class:
// list_methods.js — list all methods of a specific class
Java.perform(function () {
var cls = Java.use('com.example.app.TargetClass');
var methods = cls.class.getDeclaredMethods();
methods.forEach(function (method) {
console.log(method.toString());
});
});
Hooking Native Functions
// hook_native.js — hook a native function in a shared library
Interceptor.attach(Module.findExportByName('libnative.so', 'secret_function'), {
onEnter: function (args) {
console.log('[+] secret_function called');
console.log('[+] arg0: ' + args[0]);
console.log('[+] arg1: ' + args[1].readUtf8String());
},
onLeave: function (retval) {
console.log('[+] Return value: ' + retval);
}
});
Objection
Objection provides pre-built commands for common mobile testing tasks, built on top of Frida.
Starting a Session
# objection
# https://github.com/sensepost/objection
# Attach to a running app (device with frida-server)
objection -n com.example.app start
# Spawn the app
objection --spawn -n com.example.app start
# Connect over network instead of USB
objection -N -h <device_ip> -n com.example.app start
Common Objection Commands
Once inside the objection REPL:
# List activities
android hooking list activities
# List services
android hooking list services
# List broadcast receivers
android hooking list receivers
# List all classes (filter with grep)
android hooking list classes
android hooking search classes login
# List methods of a class
android hooking list class_methods com.example.app.LoginActivity
# Hook a method and watch arguments/return values
android hooking watch class_method com.example.app.LoginActivity.login --dump-args --dump-return
# Hook all methods in a class
android hooking watch class com.example.app.LoginActivity
# Set a method's return value
android hooking set return_value com.example.app.SecurityCheck.isRooted false
# Dump the Android Keystore
android keystore list
# List files in the app's data directory
env
ls
# Download a file from the device
file download /data/data/com.example.app/shared_prefs/prefs.xml ./prefs.xml
# Dump shared preferences
android hooking search classes SharedPreferences
# Check for root detection
android root disable
# Disable SSL pinning
android sslpinning disable
Logcat Monitoring
Android logs often contain sensitive information leaked by the app.
# View all logs
adb logcat
# Filter by app process
adb logcat --pid=$(adb shell pidof com.example.app)
# Filter by tag
adb logcat -s "MyAppTag"
# Filter by priority (V=Verbose, D=Debug, I=Info, W=Warn, E=Error)
adb logcat *:E
# Clear log buffer and start fresh
adb logcat -c && adb logcat
# Save logs to file
adb logcat > app_logs.txt
Look for: - Hardcoded credentials or tokens logged during debugging - API endpoints and parameters - Stack traces revealing internal logic - SQL queries logged by the ORM layer
Filesystem Inspection
App Data Directories
# App private data (requires root)
adb shell ls /data/data/com.example.app/
# Common subdirectories:
# /databases/ — SQLite databases
# /shared_prefs/ — XML preference files
# /files/ — App-created files
# /cache/ — Cached data
# Pull a database for inspection
adb pull /data/data/com.example.app/databases/app.db ./
# Open with sqlite3
sqlite3 app.db
sqlite> .tables
sqlite> SELECT * FROM users;
# Pull shared preferences
adb pull /data/data/com.example.app/shared_prefs/ ./shared_prefs/
# Check external storage for sensitive data
adb shell ls /sdcard/Android/data/com.example.app/
ADB Backup (if allowBackup=true)
# Create a backup of the app's data
adb backup -f backup.ab com.example.app
# Extract the backup (requires Android Backup Extractor — abe.jar)
# Convert .ab to .tar
java -jar abe.jar unpack backup.ab backup.tar
tar xf backup.tar
Traffic Interception
Using Burp Suite with ADB
# Set device proxy to Burp listener
adb shell settings put global http_proxy <burp_ip>:8080
# After testing, remove the proxy
adb shell settings put global http_proxy :0
For apps that use their own HTTP stack or WebSocket connections, use Frida to hook the networking layer directly.
Monitoring DNS Queries
# Watch DNS resolution on the device
adb shell dumpsys connectivity | grep -i dns
Interacting with Exported Components
# Start an exported activity
adb shell am start -n com.example.app/.AdminActivity
# Start with intent data
adb shell am start -n com.example.app/.DeepLinkActivity \
-d "example://admin?token=test"
# Send a broadcast to an exported receiver
adb shell am broadcast -a com.example.DEBUG \
-n com.example.app/.DebugReceiver \
--es "command" "dump_data"
# Query an exported content provider
adb shell content query --uri content://com.example.app.provider/users