Skip to main content
Synced from an Obsidian vault

For graph and advanced features, download the full Intel Codex Vault and open it in Obsidian.

Mobile Security (iOS & Android)

Purpose: Comprehensive guide to mobile application security testing, reverse engineering, and exploitation for iOS and Android platforms.


Table of Contents

  1. Overview
  2. Android Security Testing
  3. iOS Security Testing
  4. Common Mobile Vulnerabilities
  5. Network Analysis
  6. Dynamic Instrumentation
  7. Bypassing Security Controls
  8. Automated Testing
  9. 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/x64, faster)
  • Physical device: Rooted Android phone (Pixel, OnePlus recommended)
  • OS version: Test on multiple Android versions (8.0+)

iOS:

  • Simulator: Xcode Simulator (limited - no hardware features)
  • Physical device: Jailbroken iPhone (iOS 14-16)
  • OS version: Test on current and previous iOS versions

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

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!)

# Method 3: Extract from app store (requires tools)
# Use gplaycli or APKUpdater

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:

CheckSecurity 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 permissionsPrivacy 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)

# Generate keystore (one-time):
keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000

# Sign APK:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my.keystore app_modified.apk mykey

# Align APK (optimization):
zipalign -v 4 app_modified.apk 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@&lt;device_ip&gt; # Default password: alpine (change it!)

# Locate app:
find /var/containers/Bundle/Application/ -name "*.app"

# Copy IPA:
scp -r root@&lt;device_ip&gt;:/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 &lt;bundle_id&gt;
# Example: python3 dump.py com.example.app
# Output: Decrypted IPA saved to current directory

# Method 3: 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)

# Install class-dump:
# https://github.com/nygard/class-dump

# Dump Objective-C class headers:
class-dump Payload/App.app/App -H -o headers/

# Output: Objective-C headers (.h files)
# headers/
# ├── AppDelegate.h
# ├── LoginViewController.h
# └── APIManager.h

# 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 tools (varies by iOS version):
# - checkra1n (iOS 12-14.8.1, semi-tethered)
# - unc0ver (iOS 11-14.8, semi-untethered)
# - Taurine (iOS 14-14.3, semi-untethered)
# - Odyssey (iOS 13-13.7)

# After jailbreak, install essential tools via Cydia:
# - OpenSSH (remote access)
# - Frida (dynamic instrumentation)
# - SSL Kill Switch 2 (bypass SSL pinning)
# - AppSync Unified (install unsigned IPAs)

Step 2: SSH into device

# Install OpenSSH via Cydia

# Connect via SSH (default password: alpine):
ssh root@&lt;device_ip&gt;

# 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/&lt;UUID&gt;/

# 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/&lt;UUID&gt;/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@&lt;device_ip&gt;:/tmp/

# Run on device:
ssh root@&lt;device_ip&gt;
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@&lt;device_ip&gt;
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):

# Source code analysis tool (for developers)
# https://github.com/project-imas/

# Scans Objective-C/Swift source for:
# - Insecure data storage
# - Weak crypto
# - Insecure communication

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: &lt;computer_ip&gt;
Port: 8080

iOS:

Settings → Wi-Fi → Tap (i) next to network
→ HTTP Proxy: Manual
Server: &lt;computer_ip&gt;
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

# Install Frida server on Android device:
# Download frida-server for your architecture:
# https://github.com/frida/frida/releases
# Example: frida-server-16.0.0-android-arm64

adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"

# Install Frida on iOS (jailbroken):
# Add Frida repo in Cydia: https://build.frida.re
# Install: Frida

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 &lt;output_file&gt; # 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 Cydia/jailbreak apps:

if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]) {
// 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)
}

Bypass with Frida:

// iOS jailbreak detection bypass:
var fopen = new NativeFunction(Module.findExportByName(null, 'fopen'), 'pointer', ['pointer', 'pointer']);
Interceptor.replace(fopen, new NativeCallback(function(path, mode) {
var pathStr = Memory.readUtf8String(path);
if (pathStr.indexOf("Cydia") !== -1 || pathStr.indexOf("/bin/bash") !== -1) {
console.log("[+] Hiding jailbreak file: " + pathStr);
return ptr(0); // Pretend file doesn't exist
}
return fopen(path, mode);
}, 'pointer', ['pointer', 'pointer']));

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):

# Install Drozer:
# https://github.com/FSecureLABS/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

ToolPurposePlatform
JADXAPK decompiler (DEX to Java)Win/Linux/macOS
ApktoolAPK decoder/rebuilder (smali)Win/Linux/macOS
MobSFAutomated security testingWeb-based
FridaDynamic instrumentationWin/Linux/macOS
ObjectionFrida REPL for mobileWin/Linux/macOS
DrozerAndroid security assessmentWin/Linux/macOS
AndroBugsStatic analyzerLinux
QARKQuick security scannerLinux
adbAndroid Debug BridgeWin/Linux/macOS

iOS Tools

ToolPurposePlatform
frida-ios-dumpDecrypt App Store IPAsLinux/macOS
ClutchDecrypt IPAs (alternative)iOS (jailbroken)
class-dumpExtract Objective-C headersmacOS/Linux
HopperDisassembler/decompilermacOS/Linux
MobSFAutomated security testingWeb-based
FridaDynamic instrumentationWin/Linux/macOS
ObjectionFrida REPL for mobileWin/Linux/macOS
SSL Kill SwitchBypass SSL pinningiOS (Cydia)
Keychain-DumperExtract Keychain itemsiOS (jailbroken)
iMASSource code scannermacOS

Multi-Platform Tools

ToolPurposePlatform
Burp SuiteHTTPS interception/fuzzingWin/Linux/macOS
GhidraBinary analysis (ARM/x86)Win/Linux/macOS
IDA ProDisassembler (commercial)Win/Linux/macOS
FridaRuntime hookingWin/Linux/macOS
Radare2RE frameworkWin/Linux/macOS
WiresharkNetwork traffic analysisWin/Linux/macOS

Learning Resources

Documentation

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


Analysis:

Pentesting & Security: