Android APK Static Analysis — Full Methodology
Load this skill when the target is an Android APK (decompiled or not). This covers SAST — for dynamic testing (Frida, MITM), combine with playbook-webapp.
Task Decomposition — MANDATORY
An APK audit is NOT one grep job. Decompose into these independent tasks (run in parallel):
- API Security Audit — read ALL Retrofit/API interface files, find sensitive data in URLs
- Transport Security — network_security_config, certificate pinning, cleartext
- Auth Flow Analysis — full auth chain, session management, token handling
- Component Security — exported activities/receivers/providers/services, intent filters, deep links
- Build Hygiene — debug flags, dev tools in production, staging endpoints, logging
- Secrets & Crypto — hardcoded keys (but FILTER non-issues — see Known Non-Vulns below)
- Data Storage — SharedPreferences, Room/SQLite, file storage, backup flags
- Root/Tamper Detection — what's present AND what's MISSING
How to Read Decompiled Code
Smali basics (you'll see this from apktool/baksmali)
.method public someMethod(Ljava/lang/String;)V # method signature
invoke-virtual {v0}, Lcom/example/Foo;->bar()V # method call
const-string v1, "hardcoded_value" # string constant
sget-object v0, Lcom/example/Config;->API_KEY:Ljava/lang/String; # static field
Retrofit annotations in smali (THE most important pattern)
# @GET("endpoint") — HTTP method + path
.annotation runtime Lretrofit2/http/GET;
value = "auth/password"
.end annotation
# @Query("password") — parameter goes in URL query string!
.annotation runtime Lretrofit2/http/Query;
value = "password"
.end annotation
# @QueryMap — ALL params go in URL query string!
.annotation runtime Lretrofit2/http/QueryMap;
.end annotation
# @Body — parameter goes in request body (SAFE)
.annotation runtime Lretrofit2/http/Body;
.end annotation
# @Field — parameter goes in form body (SAFE)
.annotation runtime Lretrofit2/http/Field;
value = "password"
.end annotation
# @Header — parameter sent as HTTP header
.annotation runtime Lretrofit2/http/Header;
value = "Authorization"
.end annotation
CRITICAL CHECK: Any @Query/@QueryMap on a POST/PUT that carries sensitive data (password, PIN, OTP, token) = CRITICAL finding. Sensitive data in URL is logged everywhere (server access logs, proxies, CDNs, browser history, HTTP interceptors).
Jadx output (Java-like, easier to read)
@POST("auth/password")
Call<AuthResponse> login(@QueryMap Map<String, String> params);
// ^ This means login credentials go in URL query string = CRITICAL
Checklist: OWASP Mobile Top 10
M1: Improper Platform Usage
- Exported components without permission protection
- Deep links with sensitive actions (password reset, transfers)
- Content providers with
grant-uri-permissionsor no read/write permission - Intent filters on activities that handle sensitive data
-
android:debuggable="true"in manifest -
android:allowBackup="true"(data extractable via ADB) -
android:usesCleartextTraffic="true"
grep -r 'exported="true"' resources/AndroidManifest.xml
grep -r 'android:debuggable' resources/AndroidManifest.xml
grep -r 'allowBackup' resources/AndroidManifest.xml
M2: Insecure Data Storage
- Sensitive data in SharedPreferences (tokens, passwords, PII)
- Unencrypted SQLite/Room databases with sensitive data
- Sensitive data in external storage
- Logging of sensitive data (Log.d/Log.i with tokens/credentials)
# SharedPreferences usage
grep -rn "SharedPreferences\|getSharedPreferences\|PreferenceManager" sources/
# Logging sensitive data
grep -rn "Log\.\(d\|i\|v\|w\|e\)" sources/ | grep -i "token\|password\|secret\|key\|auth"
# External storage
grep -rn "getExternalStorage\|EXTERNAL_STORAGE\|Environment.getExternalStorageDirectory" sources/
M3: Insecure Communication
- Certificate pinning — check network_security_config.xml for
<pin-set>elements - Cleartext traffic —
cleartextTrafficPermittedshould be false - Custom TrustManager — accepting all certs (X509TrustManager with empty checkServerTrusted)
- HostnameVerifier — custom verifier that always returns true
- WebView —
setMixedContentMode(MIXED_CONTENT_ALWAYS_ALLOW)
# Certificate pinning
grep -r "pin-set\|CertificatePinner\|sha256/" resources/ sources/
# Trust all certs
grep -rn "X509TrustManager\|checkServerTrusted\|TrustAllCerts\|ALLOW_ALL" sources/
# Hostname verifier bypass
grep -rn "HostnameVerifier\|ALLOW_ALL_HOSTNAME_VERIFIER\|verify.*return true" sources/
M4: Insecure Authentication
- Auth credentials in URL query params (
@Query/@QueryMapon auth endpoints) - Biometric auth without server-side validation (local-only bypass)
- Hardcoded credentials (admin/password patterns in code)
- Token storage (where is the auth token kept? SharedPrefs? Encrypted?)
- Session timeout — is there an expiry? Auto-logout?
- PIN/pattern stored locally (hash vs plaintext)
# Find all API interface files (Retrofit)
find sources/ -name "*Api*" -o -name "*Service*" -o -name "*Endpoint*" | grep -i "\.java$\|\.smali$"
# Query params on auth
grep -A5 "@Query\|@QueryMap" sources/ | grep -B2 -i "password\|pin\|otp\|sms\|token\|secret"
M5: Insufficient Cryptography
- Hardcoded encryption keys/IVs
- ECB mode (patterns leak through encryption)
- MD5/SHA1 for security-critical operations
- Custom crypto implementations
- Weak key derivation (no PBKDF2/bcrypt/scrypt)
grep -rn "AES/ECB\|DES\|RC4\|Cipher.getInstance" sources/
grep -rn "MessageDigest.getInstance.*MD5\|MessageDigest.getInstance.*SHA-1" sources/
grep -rn "SecretKeySpec\|IvParameterSpec" sources/ | grep -i "hardcoded\|static\|final"
M6: Insecure Authorization
- Client-side authorization checks only (role checks in app, not server)
- IDOR — predictable/sequential IDs in API calls (transfer confirm by ID)
- Missing server-side validation on state transitions
# Find operations that use simple IDs
grep -rn '@Query("id")\|@Path("id")' sources/
M7: Client Code Quality
- Debug code in production (WebView debugging, Chucker, Stetho, LeakCanary, Flipper)
- Development/staging endpoints in production code
- Verbose error messages exposing internal details
- Test accounts or bypass codes
# Debug tools in production
grep -rn "setWebContentsDebuggingEnabled\|ChuckerInterceptor\|Stetho\|LeakCanary\|Flipper" sources/
# Dev/staging URLs
grep -rn "staging\|\.dev/\|localhost\|10\.0\.\|192\.168\.\|debug" sources/ resources/
M8: Code Tampering
- Root/jailbreak detection (RootBeer, custom checks for
su, Superuser.apk) - Frida/Xposed detection
- Emulator detection (how robust? Package name checks>
- Integrity verification (SafetyNet/Play Integrity, PairIP)
- Debugger detection (anti-debug techniques)
# Root detection
grep -rn "RootBeer\|isRooted\|/system/app/Superuser\|/system/xbin/su\|test-keys" sources/
# Frida detection
grep -rn "frida\|xposed\|substrate\|Magisk" sources/
# Emulator detection
grep -rn "generic\|goldfish\|sdk_gphone\|emulator\|genymotion\|bluestacks" sources/
M9: Reverse Engineering
- Obfuscation level (ProGuard/R8/DexGuard)
- Native code protection (if any)
- String encryption
M10: Extraneous Functionality
- Admin/debug endpoints accessible from client
- Hidden features triggered by config flags
- Logging/analytics sending sensitive data
Build Artifact Forensics
Dagger/Hilt DI Graph
Read the DI component files to find what's wired into the app:
# Find DI modules
find sources/ -name "*Module*" -o -name "*Component*" | grep -i "dagger\|hilt\|di\|inject"
# Look for interceptors in OkHttp chain
grep -rn "Interceptor\|addInterceptor\|addNetworkInterceptor" sources/
Chucker/Stetho/Flipper interceptors in the OkHttp chain = HIGH (logs all HTTP traffic).
Network Configuration Analysis
# Full network security config
cat resources/res/xml/network_security_config.xml
# Check for domain-config entries (dev/staging domains?)
grep -r "domain-config\|includeSubdomains\|cleartextTrafficPermitted" resources/
Permission Audit
Map each permission to its justification. Flag excessive:
READ_CONTACTS/READ_CALL_LOG— legitimate for banking?QUERY_ALL_PACKAGES— anti-emulator, but privacy concernRECORD_AUDIO— voice banking or unnecessary?ACCESS_FINE_LOCATION— branch locator or tracking?
API Surface Mapping — MANDATORY for Banking Apps
Read ALL Retrofit API interface files and build a complete endpoint map:
# Find all API interfaces
find sources/ -name "*Api*" | grep -v "test\|mock\|fake"
# Extract HTTP methods and paths
grep -rn "@GET\|@POST\|@PUT\|@PATCH\|@DELETE\|@HTTP" sources/
For each endpoint, note:
- HTTP method + path
- How params are passed (
@Query= URL,@Body= body,@Field= form) - Auth mechanism (header token? cookie?)
- Sensitive data exposure
Endpoints to focus on: auth, transfers, payments, password management, profile changes.
Known Non-Vulnerabilities (FALSE POSITIVES — do NOT report as findings)
These are NOT findings — downgrade to INFO or skip:
- Firebase/Google API keys in resources — required client-side, public by design
- Google Maps API key — public, restricted by package name
- reCAPTCHA site key — public by design (appears in HTML on every reCAPTCHA page)
- GCM/FCM sender ID, OneSignal app ID — public identifiers
- RSA/EC public keys for pinning/license/JWT verification — public by definition
- Package name, version code, build config — not secrets
- Obfuscated class names — obfuscation is defense, not a vuln
These BECOME findings only when:
- API key grants server-side write access (Firebase Storage write, Firestore admin) — TEST IT
- OneSignal REST API is unprotected (attempt notification send)
- Firebase DB/Storage has open rules (check
/.jsonand storage URL)
Impact Chaining — Connect Findings
After individual checks, chain findings for combined impact:
- No pinning + sensitive data in URL = MITM intercepts passwords in plaintext
- Chucker + passwords in URL = passwords logged in app's local DB
- No root detection + local token storage = trivial token extraction on rooted device
- Exported activity + deep link = intent hijacking to sensitive screens
- IDOR + valid session = unauthorized access to other users' data
Recording Findings
For each finding, call state_update add_vuln with:
severity: use the chained impact, not individualevidence: exact file:line or smali path + the relevant code/annotationstatus: "confirmed" for code-level findings (they're deterministic, no FP risk)description: what it is + WHY it matters + what an attacker can do
For absence findings (no pinning, no root detection): still record as vuln — the absence IS the finding. Evidence = "searched for X in all sources/resources, not found."