Android Static Analysis
Overview
Android static analysis examines an APK without executing it — decompiling Dalvik bytecode to Java source, decoding resources and the manifest, and searching for hardcoded secrets, insecure configurations, and vulnerable code patterns. Static analysis is the first step in any Android assessment.
APK Structure
An APK is a ZIP archive containing:
| File/Directory | Purpose |
|---|---|
AndroidManifest.xml |
App permissions, components, SDK versions (binary XML) |
classes.dex |
Compiled Dalvik bytecode (app code) |
classes2.dex ... |
Additional DEX files (multidex apps) |
res/ |
Compiled resources (layouts, drawables) |
assets/ |
Raw assets bundled with the app |
lib/ |
Native shared libraries (.so files) per architecture |
META-INF/ |
Signing certificate and manifest digests |
resources.arsc |
Compiled resource table |
Decompilation with jadx
jadx decompiles APK/DEX files directly to Java source code.
# jadx
# https://github.com/skylot/jadx
# Decompile APK to Java source
jadx -d output_dir target.apk
# Decompile without resources (faster, source-only)
jadx -r -d output_dir target.apk
# Decompile without source code (resources only)
jadx -s -d output_dir target.apk
# Decompile a single class
jadx --single-class com.example.app.MainActivity -d output_dir target.apk
# Export as Gradle project (importable in Android Studio)
jadx -e -d output_dir target.apk
# Use multiple threads for large APKs
jadx -j 4 -d output_dir target.apk
# Show deobfuscation (rename short/obfuscated names)
jadx --deobf -d output_dir target.apk
# Use fallback mode for badly decompiled code
jadx -m fallback -d output_dir target.apk
After decompilation, the output directory contains readable Java source and decoded resources. Open in any text editor or IDE for review.
Decoding with apktool
apktool decodes the APK to smali (Dalvik assembly) and decoded XML resources. Unlike jadx, apktool can rebuild a modified APK.
# apktool
# https://github.com/iBotPeaches/Apktool
# Decode APK
apktool d target.apk -o decoded_dir
# Decode without source (smali)
apktool d -s target.apk -o decoded_dir
# Decode without resources
apktool d -r target.apk -o decoded_dir
# Force overwrite existing output directory
apktool d -f target.apk -o decoded_dir
# Rebuild modified APK
apktool b decoded_dir -o modified.apk
Signing a Rebuilt APK
Rebuilt APKs must be signed before installation:
# Generate a signing key
keytool -genkey -v -keystore test.keystore -alias testkey \
-keyalg RSA -keysize 2048 -validity 10000
# Sign the APK
jarsigner -verbose -sigalg SHA256withRSA -digestalg SHA-256 \
-keystore test.keystore modified.apk testkey
# Or use objection's built-in signing
# objection
# https://github.com/sensepost/objection
objection signapk modified.apk
DEX to JAR Conversion
# dex2jar
# https://github.com/pxb1988/dex2jar
# Convert DEX to JAR
d2j-dex2jar target.apk -o target.jar
# Force overwrite
d2j-dex2jar -f target.apk -o target.jar
The resulting JAR can be opened in JD-GUI or other Java decompilers. jadx is generally preferred over the dex2jar + JD-GUI workflow as it handles DEX directly with better results.
AndroidManifest.xml Analysis
The manifest reveals the app's attack surface. Key items to review:
Permissions
<!-- Dangerous permissions that the app requests -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Look for overly broad permissions — does the app really need camera, location, or storage access?
Exported Components
<!-- Exported activity — accessible from other apps -->
<activity android:name=".AdminActivity"
android:exported="true" />
<!-- Exported content provider — data accessible to other apps -->
<provider android:name=".DataProvider"
android:exported="true"
android:authorities="com.example.app.provider" />
<!-- Exported broadcast receiver -->
<receiver android:name=".DebugReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.example.DEBUG" />
</intent-filter>
</receiver>
Exported components (android:exported="true") are accessible from any other
app on the device and are a common source of vulnerabilities.
Security Flags
<!-- App-level flags -->
<application
android:debuggable="true" <!-- VULN: debug mode enabled -->
android:allowBackup="true" <!-- VULN: data backup allowed -->
android:usesCleartextTraffic="true" <!-- VULN: HTTP allowed -->
android:networkSecurityConfig="@xml/network_security_config">
debuggable="true"— allows attaching a debugger (should be false in production)allowBackup="true"— allowsadb backupto extract app datausesCleartextTraffic="true"— allows unencrypted HTTP connections
Target SDK and Minimum SDK
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
Lower minSdkVersion means the app must support older Android versions that
lack security features. targetSdkVersion determines which security behaviors
the OS enforces.
Quick Metadata with aapt
# aapt (Android Asset Packaging Tool)
# Included in Android SDK build-tools
# Dump package info (name, version, permissions, SDK versions)
aapt dump badging target.apk
# Dump permissions only
aapt dump permissions target.apk
# Dump the string pool
aapt dump strings target.apk
# Dump the XML tree of AndroidManifest.xml
aapt dump xmltree target.apk AndroidManifest.xml
androguard Analysis
# androguard
# https://github.com/androguard/androguard
# Print package name, version code, version name
androguard apkid target.apk
# Parse and display AndroidManifest.xml
androguard axml target.apk
# Decode resources.arsc
androguard arsc target.apk
# Show signing certificate fingerprints
androguard sign target.apk
# Interactive analysis (IPython shell)
androguard analyze target.apk
In the interactive shell, the a (APK), d (DEX), and dx (Analysis)
objects are available for programmatic inspection.
Searching for Secrets and Sensitive Data
After decompilation, search the source code for common sensitive patterns:
# Search for hardcoded URLs
grep -rn 'http://' output_dir/sources/
grep -rn 'https://' output_dir/sources/
# Search for API keys and tokens
grep -rni 'api.key\|apikey\|api_key\|secret\|token\|password' output_dir/sources/
# Search for AWS credentials
grep -rn 'AKIA[0-9A-Z]\{16\}' output_dir/sources/
# Search for Firebase URLs
grep -rn 'firebaseio\.com' output_dir/sources/
# Search for hardcoded IPs
grep -rn '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' output_dir/sources/
# Search in shared_prefs XML files (if extracted from device)
grep -rn 'password\|token\|secret' shared_prefs/
# Search for SQL queries (potential injection points)
grep -rni 'rawQuery\|execSQL' output_dir/sources/
Files to Check
| Location | What to Look For |
|---|---|
res/values/strings.xml |
Hardcoded strings, API endpoints |
res/xml/network_security_config.xml |
Certificate pinning config, cleartext settings |
assets/ |
Configuration files, databases, embedded credentials |
lib/ |
Native libraries (reverse with radare2/Ghidra) |
| Source code | Crypto implementations, authentication logic |
Network Security Configuration
Android 7+ (API 24) supports a declarative network security config:
<!-- res/xml/network_security_config.xml -->
<network-security-config>
<!-- Allow cleartext traffic (insecure) -->
<base-config cleartextTrafficPermitted="true" />
<!-- Trust user-installed CAs (allows proxy interception) -->
<base-config>
<trust-anchors>
<certificates src="system" />
<certificates src="user" />
</trust-anchors>
</base-config>
<!-- Certificate pinning -->
<domain-config>
<domain includeSubdomains="true">example.com</domain>
<pin-set>
<pin digest="SHA-256">base64_hash_here=</pin>
</pin-set>
</domain-config>
</network-security-config>
If the config includes <certificates src="user" />, user-installed proxy
certificates will be trusted and interception works without additional bypass.