Mobile Testing Tools
Purpose
Deep reference for Android and iOS security testing tools. Covers the complete testing workflow from static analysis through dynamic instrumentation.
ADB โ Android Debug Bridge
The primary interface to an Android device during testing.
Device Management
# List connected devices
adb devices
# Output: device serial number + state (device/unauthorized/offline)
# Shell on specific device (when multiple connected)
adb -s emulator-5554 shell
adb -s R3CN90FXXX shell
# USB vs TCP/IP connection
adb tcpip 5555 # Switch device to TCP mode
adb connect 192.168.1.100:5555 # Connect over Wi-Fi
adb disconnect # Disconnect TCP connection
# Device info
adb shell getprop ro.product.model # Device model
adb shell getprop ro.build.version.release # Android version
adb shell getprop ro.build.version.sdk # API level
adb shell getprop ro.product.cpu.abi # Architecture
App Management
# List installed packages
adb shell pm list packages # All packages
adb shell pm list packages -3 # Third-party only
adb shell pm list packages -f # With APK path
adb shell pm list packages | grep example # Filter by name
# Get APK path
adb shell pm path com.example.app
# Output: package:/data/app/com.example.app-xxx/base.apk
# Pull APK from device
adb pull /data/app/com.example.app-xxx/base.apk ./app.apk
# Install APK
adb install app.apk
adb install -r app.apk # Reinstall (keep data)
adb install -d app.apk # Allow downgrade
# Uninstall
adb uninstall com.example.app
adb uninstall -k com.example.app # Keep data
# Start specific activity
adb shell am start -n com.example.app/.MainActivity
adb shell am start -n com.example.app/.LoginActivity
# Test deep links
adb shell am start -a android.intent.action.VIEW \
-d "https://app.example.com/reset?token=test" \
com.example.app
# Send broadcast
adb shell am broadcast -a com.example.app.ACTION_UPDATE
File Operations
# Download from device
adb pull /data/data/com.example.app/shared_prefs/prefs.xml ./
adb pull /data/data/com.example.app/databases/app.db ./
adb pull /sdcard/Download/ ./downloads/
# Upload to device
adb push ./payload /data/local/tmp/payload
adb shell chmod 755 /data/local/tmp/payload
# Access app private data (non-root, via run-as)
adb shell run-as com.example.app ls /data/data/com.example.app/
adb shell run-as com.example.app cat /data/data/com.example.app/shared_prefs/prefs.xml
# App data directories to check:
# /data/data/com.example.app/shared_prefs/ โ SharedPreferences XML
# /data/data/com.example.app/databases/ โ SQLite databases
# /data/data/com.example.app/files/ โ App files
# /data/data/com.example.app/cache/ โ Cache
# /sdcard/Android/data/com.example.app/ โ External storage
Logging and Monitoring
# All logcat output
adb logcat
# Filter by tag
adb logcat -s MyApp:D
# Filter by priority (V/D/I/W/E/F)
adb logcat *:W # Warnings and above only
# Grep for sensitive strings
adb logcat | grep -iE "password|token|key|secret|credential|api"
# Save logs to file
adb logcat > app_logs.txt
# Clear logcat buffer
adb logcat -c
# Show log with timestamps
adb logcat -v time
# Monitor specific process
PID=$(adb shell pidof com.example.app)
adb logcat --pid=$PID
# Network traffic monitoring (combine with tcpdump on device)
adb shell tcpdump -i any -s 0 -w /sdcard/capture.pcap
adb pull /sdcard/capture.pcap ./
# Open in Wireshark
Proxy Setup for Traffic Interception
# Method 1: System proxy (HTTP/HTTPS)
adb shell settings put global http_proxy 192.168.1.50:8080
adb shell settings delete global http_proxy # Remove proxy
# Method 2: Wi-Fi proxy (manual on device)
# Settings โ Wi-Fi โ Long press network โ Modify โ Advanced โ Proxy
# Install Burp CA certificate
# Export Burp cert: Proxy โ Options โ Export CA Certificate โ DER format
adb push burp_ca.der /sdcard/burp_ca.der
# On device: Settings โ Security โ Install from storage โ select file
# Android 11+: Settings โ Security โ More security settings โ Encryption & credentials
# Verify proxy is working
adb shell settings get global http_proxy
Port Forwarding
# Forward device port to host (access device service from computer)
adb forward tcp:8080 tcp:8080
# Reverse forward (access computer service from device)
adb reverse tcp:9090 tcp:9090
# List forwards
adb forward --list
adb reverse --list
# Remove forward
adb forward --remove tcp:8080
# Common use case: Frida server port
adb forward tcp:27042 tcp:27042
apktool โ APK Decompilation and Repackaging
# Decompile APK
apktool d app.apk -o output_dir/
# Decompile with specific options
apktool d app.apk -o output_dir/ --no-res # Skip resource decoding
apktool d app.apk -o output_dir/ --force # Overwrite if exists
apktool d app.apk -o output_dir/ -r # No resource decoding (faster)
apktool d app.apk -o output_dir/ --only-main-classes # Only main DEX
# Output structure:
# output_dir/
# โโโ AndroidManifest.xml โ Decoded manifest
# โโโ apktool.yml โ Build metadata
# โโโ res/ โ Decoded resources
# โ โโโ values/
# โ โโโ strings.xml โ String values including URLs, keys
# โโโ smali/ โ Disassembled Dalvik bytecode
# โโโ lib/ โ Native libraries
# Rebuild APK after modification
apktool b output_dir/ -o app_modified.apk
apktool b output_dir/ -o app_modified.apk --use-aapt2
# Generate signing keystore
keytool -genkeypair -v \
-keystore test.jks \
-alias testkey \
-keyalg RSA \
-keysize 2048 \
-validity 10000 \
-storepass password \
-keypass password \
-dname "CN=Test,O=Test,C=US"
# Sign APK
apksigner sign \
--ks test.jks \
--ks-key-alias testkey \
--ks-pass pass:password \
--key-pass pass:password \
--out app_signed.apk \
app_modified.apk
# Verify signature
apksigner verify app_signed.apk
# Install signed APK
adb install -r app_signed.apk
What to Look For in apktool Output
# 1. AndroidManifest.xml โ critical security indicators
grep -i "debuggable" output_dir/AndroidManifest.xml # Should be false/absent
grep -i "allowBackup" output_dir/AndroidManifest.xml # Should be false
grep -i "exported" output_dir/AndroidManifest.xml # Note exported components
grep -i "permission" output_dir/AndroidManifest.xml # All permissions requested
grep -i "networkSecurityConfig" output_dir/AndroidManifest.xml
# 2. Network Security Config
cat output_dir/res/xml/network_security_config.xml
# Look for: cleartextTrafficPermitted, certificate pinning config, trust anchors
# 3. Strings โ hardcoded secrets
grep -ri "api" output_dir/res/values/strings.xml
grep -ri "key" output_dir/res/values/strings.xml
grep -ri "password" output_dir/res/values/strings.xml
grep -ri "http://" output_dir/res/values/strings.xml
# 4. Smali code โ dangerous API usage
grep -r "getSharedPreferences\|putString\|putPassword" output_dir/smali/
grep -r "SecretKeySpec\|DESede\|AES/ECB" output_dir/smali/
grep -r "Runtime.getRuntime\|exec\b" output_dir/smali/
jadx โ Decompile APK to Java
jadx produces more readable code than apktool's smali output.
# CLI decompile
jadx -d output_dir/ app.apk
# With options
jadx -d output_dir/ app.apk \
--deobf \ # Attempt deobfuscation
--show-bad-code \ # Show decompile errors inline
--no-imports \ # Full class names (avoid ambiguity)
--threads-count 4 # Parallel decompilation
# GUI (recommended for large codebases)
jadx-gui app.apk
# Features: file navigation, full-text search, xref (find usages)
# Decompile multiple DEX files (if multidex app)
jadx -d output_dir/ classes.dex classes2.dex classes3.dex
Key Search Patterns After Decompilation
# Hardcoded credentials and secrets
grep -r "api_key\|apikey\|api-key" output_dir/ -i
grep -r "password\|passwd\|pwd" output_dir/ -i
grep -r "secret\|token\|bearer" output_dir/ -i
grep -r "AWS_\|AZURE_\|GCP_" output_dir/ -i
grep -r "firebase\|firebaseio" output_dir/ -i
# HTTP URLs (cleartext traffic)
grep -r "http://" output_dir/ | grep -v "//" # Exclude comments
# Logging sensitive data
grep -r "Log\.d\|Log\.v\|Log\.i\|Log\.e\|Log\.w" output_dir/ | \
grep -v "TAG" | head -50 # Show non-tag log calls
# Insecure cryptography
grep -r "AES/ECB\|DES\|RC4\|MD5\|SHA-1\b" output_dir/ -i
grep -r "SecretKeySpec\|PBEKeySpec" output_dir/
grep -r "new Random()\|Math\.random()" output_dir/
# WebView JavaScript (XSS risk)
grep -r "setJavaScriptEnabled(true)\|addJavascriptInterface" output_dir/
grep -r "loadUrl\|loadData\|evaluateJavascript" output_dir/
# File operations (path traversal risk)
grep -r "openFileInput\|FileInputStream\|new File(" output_dir/
# SSL/TLS pinning implementation (helps plan bypass)
grep -r "CertificatePinner\|TrustManager\|checkServerTrusted\|X509TrustManager" output_dir/
grep -r "hostnameVerifier\|ALLOW_ALL_HOSTNAME" output_dir/
# SQL queries (local SQLi)
grep -r "rawQuery\|execSQL\|compileStatement" output_dir/
# SharedPreferences (data storage)
grep -r "getSharedPreferences\|putString\|putInt" output_dir/
# Broadcast receivers and intents
grep -r "sendBroadcast\|onReceive\|registerReceiver" output_dir/
MobSF โ Mobile Security Framework
Setup
# Docker (recommended)
docker pull opensecurity/mobile-security-framework-mobsf:latest
docker run -it --rm \
-p 8000:8000 \
-v /path/to/mobsf_data:/home/mobsf/.MobSF \
opensecurity/mobile-security-framework-mobsf:latest
# Access: http://localhost:8000
# Local installation (Python)
git clone https://github.com/MobSF/Mobile-Security-Framework-MobSF
cd Mobile-Security-Framework-MobSF
./setup.sh # Linux
python manage.py runserver 0.0.0.0:8000
Static Analysis Workflow
1. Upload APK (drag-and-drop or REST API)
2. Wait for analysis (~60 seconds)
3. Review each section:
SECURITY SCORE
โ Overall risk percentage
ANDROID MANIFEST
โ Exported components (Activities, Services, Receivers, Providers)
โ Dangerous permissions
โ Backup settings, debuggable flag
SECURITY ANALYSIS
โ Code analysis findings (severity: high/warning/info/secure)
โ Each finding shows file + line number + code snippet
โ Critical patterns: hardcoded keys, insecure crypto, SQL injection, path traversal
FILE ANALYSIS
โ Interesting files detected in APK
โ Certificates, config files, SQLite databases embedded in APK
STRINGS ANALYSIS
โ All strings extracted from binary
โ Filter for: URLs, keys, tokens, emails, base64 blobs
NETWORK SECURITY
โ Network security config analysis
โ Cleartext traffic permissions
โ Domain-specific configurations
PERMISSIONS
โ Dangerous permissions mapped to what they access
โ Overprivileged app detection
CERTIFICATE ANALYSIS
โ Signing certificate details
โ Debug vs release certificate detection
REST API for Automation
# Upload and scan via API
APIKEY="your_api_key" # Set in MobSF config
# Upload APK
curl -F "file=@app.apk" http://localhost:8000/api/v1/upload \
-H "Authorization: $APIKEY"
# Returns: {"scan_type": "apk", "hash": "abc123..."}
# Start scan
curl -X POST http://localhost:8000/api/v1/scan \
-d "scan_type=apk&hash=abc123..." \
-H "Authorization: $APIKEY"
# Get JSON report
curl "http://localhost:8000/api/v1/report_json?hash=abc123..." \
-H "Authorization: $APIKEY" > report.json
# Download PDF report
curl "http://localhost:8000/api/v1/download_pdf?hash=abc123..." \
-H "Authorization: $APIKEY" > report.pdf
Frida โ Dynamic Instrumentation
Setup
# Install frida-tools on attacker machine
pip install frida-tools
# Find matching frida-server version
frida --version # Note the version number
# Download frida-server for device architecture
# https://github.com/frida/frida/releases
# Choose: frida-server-{version}-android-{arch}.xz
# Common archs: arm64 (modern phones), x86_64 (emulators)
# Push and start frida-server
adb push frida-server-16.x.x-android-arm64 /data/local/tmp/frida-server
adb shell chmod 755 /data/local/tmp/frida-server
adb shell "/data/local/tmp/frida-server &"
# Verify
frida-ps -U # List processes on USB device
frida-ps -U | grep com.example # Find target app
Core Usage
# Attach to running process
frida -U -n "com.example.app" -l hook_script.js
# Spawn and attach (app starts fresh)
frida -U -f com.example.app -l hook_script.js --no-pause
# Interactive REPL (no script)
frida -U -n "com.example.app"
# Run script and exit
frida -U -f com.example.app -l script.js --no-pause -q -e "run()"
# Detach after timeout
timeout 60 frida -U -f com.example.app -l script.js --no-pause
Essential Frida Scripts
// === Hook a specific Java method ===
Java.perform(function() {
var TargetClass = Java.use("com.example.app.AuthManager");
TargetClass.login.implementation = function(username, password) {
console.log("[*] login() called");
console.log("[*] Username: " + username);
console.log("[*] Password: " + password);
// Call original method
var result = this.login(username, password);
console.log("[*] Return value: " + result);
return result;
};
});
// === Hook overloaded method ===
Java.perform(function() {
var Crypto = Java.use("com.example.app.CryptoUtil");
// Hook specific overload
Crypto.decrypt.overload("java.lang.String", "java.lang.String")
.implementation = function(data, key) {
var result = this.decrypt(data, key);
console.log("[Decrypt] Input: " + data);
console.log("[Decrypt] Key: " + key);
console.log("[Decrypt] Output: " + result);
return result;
};
});
// === Watch all methods in a class ===
Java.perform(function() {
var className = "com.example.app.NetworkManager";
var TargetClass = Java.use(className);
// Get all method names
var methods = TargetClass.class.getDeclaredMethods();
methods.forEach(function(method) {
var methodName = method.getName();
try {
TargetClass[methodName].overloads.forEach(function(overload) {
overload.implementation = function() {
console.log("[*] " + className + "." + methodName + "() called");
return overload.apply(this, arguments);
};
});
} catch(e) {}
});
});
// === Monitor SharedPreferences ===
Java.perform(function() {
var SharedPrefsImpl = Java.use("android.app.SharedPreferencesImpl");
SharedPrefsImpl.getString.implementation = function(key, defValue) {
var result = this.getString(key, defValue);
if (result !== null) {
console.log("[SharedPrefs] getString(" + key + ") = " + result);
}
return result;
};
// Also hook Editor.putString to catch writes
var EditorImpl = Java.use("android.app.SharedPreferencesImpl$EditorImpl");
EditorImpl.putString.implementation = function(key, value) {
console.log("[SharedPrefs] putString(" + key + ", " + value + ")");
return this.putString(key, value);
};
});
// === Hook SQLite ===
Java.perform(function() {
var SQLiteDatabase = Java.use("android.database.sqlite.SQLiteDatabase");
SQLiteDatabase.rawQuery.implementation = function(sql, selectionArgs) {
console.log("[SQLite] Query: " + sql);
if (selectionArgs !== null) {
console.log("[SQLite] Args: " + selectionArgs.join(", "));
}
return this.rawQuery(sql, selectionArgs);
};
SQLiteDatabase.execSQL.overload("java.lang.String").implementation = function(sql) {
console.log("[SQLite] execSQL: " + sql);
return this.execSQL(sql);
};
});
// === Monitor HTTP requests (OkHttp) ===
Java.perform(function() {
var OkHttpClient = Java.use("okhttp3.OkHttpClient");
var RealCall = Java.use("okhttp3.internal.connection.RealCall");
RealCall.execute.implementation = function() {
var request = this.request();
console.log("[OkHttp] URL: " + request.url());
console.log("[OkHttp] Method: " + request.method());
var headers = request.headers();
for (var i = 0; i < headers.size(); i++) {
console.log("[OkHttp] Header: " + headers.name(i) + ": " + headers.value(i));
}
return this.execute();
};
});
SSL Pinning Bypass Scripts
// === Universal Android SSL Pinning Bypass ===
// Hooks TrustManager, OkHttp, Conscrypt, Apache HTTP, WebViewClient
Java.perform(function() {
// Bypass 1: TrustManager (most common)
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var SSLContext = Java.use("javax.net.ssl.SSLContext");
var TrustManager = Java.registerClass({
name: "com.frida.TrustManager",
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) { },
checkServerTrusted: function(chain, authType) { },
getAcceptedIssuers: function() { return []; }
}
});
var TrustManagers = [TrustManager.$new()];
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, TrustManagers, null);
// Bypass 2: OkHttp3 CertificatePinner
try {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload("java.lang.String", "java.util.List")
.implementation = function(hostname, peerCertificates) {
console.log("[Bypass] OkHttp pinning bypassed for: " + hostname);
return;
};
} catch(e) {}
// Bypass 3: HostnameVerifier
try {
var OkHostnameVerifier = Java.use("okhttp3.internal.tls.OkHostnameVerifier");
OkHostnameVerifier.verify.overload("java.lang.String", "javax.net.ssl.SSLSession")
.implementation = function(hostname, session) {
return true;
};
} catch(e) {}
console.log("[*] SSL Pinning bypass active");
});
Objection โ Automated Frida Framework
# Install
pip install objection
# Launch with target app
objection -g com.example.app explore
# Launch with frida gadget (for non-jailbroken/no root)
objection -g gadget explore
Objection Command Reference
# === App Information ===
android info list # App metadata, paths
android info packages # All installed packages
ios info list # iOS equivalent
# === File System ===
android filesystem list # List app file system
android filesystem download /data/data/com.example/databases/app.db
android filesystem upload ./payload /data/local/tmp/payload
# === Class and Method Hooking ===
android hooking list classes # All loaded classes
android hooking search classes auth # Search class names
android hooking list methods com.example.AuthManager # Methods in class
android hooking watch class com.example.Crypto # Hook all methods
android hooking watch method com.example.Auth.checkPin --dump-args --dump-return
# === Memory ===
memory list modules # Loaded libraries
memory list exports libssl.so # Exports from library
memory dump all /tmp/dump.bin # Full memory dump
memory search "password" utf8 # Search for string in memory
memory write ... # Write to memory address
# === Environment ===
env # App environment variables and paths
# === Keystore ===
android keystore list # List Android KeyStore contents
# === SQLite ===
android sqlite list databases
android sqlite connect /data/data/com.example/databases/app.db
android sqlite execute "SELECT * FROM users"
# === Security Bypasses ===
android root disable # Bypass root detection hooks
android root simulate # Simulate non-root environment
android sslpinning disable # Bypass SSL certificate pinning
# === iOS-Specific ===
ios keychain dump # Dump iOS Keychain items
ios pasteboard monitor # Monitor clipboard
ios sslpinning disable # Bypass iOS SSL pinning
ios hooking list classes
ios hooking watch class SomeClass
ios hooking watch method '-[SomeClass someMethod]' --dump-args --dump-return
# === UI Interaction ===
android ui screenshot /tmp/screen.png
android ui alert "test"
class-dump (iOS)
Extracts Objective-C class headers from iOS binaries.
# Install
brew install class-dump # macOS
# or download binary from http://stevenygard.com/projects/class-dump/
# Extract headers from iOS binary
class-dump App.app/App > App_headers.h
# Organized output
class-dump -H App.app/App -o headers/
# Grep for interesting classes
grep -i "auth\|login\|password\|token\|crypt\|key" App_headers.h
# Grep for interesting methods
grep -i "checkPin\|verify\|authenticate\|encrypt\|decrypt" App_headers.h
Quick Reference: Setup Checklists
Android Assessment Setup
โก Device with USB debugging enabled
โก ADB connected: adb devices (shows device)
โก Frida server running: adb shell /data/local/tmp/frida-server &
โก frida-ps -U confirms target app process
โก Burp CA cert installed on device
โก Device proxy set to Burp listener
โก apktool installed: apktool --version
โก jadx installed: jadx --version
โก MobSF running: http://localhost:8000
โก objection installed: objection --version
iOS Assessment Setup (Jailbroken)
โก Jailbroken device (checkra1n/palera1n/unc0ver)
โก SSH accessible: ssh mobile@device_ip
โก Frida installed via Sileo/Cydia (build.frida.re repo)
โก frida-ps -U confirms device visible
โก Burp CA cert installed (Settings โ General โ VPN & Device Mgmt)
โก Device proxy configured
โก class-dump available
โก objection installed: pip install objection
โก SSL Kill Switch 2 installed (via Cydia)