
Android App Analysis
Static and dynamic analysis of Android applications, from pulling the APK off a device through decompiling Smali, inspecting the manifest, and dumping runtime state.
Getting the APK
If the app is in the Play Store, the lazy way is to install it on a device and pull it off:
adb shell pm list packages | grep target
adb shell pm path com.target.app
# package:/data/app/.../base.apk
adb pull /data/app/~~hash~~/com.target.app-xxx/base.apkIf the app ships split APKs (most do now), pull all of them:
adb shell pm path com.target.app
# package:/data/app/.../base.apk
# package:/data/app/.../split_config.arm64_v8a.apk
# package:/data/app/.../split_config.en.apk
# package:/data/app/.../split_config.xxhdpi.apk
# Pull each, or merge into a single APK with apkeditor or apkmirror toolsIf the app is not in the store, the client should provide a build. If they will not, that is itself a finding worth raising.
First Pass: The Manifest
The manifest tells you the attack surface in two minutes.
apktool d base.apk -o target/
cat target/AndroidManifest.xmlWhat I look for, in order:
android:debuggable="true", free game if presentandroid:allowBackup="true", lets youadb backupthe app dataandroid:networkSecurityConfig, points to the cleartext and pinning config- Exported components, anything with
android:exported="true"and no permission is reachable from any app on the device - Custom permissions, sometimes apps define a permission then forget to use it
- Deep links, look for
<intent-filter>with<data>elements - Custom URL schemes
Quick command for exported components:
grep -E 'android:exported="true"' target/AndroidManifest.xmlDecompiling
Two tools, two purposes.
apktool, for resources and Smali
apktool d base.apk -o target/
# target/smali/ - Smali assembly
# target/res/ - Resources
# target/AndroidManifest.xmlSmali is ugly to read but it is the ground truth. When jadx output is wrong (and it sometimes is), Smali is what is actually running.
jadx, for readable Java
jadx -d jadx-out base.apk
# or interactively
jadx-gui base.apkjadx-gui is what I open first. Search across the decompiled source, jump to definitions, watch the call graph build out.
Useful searches in jadx
OkHttpClientandCertificatePinner, find SSL pinningBuildConfig, hardcoded API URLs and keysgetSharedPreferences, local storage usageCipher.getInstance, crypto choicesLog.dandLog.e, what gets loggedIntent.parseUri, deep link handlers
Static Analysis Targets
Hardcoded secrets
# Search the unpacked resources and Smali
grep -r "api_key\|apiKey\|secret\|token\|password" target/
# Search strings in the original APK
strings base.apk | grep -i "api\|key\|secret"Cleartext traffic
Look for usesCleartextTraffic="true" in the manifest, or HTTP URLs in the strings.
Crypto
Search for DES, ECB, MD5, hardcoded IVs (new IvParameterSpec(new byte[]), and hardcoded keys.
Logging
Log.d, Log.v, Log.i, sometimes including tokens or PII. Production builds should strip these but rarely do.
Dynamic Analysis
Watch logcat
# Filter by the app's PID
adb shell pidof com.target.app
adb logcat --pid=$(adb shell pidof com.target.app)
# Or filter by tag
adb logcat -s TargetApp:VInspect storage
adb shell run-as com.target.app
ls -la /data/data/com.target.app/
# shared_prefs/ databases/ files/ cache/
cat shared_prefs/prefs.xml
sqlite3 databases/main.dbIf run-as does not work (release build), you need root.
Intercept traffic
Configure Burp or mitmproxy as the device proxy, push the CA into the system store (see Mobile Pentesting Fundamentals), and start the app. If pinning is in place, see SSL Pinning Bypass.
Trigger exported components
# Exported activity
adb shell am start -n com.target.app/.HiddenActivity
# Exported activity with extras
adb shell am start -n com.target.app/.WebViewActivity -e url "https://attacker.com"
# Exported service
adb shell am startservice -n com.target.app/.BackgroundService
# Broadcast receiver
adb shell am broadcast -a com.target.app.RECEIVE_DATA --es payload "evil"
# Content provider
adb shell content query --uri content://com.target.app.provider/usersModifying the App
When hooking is not enough and you need to actually patch the binary:
# Decompile
apktool d base.apk -o target/
# Edit Smali in target/smali/...
# Common edit: change `const/4 v0, 0x0` (false) to `const/4 v0, 0x1` (true)
# Rebuild
apktool b target/ -o patched.apk
# Sign
keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000
apksigner sign --ks my.keystore patched.apk
# Install
adb install -r patched.apkThis is heavy-handed. I prefer Frida hooks first, patching second.
Common Findings
| Finding | How I look for it |
|---|---|
| Hardcoded API keys | grep on BuildConfig and strings |
| Insecure data storage | shared_prefs, databases, file mode MODE_WORLD_READABLE |
| Exported activity bypasses login | List exported activities, launch them directly |
| Deep link XSS or open redirect | Find Intent.parseUri, test with crafted URIs |
| WebView with file access | Search for setAllowFileAccess(true) or setJavaScriptEnabled(true) |
| Insecure broadcast receivers | Exported receivers handling sensitive intents |
| Tapjacking | Activities without filterTouchesWhenObscured |
Related Notes
Last updated on
Mobile Application Security
Mobile application penetration testing for Android and iOS, covering static and dynamic analysis, runtime instrumentation with Frida and Objection, and bypassing common protections like SSL pinning and root detection.
Frida
Frida is the dynamic instrumentation toolkit I use on almost every mobile engagement. This is a working reference for installation, attaching to processes, writing hooks, and the patterns I reach for most.