For graph and advanced features, download the full Intel Codex Vault and open it in Obsidian.
Mobile Security (iOS & Android)
Authorized environments only. Comprehensive guide to mobile application security testing, reverse engineering, and exploitation for iOS and Android platforms. Use against apps and devices you own or have written authorization to test. See Legal & Ethics.
Table of Contents
- Overview
- Pre-Engagement & Authorization
- Android Security Testing
- iOS Security Testing
- Common Mobile Vulnerabilities
- Network Analysis
- Dynamic Instrumentation
- Bypassing Security Controls
- Automated Testing
- Tools Reference
Overview
Mobile Security Testing Scope
Key areas:
- App reverse engineering: Decompile, analyze code, find vulnerabilities
- Runtime analysis: Hook functions, modify behavior, bypass protections
- Network security: HTTPS interception, API security, certificate pinning bypass
- Data storage: Analyze local databases, shared preferences, keychains
- Authentication/authorization: Session management, token security
- Cryptography: Weak algorithms, hardcoded keys, insecure random number generation
- Platform-specific: Android intents, iOS URL schemes, deep linking
Testing Environments
Android:
- Emulator: Android Studio AVD (x86_64/arm64, faster); Genymotion as alternative
- Physical device: Rooted Android phone (Pixel preferred for AOSP-clean Magisk/KernelSU support)
- OS version: Test on multiple Android versions; Android 8.0 minimum (system trust store CA enforcement, scoped storage shifts) [verify 2026-04-26]
iOS:
- Simulator: Xcode Simulator (limited - no Secure Enclave, no Keychain semantics, no jailbreak-detection surface)
- Physical device: Jailbroken iPhone — current public JBs land on iOS 15-17 via palera1n / Dopamine; iOS 18 public JB status [verify 2026-04-26]
- OS version: Test on current and previous iOS versions; arm64e (A12+) devices required to exercise PAC-aware code paths
Legal & Ethical Considerations
Authorized testing only:
- ✅ Test apps you developed or have permission to test
- ✅ Bug bounty programs (HackerOne, Bugcrowd)
- ✅ Penetration testing engagements with signed contracts
- ❌ Reverse engineering apps without authorization (may violate TOS)
- ❌ Circumventing DRM or software protection for piracy
Responsible disclosure:
- Report vulnerabilities to app developers/vendors
- Allow 90 days for remediation before public disclosure
- Use CVE process for significant vulnerabilities
Pre-Engagement & Authorization
Mobile testing inherits unique authorization wrinkles: rooting/jailbreaking is destructive and warranty-voiding, sideload/re-sign tooling can violate app-store ToS, and on-device data routinely contains real PII even on "test" accounts. The bullets below are pre-flight gates — do not skip them, and do not re-derive legal content here. See also the Legal & Ethical Considerations sub-block under Overview for the testing-only allow/deny list, and Legal & Ethics for the canonical jurisdictional framework.
Authorization Checklist
- Target app, device, and backend covered by signed engagement letter, bug-bounty / VDP scope, or written owner permission; app-store ToS implications of sideload, re-sign, and tampering tooling acknowledged in writing.
- Device root (Magisk, KernelSU) or jailbreak (checkra1n, palera1n, unc0ver, Dopamine) authorized on this device only — warranty-void and brick risk explicitly accepted by the asset owner.
- App-data extraction scope (Keychain, Keystore, shared prefs, app sandbox, WebView caches) and PII handling defined; on-device user data treated per Collection Log chain-of-custody and OPSEC artifact rules.
- Frida-server install, Objection injection, and IPA/APK re-signing scoped to the test device — not the analyst's daily driver or any device holding personal credentials.
- Network MITM authorization (Burp/MITMproxy CA in system trust store, SSL pinning bypass) scoped separately from production API interception; out-of-scope endpoints, third-party SDK telemetry, and shared backend tenants explicitly excluded.
- MDM bypass, biometric bypass, iOS arm64e PAC bypass, and Android FBE decryption are explicitly listed as in-scope or out-of-scope (default: out-of-scope unless the engagement letter says otherwise).
- Halt criteria, emergency contact, and disclosure pathway (vendor PSIRT, app-store security inbox, OWASP MASA assessor [verify 2026-04-26]) identified before any tooling touches the device.
Lab Environment Requirements
- Dedicated test devices matched to target OS version: jailbreak/root tooling is tightly version-pinned (checkra1n A7–A11 only, palera1n for arm64 iOS 15–17 [verify 2026-04-26], unc0ver legacy iOS 11–14, Dopamine for arm64 iOS 15–16.6.1 [verify 2026-04-26], Magisk + KernelSU per Android major version).
fridahost CLI version matched tofrida-serverarch+version on device (mismatch silently no-ops or crashes the agent); Objection ridden on top.- Separate Apple ID provisioned for iOS testing — never sign in with personal iCloud; disable Find My, iMessage, and iCloud Keychain on the test device.
- Android Studio AVD with system image matching target ABI (arm64-v8a vs x86_64) for automated dynamic runs; emulator proxy with Burp/MITMproxy CA installed into the system trust store (Android 7+ ignores user-store CAs for apps that don't opt-in via
network_security_config.xml). - MobSF instance for static triage running containerized on an isolated bridge — never expose the MobSF web UI to a routable network.
- Snapshot device/emulator state before each instrumentation campaign; revert after Frida hooks, re-signs, or kernel patches to keep evidence reproducible.
Disclosure-Ready Posture
Capture before you touch: app version + build identifier (aapt dump badging app.apk, apksigner verify --print-certs app.apk, codesign -dvvv App.app, otool -l App | grep -A2 LC_VERSION), acquisition provenance (store, vendor handoff, internal build URL), and device jailbreak/root state at test time. Version-pin every Frida script, Objection plugin, and re-sign cert (thumbprint + signing chain). Archive the decompiled APK/IPA, smali/Hopper exports, and the re-signed package alongside hashes per Collection Log. Stream device logs throughout (idevicesyslog or log collect --device on iOS; adb logcat -v threadtime on Android) so post-hoc reconstruction is unnecessary. Defang any URLs, package IDs, deep-link schemes, or endpoints that surface in the writeup (hxxp://, [.], [@]) and ship the package per Reporting, Packaging & Disclosure.
Android Security Testing
Android Application Structure
APK (Android Package) contents:
app.apk (ZIP file)
│
├── AndroidManifest.xml # App metadata, permissions, components
├── classes.dex # Dalvik bytecode (compiled Java/Kotlin)
├── resources.arsc # Compiled resources
├── res/ # Resources (images, layouts, strings)
│ ├── drawable/
│ ├── layout/
│ └── values/
├── lib/ # Native libraries (.so files)
│ ├── armeabi-v7a/
│ ├── arm64-v8a/
│ ├── x86/
│ └── x86_64/
├── assets/ # Raw asset files
├── META-INF/ # APK signature
│ ├── MANIFEST.MF
│ ├── CERT.RSA
│ └── CERT.SF
└── kotlin/ # Kotlin metadata (if Kotlin app)
Android Reverse Engineering
Step 1: Obtain APK
# Method 1: Download from device (requires adb)
adb shell pm list packages | grep <app_name>
# Output: package:com.example.app
adb shell pm path com.example.app
# Output: package:/data/app/com.example.app-xxx/base.apk
adb pull /data/app/com.example.app-xxx/base.apk app.apk
# Method 2: Download from APK mirror sites
# APKMirror, APKPure, APKMonk — verify integrity by re-checking
# the signing certificate fingerprint against vendor's known cert
# (`apksigner verify --print-certs app.apk`).
# Method 3: Extract from Play Store (requires tools)
# - gplaydl (Python; gplaycli successor) [verify 2026-04-26]
# - apkeep (Rust; supports Play, APKPure, F-Droid, Huawei AppGallery)
# - Aurora Store (anonymous Play client; not headless)
Step 2: Decompile APK with JADX
# Install JADX:
# https://github.com/skylot/jadx/releases
# Decompile APK to Java source:
jadx app.apk -d output_dir/
# Output structure:
# output_dir/
# ├── sources/ # Decompiled Java code
# │ └── com/example/app/
# │ ├── MainActivity.java
# │ ├── LoginActivity.java
# │ └── utils/
# ├── resources/ # Decoded resources
# └── AndroidManifest.xml
# Or use JADX-GUI:
jadx-gui app.apk
Step 3: Analyze decompiled code
// Example: LoginActivity.java (decompiled)
public class LoginActivity extends AppCompatActivity {
private static final String API_KEY = "sk_live_abc123xyz"; // Hardcoded API key!
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
public void loginUser(String username, String password) {
// Vulnerable: No input validation
String query = "SELECT * FROM users WHERE username='" + username + "'"; // SQL injection!
// Insecure: HTTP instead of HTTPS
String url = "http://api.example.com/login"; // Cleartext transmission!
// Weak crypto: MD5 for password hashing
String hashedPassword = MD5(password); // MD5 is broken!
}
}
Step 4: Decode with Apktool (for resources and smali)
# Install Apktool:
# https://ibotpeaches.github.io/Apktool/
# Decode APK:
apktool d app.apk -o app_decoded/
# Output structure:
# app_decoded/
# ├── AndroidManifest.xml # Decoded manifest (readable XML)
# ├── smali/ # Dalvik assembly code
# │ └── com/example/app/
# │ ├── MainActivity.smali
# │ └── LoginActivity.smali
# ├── res/ # Decoded resources
# │ ├── layout/
# │ ├── values/
# │ │ └── strings.xml # App strings
# └── original/ # Original files
# Analyze resources:
cat app_decoded/res/values/strings.xml
Step 5: Analyze AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app">
<!-- Permissions (check for excessive permissions) -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CONTACTS" /> <!-- Suspicious! -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Exported components (vulnerable if not protected) -->
<activity android:name=".MainActivity"
android:exported="true"> <!-- Exported = accessible by other apps! -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- Debuggable flag (should be false in production) -->
<application
android:debuggable="true" <!-- VULNERABLE! Allows debugging -->
android:allowBackup="true" <!-- Data can be backed up -->
android:networkSecurityConfig="@xml/network_security_config">
...
</application>
</manifest>
Key manifest checks:
| Check | Security Impact |
|---|---|
android:debuggable="true" | Allows attaching debugger, inspecting memory |
android:allowBackup="true" | Data can be extracted via adb backup |
android:exported="true" | Component accessible by other apps (intent hijacking) |
| Excessive permissions | Privacy violation, larger attack surface |
android:usesCleartextTraffic="true" | Allows HTTP traffic (MITM risk) |
Android Dynamic Analysis
Step 1: Set up testing device
# Enable Developer Options on Android device:
# Settings → About Phone → Tap "Build number" 7 times
# Enable USB debugging:
# Settings → Developer Options → USB debugging (ON)
# Connect device via USB:
adb devices
# Output: List of devices attached
# ABC123XYZ device
# Root device (if not rooted):
# Use Magisk: https://github.com/topjohnwu/Magisk
# Flash Magisk via custom recovery (TWRP)
Step 2: Install app
# Install APK:
adb install app.apk
# Or install from device:
adb shell pm install /sdcard/app.apk
# Verify installation:
adb shell pm list packages | grep com.example.app
Step 3: Explore app filesystem
# App data directory (requires root):
adb shell
su # Switch to root
cd /data/data/com.example.app/
# Directory structure:
# /data/data/com.example.app/
# ├── cache/ # Cached data
# ├── databases/ # SQLite databases
# │ └── app.db
# ├── shared_prefs/ # SharedPreferences (XML)
# │ └── settings.xml
# ├── files/ # App files
# └── lib/ # Native libraries
# Check for sensitive data:
cat shared_prefs/settings.xml
# Example:
# <string name="api_token">eyJhbGciOiJIUzI1...</string> # Stored token!
cat databases/app.db
# Or use sqlite3:
sqlite3 databases/app.db
sqlite> .tables
sqlite> SELECT * FROM users;
Step 4: Monitor logcat (logs)
# View real-time logs:
adb logcat | grep com.example.app
# Common log leaks:
# - API keys, tokens
# - Passwords (if logged during debugging)
# - Sensitive user data (PII)
# Example log:
# D/LoginActivity: Logging in user: john@example.com with password: password123
Step 5: Intercept network traffic (see Network Analysis section)
Android Static Analysis (Automated)
MobSF (Mobile Security Framework):
# Install MobSF (Docker):
docker pull opensecurity/mobile-security-framework-mobsf
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
# Access: http://localhost:8000
# Upload APK → Automated scan:
# - Permissions analysis
# - Manifest security checks
# - Code analysis (hardcoded secrets, SQL injection, etc.)
# - Binary analysis
# - Report generation (PDF)
AndroBugs:
# Install AndroBugs:
git clone https://github.com/AndroBugs/AndroBugs_Framework.git
cd AndroBugs_Framework
# Run analysis:
python androbugs.py -f app.apk
# Output: Detailed report (vulnerabilities, security misconfigurations)
Android Native Library Analysis
Analyzing .so files (ARM/x86 native libraries):
# Extract native library from APK:
unzip app.apk "lib/arm64-v8a/*" -d lib_extracted/
# Analyze with Ghidra:
# File → Import File → lib_extracted/lib/arm64-v8a/libnative.so
# Analyze with ARM:LE:64:v8A processor
# Look for:
# - Hardcoded strings (encryption keys, API endpoints)
# - JNI functions (Java Native Interface bridges)
# - Crypto implementations (custom or weak crypto)
# - Anti-tampering checks
JNI (Java Native Interface) reversing:
// Java side (loads native library):
public class NativeLib {
static {
System.loadLibrary("native"); // Loads libnative.so
}
// Native method declaration:
public native String decrypt(String encrypted);
}
// Native side (C/C++ in libnative.so):
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_example_app_NativeLib_decrypt(JNIEnv *env, jobject thiz, jstring encrypted) {
const char *enc_str = (*env)->GetStringUTFChars(env, encrypted, 0);
// Hardcoded AES key (vulnerable!):
char aes_key[] = "0123456789ABCDEF0123456789ABCDEF";
// Decrypt with AES...
char *decrypted = aes_decrypt(enc_str, aes_key);
return (*env)->NewStringUTF(env, decrypted);
}
Modifying & Repackaging APKs
Step 1: Decompile with Apktool
apktool d app.apk -o app_decoded/
Step 2: Modify smali code
# Edit app_decoded/smali/com/example/app/LoginActivity.smali
# Original (license check):
.method public checkLicense()Z
# ... license validation code ...
const/4 v0, 0x0 # Return false (invalid)
return v0
.end method
# Modified (bypass license check):
.method public checkLicense()Z
const/4 v0, 0x1 # Always return true (valid)
return v0
.end method
Step 3: Rebuild APK
apktool b app_decoded/ -o app_modified.apk
Step 4: Sign APK (required for installation)
Modern Android (7.0+) requires APK Signature Scheme v2/v3/v4 — the legacy jarsigner v1 scheme alone is rejected on most installs. Use apksigner from build-tools, and align before signing (v2+ embeds the signature over the aligned file).
# Generate keystore (one-time):
keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000
# Align FIRST (required for v2/v3 signing):
zipalign -p -f -v 4 app_modified.apk app_aligned.apk
# Sign with apksigner (v1+v2+v3 by default, build-tools 24.0.3+):
apksigner sign --ks my.keystore --ks-key-alias mykey --out app_final.apk app_aligned.apk
# Verify signature scheme(s) attached:
apksigner verify --verbose --print-certs app_final.apk
# Install:
adb install app_final.apk
iOS Security Testing
iOS Application Structure
IPA (iOS App Store Package) contents:
app.ipa (ZIP file)
│
└── Payload/
└── App.app/
├── Info.plist # App metadata, bundle ID, permissions
├── App (Mach-O binary) # Main executable (ARM64)
├── embedded.mobileprovision # Provisioning profile
├── _CodeSignature/ # Code signature
├── Frameworks/ # Embedded frameworks
├── Assets.car # Compiled assets
└── Base.lproj/ # Localized resources
iOS Reverse Engineering
Step 1: Obtain IPA
# Method 1: Download from jailbroken device (requires Frida or SSH)
# SSH into device:
ssh root@<device_ip> # Default password: alpine (change it!)
# Locate app:
find /var/containers/Bundle/Application/ -name "*.app"
# Copy IPA:
scp -r root@<device_ip>:/var/containers/Bundle/Application/XXX/App.app ~/Desktop/
# Method 2: Decrypt from App Store (requires jailbroken device)
# Apps downloaded from App Store are encrypted (FairPlay DRM)
# Use frida-ios-dump or Clutch to decrypt:
# frida-ios-dump:
git clone https://github.com/AloneMonkey/frida-ios-dump.git
cd frida-ios-dump
pip3 install -r requirements.txt
# Configure device IP in dump.py
python3 dump.py <bundle_id>
# Example: python3 dump.py com.example.app
# Output: Decrypted IPA saved to current directory
# bagbak — modern frida-ios-dump alternative (Node, fewer config knobs):
# npm i -g bagbak && bagbak <bundle_id>
# r2flutch — frida-ios-dump variant focused on Flutter apps.
# Method 3: ipatool (download encrypted IPA from App Store with an Apple ID;
# purchase / "Get" must already be on the account; no jailbreak required):
# https://github.com/majd/ipatool
ipatool auth login -e <appleid_email>
ipatool download -b com.example.app -o ./app.ipa
# Resulting IPA is FairPlay-encrypted — still need a JB device + dumper to
# get plaintext Mach-O for static analysis.
# Method 4: Download from third-party sites (verify integrity!)
Step 2: Analyze with Hopper/Ghidra
# Extract IPA:
unzip app.ipa
# Analyze main binary:
# Payload/App.app/App (Mach-O ARM64 binary)
# Ghidra:
# File → Import File → Payload/App.app/App
# Analyze with AARCH64:LE:64:AppleSilicon processor
# Hopper (macOS/Linux):
hopper -e Payload/App.app/App
# IDA Pro (commercial):
ida64 Payload/App.app/App
Step 3: Class dump (Objective-C apps)
# class-dump (nygard/class-dump) — original, Objective-C runtime metadata only;
# upstream effectively unmaintained, fork ecosystem fragmented [verify 2026-04-26].
# Modern alternatives:
# - jtool2 — Mach-O swiss army knife (`jtool2 -d objc App` ≈ class-dump output)
# - Hopper / Ghidra / IDA — handle Objective-C metadata natively in disassembly
# - For Swift symbols: rely on Hopper/Ghidra/IDA Swift demangler (no class-dump
# equivalent — Swift uses different runtime metadata layout than Obj-C)
class-dump Payload/App.app/App -H -o headers/
# Example header:
# @interface LoginViewController : UIViewController
# - (void)loginWithUsername:(NSString *)username password:(NSString *)password;
# - (BOOL)validateLicense:(NSString *)license;
# @end
Step 4: Analyze Info.plist
# View Info.plist:
plutil -p Payload/App.app/Info.plist
# Or convert to XML:
plutil -convert xml1 Payload/App.app/Info.plist -o Info_readable.plist
# Key checks:
# - CFBundleIdentifier: Bundle ID
# - NSAppTransportSecurity: ATS settings (allows HTTP?)
# - UIRequiredDeviceCapabilities: Required capabilities
# - Entitlements: Permissions (location, camera, etc.)
Info.plist security checks:
<!-- Example Info.plist -->
<dict>
<!-- App Transport Security (ATS) disabled (VULNERABLE!) -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/> <!-- Allows HTTP connections! -->
</dict>
<!-- URL schemes (deep linking attack surface) -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string> <!-- myapp:// URL scheme -->
</array>
</dict>
</array>
</dict>
iOS Dynamic Analysis
Step 1: Jailbreak device
Jailbreak tooling is hardware- and iOS-version-pinned. Always confirm support matrix on the project's release page before flashing — the matrix below is a snapshot, verify against current upstream releases.
# Current public jailbreak tools (snapshot — [verify 2026-04-26]):
# - palera1n iOS 15-17 on A11 and below (tethered/semi-tethered);
# rootless mode for A12+ on iOS 15-17
# - Dopamine iOS 15-16.6.1 on A12+ (rootless, semi-untethered) [verify 2026-04-26]
# - checkra1n A7-A11 only (BootROM checkm8 exploit); last release pre-iOS 14;
# inactive but still works for in-scope hardware
# - unc0ver LEGACY — last support iOS 14.x, no modern updates
# - Taurine RETIRED (was iOS 14.0-14.3)
# - iOS 18 / arm64e A12+ on iOS 17.x late builds: public JB status [verify 2026-04-26]
# (research-only PAC bypasses exist, no consumer JB)
# Package managers on modern jailbreaks (Cydia is largely defunct on rootless):
# - Sileo (default on palera1n / Dopamine)
# - Zebra (alternative)
# - Cydia still ships on legacy checkra1n / unc0ver setups
# Essential tools to install post-JB:
# - OpenSSH (remote access; rootless path is /var/jb/usr/bin/ssh)
# - Frida + frida-server (dynamic instrumentation; server arch+major.minor must
# match host frida CLI exactly or hooks no-op/crash)
# - SSL Kill Switch 3 (modern fork of SSL Kill Switch 2 for rootless JBs)
# [verify 2026-04-26]
# - AppSync Unified (install unsigned IPAs)
# - Choicy / A-Bypass (per-app tweak injection control for JB-detection bypass)
iOS 16+ Lockdown Mode awareness: if the test device has Lockdown Mode enabled (Settings → Privacy & Security → Lockdown Mode), WebKit JIT is disabled, attachments/link-previews are stripped, FaceTime invitations from unknown senders blocked, and configuration profiles cannot be installed — turn it off on the test device or expect MITM-CA install and many WebView exploits to silently fail [verify 2026-04-26].
Step 2: SSH into device
# Install OpenSSH via Cydia
# Connect via SSH (default password: alpine):
ssh root@<device_ip>
# IMPORTANT: Change default password!
passwd # For root
passwd mobile # For mobile user
# Explore filesystem:
cd /var/mobile/Containers/Data/Application/
ls -la
# Each app has a UUID directory
# Find app by bundle ID:
find . -name "*.plist" -exec grep -l "com.example.app" {} \;
Step 3: Analyze app sandbox
# App data directory:
# /var/mobile/Containers/Data/Application/<UUID>/
# Documents/ # User documents
# Library/ # App data
# ├── Caches/ # Cached data
# ├── Preferences/ # User preferences (.plist files)
# └── Cookies/ # Cookies
# tmp/ # Temporary files
# Check for sensitive data:
cd /var/mobile/Containers/Data/Application/<UUID>/Library/Preferences/
# View .plist files:
plutil -p com.example.app.plist
# Example (leaked token):
# "api_token" => "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Step 4: Analyze Keychain (requires tool)
# Install Keychain-Dumper:
# https://github.com/ptoomey3/Keychain-Dumper
# Copy to device:
scp keychain_dumper root@<device_ip>:/tmp/
# Run on device:
ssh root@<device_ip>
cd /tmp
chmod +x keychain_dumper
./keychain_dumper
# Output: All keychain items accessible by current app
# Example:
# Generic Password
# Service: com.example.app
# Account: user@example.com
# Password: supersecret123
Step 5: Monitor system logs
# Install syslog on device (via Cydia)
# View logs:
ssh root@<device_ip>
tail -f /var/log/syslog | grep -i "app"
# Or use Xcode Devices (macOS):
# Xcode → Window → Devices and Simulators → Select device → Open Console
iOS Static Analysis (Automated)
MobSF (supports iOS):
# Upload IPA to MobSF (same as Android):
# http://localhost:8000
# Automated analysis:
# - Binary analysis (strings, symbols)
# - Info.plist checks
# - Code signing verification
# - Entitlements analysis
# - Hardcoded secrets detection
iMAS (iOS Mobile Application Security):
# https://github.com/project-imas/
# LEGACY — iMAS organisation has been inactive for years; treat as historical
# reference rather than current tooling [verify 2026-04-26].
# Modern equivalents:
# - Semgrep with mobile rulepack (multi-language SAST, actively maintained)
# - MobSF (also runs SAST passes on iOS source/IPA — see above)
# - r2 / Ghidra / IDA scripts for binary-side checks
Common Mobile Vulnerabilities
Insecure Data Storage
Android - SharedPreferences (plaintext):
<!-- /data/data/com.example.app/shared_prefs/settings.xml -->
<map>
<string name="username">john@example.com</string>
<string name="password">password123</string> <!-- PLAINTEXT! -->
<string name="api_token">sk_live_abc123</string>
</map>
Mitigation:
// Encrypt sensitive data with EncryptedSharedPreferences (Android):
import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);
SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
"secure_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
iOS - NSUserDefaults (plaintext):
// Vulnerable (plaintext storage):
[[NSUserDefaults standardUserDefaults] setObject:@"password123" forKey:@"password"];
Mitigation:
// Store sensitive data in Keychain:
#import <Security/Security.h>
// Save to Keychain:
NSDictionary *query = @{
(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
(__bridge id)kSecAttrService: @"com.example.app",
(__bridge id)kSecAttrAccount: @"user_token",
(__bridge id)kSecValueData: [@"secret_token" dataUsingEncoding:NSUTF8StringEncoding]
};
SecItemAdd((__bridge CFDictionaryRef)query, NULL);
Insecure Communication
Cleartext HTTP traffic:
// Vulnerable (Android):
URL url = new URL("http://api.example.com/login"); // HTTP!
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
Mitigation:
// Use HTTPS:
URL url = new URL("https://api.example.com/login");
// Android Network Security Config (res/xml/network_security_config.xml):
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false" /> <!-- Block HTTP -->
</network-security-config>
iOS - App Transport Security (ATS) bypass:
<!-- Vulnerable Info.plist (allows HTTP): -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/> <!-- BAD! -->
</dict>
Mitigation:
<!-- Enforce HTTPS only: -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
Hardcoded Secrets
API keys in code:
// Android (decompiled):
public class APIClient {
private static final String API_KEY = "sk_live_abc123xyz"; // Exposed!
private static final String SECRET = "mysecret";
}
Finding secrets:
# Android:
jadx app.apk
grep -r "api_key\|secret\|password\|token" output_dir/
# iOS:
strings Payload/App.app/App | grep -i "api\|key\|secret\|token"
Mitigation:
- Store secrets server-side (fetch at runtime)
- Use environment variables (build-time injection)
- Obfuscate strings (minimal protection, not secure)
Weak Cryptography
MD5/SHA1 for passwords:
// Vulnerable:
String hashedPassword = MD5(password); // MD5 is broken!
Weak encryption:
// Vulnerable (hardcoded key, ECB mode):
SecretKeySpec key = new SecretKeySpec("1234567890123456".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); // ECB mode!
Mitigation:
// Use bcrypt/Argon2 for passwords (server-side):
// For encryption, use AES-GCM with randomly generated keys:
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] iv = cipher.getIV(); // Save IV for decryption
byte[] encrypted = cipher.doFinal(plaintext.getBytes());
SQL Injection (Mobile)
Vulnerable SQLite query:
// Android:
String query = "SELECT * FROM users WHERE username='" + username + "'";
Cursor cursor = db.rawQuery(query, null); // SQL injection!
Exploitation:
Username: admin' OR '1'='1' --
Query becomes: SELECT * FROM users WHERE username='admin' OR '1'='1' --'
Result: Bypasses authentication
Mitigation:
// Use parameterized queries:
String query = "SELECT * FROM users WHERE username=?";
Cursor cursor = db.rawQuery(query, new String[]{username});
Intent Hijacking (Android)
Exported component without permission:
<!-- AndroidManifest.xml -->
<activity android:name=".PaymentActivity"
android:exported="true"> <!-- Accessible by other apps! -->
</activity>
Exploitation:
// Malicious app sends intent:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.example.app", "com.example.app.PaymentActivity"));
intent.putExtra("amount", 0.01); // Manipulate payment amount
intent.putExtra("recipient", "attacker@evil.com");
startActivity(intent);
Mitigation:
<!-- Require permission or set exported=false: -->
<activity android:name=".PaymentActivity"
android:exported="false"> <!-- Only accessible by app -->
</activity>
<!-- Or require custom permission: -->
<permission android:name="com.example.app.PAYMENT_ACCESS"
android:protectionLevel="signature" />
<activity android:name=".PaymentActivity"
android:permission="com.example.app.PAYMENT_ACCESS"
android:exported="true">
</activity>
Network Analysis
HTTPS Interception (MITM Proxy)
Setup Burp Suite for mobile:
Step 1: Configure proxy
# Burp Suite:
# Proxy → Options → Proxy Listeners
# Add: 0.0.0.0:8080 (all interfaces)
# Find computer IP:
ip addr show # Linux
ipconfig # Windows
Step 2: Configure device proxy
Android:
Settings → Wi-Fi → Long-press network → Modify network
→ Advanced → Proxy: Manual
Hostname: <computer_ip>
Port: 8080
iOS:
Settings → Wi-Fi → Tap (i) next to network
→ HTTP Proxy: Manual
Server: <computer_ip>
Port: 8080
Step 3: Install Burp CA certificate
Android:
# Export Burp CA cert:
# Burp → Proxy → Options → Import/Export CA certificate
# Export → Certificate in DER format → Save as burp.der
# Convert to PEM:
openssl x509 -inform DER -in burp.der -out burp.crt
# Get certificate hash (for Android 7+):
openssl x509 -inform PEM -subject_hash_old -in burp.crt | head -1
# Output: 9a5ba575 (example hash)
# Rename cert:
mv burp.crt 9a5ba575.0
# Push to device (requires root):
adb root
adb remount
adb push 9a5ba575.0 /system/etc/security/cacerts/
adb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0
adb reboot
iOS:
# Navigate to Burp CA cert URL on device:
# http://burp/cert
# Download burp.crt
# Install:
# Settings → General → Profile Downloaded → Install
# Settings → General → About → Certificate Trust Settings
# Enable full trust for Burp CA
Step 4: Intercept traffic
# Burp → Proxy → Intercept: ON
# Launch app, observe HTTP/HTTPS requests
# Example intercepted request:
POST /api/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{"username": "john", "password": "password123"}
# Modify and forward
Bypassing Certificate Pinning
Certificate pinning validates server certificate against known cert/public key.
Detection:
# Android: Search for keywords in decompiled code:
grep -r "CertificatePinner\|TrustManager\|PinningTrustManager" output_dir/
# iOS: Search in binary:
strings Payload/App.app/App | grep -i "pin\|certificate\|ssl"
Bypass methods:
1. Frida script (runtime hooking):
// Android - Bypass OkHttp CertificatePinner:
Java.perform(function() {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(str, list) {
console.log("[+] Bypassing SSL pinning for: " + str);
return; // Do nothing (skip pinning check)
};
});
// iOS - Bypass NSURLSession pinning:
Interceptor.attach(ObjC.classes.NSURLSession["- URLSession:didReceiveChallenge:completionHandler:"].implementation, {
onEnter: function(args) {
console.log("[+] Bypassing SSL pinning");
var completionHandler = new ObjC.Block(args[4]);
completionHandler.implementation = function(disposition, credential) {
// Accept any certificate
var NSURLSessionAuthChallengeDisposition = {
UseCredential: 0,
PerformDefaultHandling: 1,
CancelAuthenticationChallenge: 2,
RejectProtectionSpace: 3
};
return completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, credential);
};
}
});
2. Xposed/Magisk module (Android):
# Install TrustMeAlready (Magisk module):
# https://github.com/ViRb3/TrustMeAlready
# Or SSLUnpinning (Xposed module):
# Install via Xposed Installer app
# Reboot device
3. SSL Kill Switch (iOS):
# Install via Cydia:
# Search: SSL Kill Switch 2
# Install and reboot
# Toggle in Settings
4. Objection (automated):
# Install Objection:
pip3 install objection
# Patch APK to include Frida gadget:
objection patchapk -s app.apk
# Install patched APK:
adb install app_objection.apk
# Run Objection:
objection explore
# Disable SSL pinning:
android sslpinning disable
Dynamic Instrumentation
Frida Basics
Installation:
# Install Frida on computer:
pip3 install frida frida-tools # pin major.minor to match the frida-server you push
# Install Frida server on Android device:
# Download frida-server for your architecture and EXACT host CLI version:
# https://github.com/frida/frida/releases
# Filename pattern: frida-server-<version>-android-<arch> (e.g. -arm64, -arm, -x86_64)
# Mismatched server vs CLI = silent no-op or crashed agent.
adb push frida-server-<version>-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "su -c '/data/local/tmp/frida-server &'" # detached as root; nohup if persistence needed
# Install Frida on iOS (jailbroken):
# Sileo / Zebra → Sources → add https://build.frida.re → install "Frida"
# (legacy Cydia repo URL is the same; Cydia itself is largely retired on rootless JBs)
Listing processes:
# List running apps:
frida-ps -U # USB device
# Output:
# PID Name
# ---- --------
# 1234 com.example.app
Attaching to app:
# Spawn app with Frida:
frida -U -f com.example.app -l script.js
# Attach to running app:
frida -U com.example.app -l script.js
Frida Scripting Examples
1. Hooking Java methods (Android):
// Hook LoginActivity.loginUser() method
Java.perform(function() {
var LoginActivity = Java.use("com.example.app.LoginActivity");
LoginActivity.loginUser.implementation = function(username, password) {
console.log("[+] loginUser called!");
console.log(" Username: " + username);
console.log(" Password: " + password);
// Call original method:
var result = this.loginUser(username, password);
console.log(" Result: " + result);
return result;
};
});
2. Bypassing root detection (Android):
Java.perform(function() {
// Hook common root detection methods:
// Method 1: Check for su binary
var File = Java.use("java.io.File");
File.exists.implementation = function() {
var path = this.getAbsolutePath();
if (path.indexOf("su") !== -1) {
console.log("[+] Hiding su binary: " + path);
return false; // Pretend su doesn't exist
}
return this.exists();
};
// Method 2: Check for Magisk/SuperSU
var PackageManager = Java.use("android.app.ApplicationPackageManager");
PackageManager.getPackageInfo.overload('java.lang.String', 'int').implementation = function(packageName, flags) {
if (packageName === "com.topjohnwu.magisk" || packageName === "eu.chainfire.supersu") {
console.log("[+] Hiding root package: " + packageName);
throw Java.use("android.content.pm.PackageManager$NameNotFoundException").$new();
}
return this.getPackageInfo(packageName, flags);
};
});
3. Hooking Objective-C methods (iOS):
// Hook UIViewController viewDidLoad
if (ObjC.available) {
var UIViewController = ObjC.classes.UIViewController;
Interceptor.attach(UIViewController['- viewDidLoad'].implementation, {
onEnter: function(args) {
var controller = new ObjC.Object(args[0]);
console.log("[+] viewDidLoad called on: " + controller.$className);
}
});
// Hook specific class method:
var LoginVC = ObjC.classes.LoginViewController;
Interceptor.attach(LoginVC['- loginWithUsername:password:'].implementation, {
onEnter: function(args) {
var username = new ObjC.Object(args[2]).toString();
var password = new ObjC.Object(args[3]).toString();
console.log("[+] Login attempt:");
console.log(" Username: " + username);
console.log(" Password: " + password);
}
});
}
4. Dumping memory:
// Dump all loaded modules:
Process.enumerateModules({
onMatch: function(module) {
console.log("Module: " + module.name + " @ " + module.base);
},
onComplete: function() {}
});
// Dump memory region:
var baseAddr = Module.findBaseAddress("libnative.so");
var size = 0x1000; // 4KB
console.log(hexdump(baseAddr, { length: size }));
5. Tracing all function calls:
// Android - Trace all methods in a class:
Java.perform(function() {
var LoginActivity = Java.use("com.example.app.LoginActivity");
LoginActivity.$init.implementation = function() {
console.log("[+] LoginActivity constructor");
return this.$init();
};
var methods = LoginActivity.class.getDeclaredMethods();
methods.forEach(function(method) {
var methodName = method.getName();
if (LoginActivity[methodName]) {
LoginActivity[methodName].implementation = function() {
console.log("[+] Called: " + methodName);
return this[methodName].apply(this, arguments);
};
}
});
});
Objection (Frida REPL)
Objection provides interactive Frida REPL with common commands.
# Launch Objection:
objection -g com.example.app explore
# Common commands:
android hooking list classes # List all classes
android hooking search classes LoginActivity # Search classes
android hooking list class_methods com.example.app.LoginActivity # List methods
android hooking watch class com.example.app.LoginActivity # Hook all methods
android sslpinning disable # Bypass SSL pinning
android root disable # Bypass root detection
ios hooking list classes # List Objective-C classes
ios hooking search classes LoginViewController
ios hooking watch class LoginViewController
memory dump all <output_file> # Dump memory
Bypassing Security Controls
Root/Jailbreak Detection Bypass
Android root detection methods:
1. Check for su binary:
File suFile = new File("/system/xbin/su");
if (suFile.exists()) {
// Rooted!
}
Bypass with Frida (see Frida examples above)
2. Check for root apps (Magisk, SuperSU):
PackageManager pm = getPackageManager();
try {
pm.getPackageInfo("com.topjohnwu.magisk", 0);
// Magisk installed = rooted
} catch (PackageManager.NameNotFoundException e) {
// Not rooted
}
3. Check build tags:
String buildTags = Build.TAGS;
if (buildTags.contains("test-keys")) {
// Custom ROM = likely rooted
}
iOS jailbreak detection:
1. Check for jailbreak package managers / artefacts — modern rootless JBs (palera1n, Dopamine) install to /var/jb/... rather than /, so well-written checks scan multiple paths:
NSArray *jbPaths = @[
@"/Applications/Cydia.app", @"/Applications/Sileo.app", @"/Applications/Zebra.app",
@"/var/jb/", @"/var/jb/Applications/Sileo.app",
@"/private/var/lib/apt", @"/usr/sbin/sshd", @"/bin/bash",
@"/etc/apt", @"/usr/libexec/ssh-keysign"
];
for (NSString *p in jbPaths) {
if ([[NSFileManager defaultManager] fileExistsAtPath:p]) { /* jailbroken */ }
}
2. Check for write access to /private:
if ([[NSFileManager defaultManager] isWritableFileAtPath:@"/private"]) {
// Jailbroken
}
3. Fork() restriction:
if (fork() >= 0) {
// Jailbroken (sandboxed apps can't fork)
}
4. ptrace(PT_DENY_ATTACH) / sysctl debugger check — canonical iOS anti-debug primitive; often paired with JB detection in the same routine. Bypass with Frida hook on ptrace, sysctl, or by patching PT_DENY_ATTACH constant out of the binary:
#include <sys/ptrace.h>
ptrace(PT_DENY_ATTACH, 0, 0, 0); // Will SIGKILL on debugger attach
Bypass with Frida:
// iOS jailbreak detection bypass — covers legacy + rootless paths:
var jbMarkers = ["Cydia", "Sileo", "Zebra", "/bin/bash", "/etc/apt", "/var/jb"];
var fopen = new NativeFunction(Module.findExportByName(null, 'fopen'), 'pointer', ['pointer', 'pointer']);
Interceptor.replace(fopen, new NativeCallback(function(path, mode) {
var pathStr = Memory.readUtf8String(path);
for (var i = 0; i < jbMarkers.length; i++) {
if (pathStr.indexOf(jbMarkers[i]) !== -1) {
console.log("[+] Hiding jailbreak file: " + pathStr);
return ptr(0);
}
}
return fopen(path, mode);
}, 'pointer', ['pointer', 'pointer']));
// Pre-built bypass scripts: r2frida + Frida CodeShare (e.g. `ios-jailbreak-detection-bypass`,
// `objection`'s `ios jailbreak disable`) — preferred over hand-rolled hooks for breadth. [verify 2026-04-26]
Attestation APIs (Play Integrity / App Attest / DeviceCheck)
Modern apps increasingly defer integrity verdicts to platform-attested signals rather than rolling their own root/JB checks. Bypassing these requires either control of the server-side verifier, a valid legitimate-device attestation token relayed from a clean device, or a known weakness in how the app consumes the verdict.
| Platform | API | Notes |
|---|---|---|
| Android | Play Integrity API | Replaced SafetyNet Attestation; SafetyNet Attestation API decommissioned for new integrations in 2024 [verify 2026-04-26]. Returns device, app, and account integrity verdicts signed by Google. |
| Android | SafetyNet (legacy) | Deprecated/decommissioned — apps still calling it may degrade gracefully or hard-fail. |
| iOS | App Attest | iOS 14+; per-install hardware-backed key in Secure Enclave; assertions signed and verified server-side via Apple. |
| iOS | DeviceCheck | Older, lighter — 2 bits of per-device state. |
Common testing notes:
- Verify whether the app fails-closed (refuses to run on non-attested device) or fails-open (only blocks specific actions). Report fails-open as a finding.
- Server-side verdict consumption is where most real bugs live: missing nonce, replay window too long, accepting verdicts without checking package name / bundle ID, accepting expired tokens. Test with intercepted/replayed verdicts in Burp/Caido.
- Tools like PIBypass / Frida codeshare scripts target client-side verdict-handling functions [verify 2026-04-26]; they do not forge a server-acceptable verdict — they simulate "device is clean" to the app's local checker only.
Debugger Detection Bypass
Android debugger detection:
// Check if debugger attached:
if (Debug.isDebuggerConnected()) {
// Debugger detected
}
// Check debuggable flag:
if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// App is debuggable
}
iOS debugger detection:
// Check for debugger via sysctl:
#include <sys/sysctl.h>
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()};
struct kinfo_proc info;
size_t size = sizeof(info);
sysctl(mib, 4, &info, &size, NULL, 0);
if (info.kp_proc.p_flag & P_TRACED) {
// Debugger attached
}
Bypass:
- Patch binary to remove checks (static analysis)
- Hook detection functions with Frida (dynamic)
Emulator/Simulator Detection Bypass
Android emulator detection:
// Check for emulator properties:
String brand = Build.BRAND; // "generic" on emulators
String device = Build.DEVICE; // "generic" on emulators
String model = Build.MODEL; // "sdk" on emulators
// Check for Genymotion:
if (Build.PRODUCT.contains("vbox")) {
// Genymotion emulator
}
Bypass:
# Modify build.prop on emulator:
adb root
adb remount
adb shell
vi /system/build.prop
# Change:
# ro.build.product=generic -> ro.build.product=OnePlus7
# ro.product.model=sdk -> ro.product.model=OnePlus 7
reboot
Automated Testing
Mobile App Scanners
QARK (Quick Android Review Kit):
# Install QARK:
pip3 install qark
# Analyze APK:
qark --apk app.apk
# Output: Vulnerability report (manifest issues, code vulnerabilities, etc.)
MobSF (Mobile Security Framework):
# See Android/iOS Static Analysis sections for setup
# Features:
# - Automated vulnerability scanning
# - Code analysis (SAST)
# - Binary analysis
# - API testing (dynamic)
# - Report generation
Automated Fuzzing
Drozer (Android Intent fuzzing):
Drozer was effectively unmaintained for several years after MWR/F-Secure changes; current upstream is WithSecure Labs (WithSecureLabs/drozer). Still useful for IPC attack-surface enumeration but the Python 2 → 3 port history means flag and command parity drift between forks — verify against current README. [verify 2026-04-26]
# Install Drozer:
# https://github.com/WithSecureLabs/drozer
# Install agent APK on device:
adb install drozer-agent.apk
# Start drozer server on device (via agent app)
# Connect from computer:
drozer console connect
# List attack surface:
dz> run app.package.attacksurface com.example.app
# Output:
# Exported Activities: 3
# Exported Services: 1
# Exported Broadcast Receivers: 2
# Fuzz exported components:
dz> run app.activity.start --component com.example.app com.example.app.PaymentActivity --extra string amount "0.01"
Tools Reference
Android Tools
| Tool | Purpose | Platform |
|---|---|---|
| JADX | APK decompiler (DEX to Java) | Win/Linux/macOS |
| Apktool | APK decoder/rebuilder (smali) | Win/Linux/macOS |
| apksigner / zipalign | Modern v2/v3/v4 sign + align (build-tools) | Win/Linux/macOS |
| MobSF | Automated security testing (SAST + dynamic) | Web-based |
| Frida + frida-server | Dynamic instrumentation (CLI/server versions must match) | Win/Linux/macOS |
| Objection | Frida REPL for mobile | Win/Linux/macOS |
| r2frida | Radare2 ↔ Frida bridge | Win/Linux/macOS |
| Drozer (WithSecure fork) | Android IPC / attack-surface assessment | Win/Linux/macOS |
| apkeep / gplaydl | APK acquisition (Play, APKPure, F-Droid, AppGallery) | Linux/macOS |
| AndroBugs | Static analyzer (legacy, infrequent updates) [verify 2026-04-26] | Linux |
| QARK | Quick security scanner (legacy) [verify 2026-04-26] | Linux |
| adb | Android Debug Bridge | Win/Linux/macOS |
| Magisk / KernelSU | Root frameworks (KernelSU = kernel-level alternative) | Android device |
iOS Tools
| Tool | Purpose | Platform |
|---|---|---|
| frida-ios-dump / bagbak | Decrypt App Store IPAs (FairPlay → plaintext Mach-O) | Linux/macOS + JB device |
| ipatool | Download encrypted IPA from App Store via Apple ID | Linux/macOS |
| Clutch | Legacy IPA decryptor (largely superseded by frida-ios-dump/bagbak) | iOS (jailbroken) |
| class-dump | Extract Objective-C headers (legacy; Swift unsupported) | macOS/Linux |
| jtool2 | Mach-O analysis (codesign, entitlements, Obj-C metadata) | macOS/Linux |
| Hopper | Disassembler/decompiler | macOS/Linux |
| MobSF | Automated security testing | Web-based |
| Frida + frida-server | Dynamic instrumentation | Win/Linux/macOS |
| Objection | Frida REPL for mobile | Win/Linux/macOS |
| r2frida | Radare2 ↔ Frida bridge | Win/Linux/macOS |
| SSL Kill Switch 3 | Bypass SSL pinning (rootless-aware fork of SKS2) | iOS (Sileo/Zebra) |
| Keychain-Dumper | Extract Keychain items | iOS (jailbroken) |
| palera1n / Dopamine / checkra1n | Jailbreak (matrix in §iOS Security Testing) | macOS/Linux + iOS device |
libimobiledevice (idevicesyslog, ideviceinstaller, idevice_id) | Non-JB device interaction & log streaming | Linux/macOS |
Multi-Platform Tools
| Tool | Purpose | Platform |
|---|---|---|
| Burp Suite | HTTPS interception/fuzzing | Win/Linux/macOS |
| Ghidra | Binary analysis (ARM/x86) | Win/Linux/macOS |
| IDA Pro | Disassembler (commercial) | Win/Linux/macOS |
| Frida | Runtime hooking | Win/Linux/macOS |
| Radare2 | RE framework | Win/Linux/macOS |
| Wireshark | Network traffic analysis | Win/Linux/macOS |
Learning Resources
Documentation
- OWASP MASTG (Mobile Application Security Testing Guide): https://mas.owasp.org/MASTG/ — unified successor to MSTG + MASVS as separate projects (renamed/merged 2023). Use this as the canonical control reference.
- OWASP MASVS (Mobile Application Security Verification Standard): https://mas.owasp.org/MASVS/ — verification levels & control IDs cross-referenced from MASTG. Current major: v2.x [verify 2026-04-26].
- OWASP Mobile Top 10: https://owasp.org/www-project-mobile-top-10/ — last refresh status [verify 2026-04-26].
- Android Security Documentation: https://source.android.com/security
- Apple Platform Security Guide: https://support.apple.com/guide/security/ — current location of what was historically published as the iOS Security Guide PDF (PDF distribution discontinued).
Books
- Mobile Application Penetration Testing by Vijay Kumar
- Android Hacker's Handbook by Joshua J. Drake et al.
- iOS Hacker's Handbook by Charlie Miller et al.
- The Mobile Application Hacker's Handbook by Dominic Chell et al.
Online Courses
- Mobile Application Security and Penetration Testing (eLearnSecurity eMAPT)
- SANS SEC575: Mobile Device Security and Ethical Hacking
- Pentester Academy: Mobile Security
Practice Apps
- DVIA (Damn Vulnerable iOS App): https://github.com/prateek147/DVIA-v2
- InsecureBankv2 (Android): https://github.com/dineshshetty/Android-InsecureBankv2
- AndroGoat (Android): https://github.com/satishpatnayak/AndroGoat
- OWASP iGoat (iOS): https://github.com/OWASP/igoat
Related SOPs
Analysis:
- Reverse Engineering - Mobile binary analysis (APK/IPA reverse engineering)
- Cryptography Analysis - SSL pinning, certificate analysis, crypto failures
- Malware Analysis - Malicious mobile app analysis
Pentesting & Security:
- Web Application Security - Mobile API and backend testing
- Firmware Reverse Engineering - IoT and embedded mobile device analysis
- Vulnerability Research - Finding mobile platform vulnerabilities
- Bug Bounty Hunting - Responsible mobile app vulnerability disclosure
- Detection Evasion Testing - Bypassing mobile security controls
- Forensics Investigation - Mobile device forensics
Methodology & Authorization:
- Legal & Ethics - Pre-engagement authorization, jurisdictional CFAA/CMA/EU NIS2 framing
- OPSEC Plan - Test-device hygiene, separate Apple ID, isolated network
- Collection Log - APK/IPA evidence handling, hash discipline, chain-of-custody
- Reporting, Packaging & Disclosure - Vendor PSIRT routing, defanging, packaging
Version: 1.1 Last Updated: 2026-04-26 Review Frequency: Quarterly (mobile platform APIs, jailbreak tooling, and SSL-pinning bypass primitives churn quickly)