Android APK Malware Analysis
Full analysis workflow for suspicious Android APKs — from static manifest review through dynamic instrumentation and C2 extraction.
Quick Reference
# Decode APK resources and smali
apktool d <TARGET>.apk -o ./decoded/
# Decompile to Java source
jadx -d ./java_src/ <TARGET>.apk
# Quick permission audit
aapt dump permissions <TARGET>.apk
# Hash and signature check
sha256sum <TARGET>.apk
apksigner verify --print-certs <TARGET>.apk
# Automated scan
docker run -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload APK to http://localhost:8000
# List Frida processes on device
frida-ps -U
MITRE ATT&CK Mapping
| Technique | ID | How It Appears |
|---|---|---|
| Exploitation for Privilege Escalation | T1404 | Root exploits, DirtyCOW, Magisk abuse in APK payloads |
| Software Discovery | T1418 | Enumerating installed apps to detect security tools or targets |
| Input Capture | T1417 | Keyloggers, accessibility service abuse for credential capture |
| SMS/MMS Collection | T1636.004 | Banking trojans intercepting OTP SMS messages |
| Location Tracking | T1430 | Stalkerware GPS tracking via background services |
| Obfuscated Files | T1406 | ProGuard, DexGuard, string encryption, native packer |
| Application Layer Protocol | T1437 | C2 over HTTPS, Telegram bot API, Firebase messaging |
1. Static Analysis — APK Structure
Unpack and examine the APK structure and manifest.
# Decode APK (keeps smali + resources)
apktool d <TARGET>.apk -o ./apk_decoded/
# Examine AndroidManifest.xml for suspicious permissions
cat ./apk_decoded/AndroidManifest.xml | grep -iE 'permission|receiver|service|provider'
# High-risk permissions to flag:
# SEND_SMS, RECEIVE_SMS, READ_SMS — SMS interception (banking trojans)
# READ_CONTACTS, READ_CALL_LOG — data exfiltration
# RECORD_AUDIO, CAMERA — spyware
# ACCESSIBILITY_SERVICE — overlay attacks, keylogging
# BIND_DEVICE_ADMIN — device admin abuse (ransomware)
# INSTALL_PACKAGES — dropper / sideloader
# REQUEST_IGNORE_BATTERY_OPTIMIZATIONS — persistent background service
# Check for accessibility service (major red flag for credential stealers)
grep -r "android.accessibilityservice" ./apk_decoded/AndroidManifest.xml
# List receivers (broadcast receivers for SMS, boot, etc.)
grep -A5 "receiver" ./apk_decoded/AndroidManifest.xml
# Check for native libraries
ls -la ./apk_decoded/lib/*/
# Certificate analysis
apksigner verify --print-certs -v <TARGET>.apk
# Self-signed with debug key? → highly suspicious
# Check: keytool -printcert -jarfile <TARGET>.apk
2. Decompile to Java
Recover readable source from DEX bytecode.
# jadx — best-in-class Android decompiler
jadx -d ./java_src/ --show-bad-code <TARGET>.apk
# --show-bad-code includes methods that failed to decompile cleanly
# Search for key behaviors
grep -rn "SmsManager\|sendTextMessage\|getDefault()" ./java_src/
grep -rn "HttpURLConnection\|OkHttpClient\|Retrofit\|Volley" ./java_src/
grep -rn "getDeviceId\|getSubscriberId\|getLine1Number\|IMEI" ./java_src/
grep -rn "getInstalledPackages\|getInstalledApplications" ./java_src/
grep -rn "TelephonyManager\|PackageManager" ./java_src/
grep -rn "Runtime\.exec\|ProcessBuilder\|\"su\"" ./java_src/
grep -rn "DexClassLoader\|PathClassLoader\|InMemoryDexClassLoader" ./java_src/
grep -rn "crypto\|Cipher\|SecretKey\|AES\|DES" ./java_src/
grep -rn "AccessibilityService\|onAccessibilityEvent" ./java_src/
# Extract hardcoded URLs / IPs / domains
grep -rnE 'https?://[^\s"]+|[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' ./java_src/
# Alternative: dex2jar + JD-GUI
d2j-dex2jar <TARGET>.apk -o <TARGET>.jar
# Open <TARGET>.jar in JD-GUI
3. Smali Analysis
For obfuscated samples where jadx fails, work at smali level.
# Smali is already extracted by apktool in step 1
# Navigate to entry points
find ./apk_decoded/smali* -name "*.smali" | head -20
# Find the main activity
grep -r "android.intent.action.MAIN" ./apk_decoded/AndroidManifest.xml
# Then read the corresponding smali file
# Search for reflection (used to hide API calls)
grep -rn "Ljava/lang/reflect" ./apk_decoded/smali*/
grep -rn "invoke-virtual.*Method;->invoke" ./apk_decoded/smali*/
# Search for dynamic class loading (dropper behavior)
grep -rn "DexClassLoader\|InMemoryDexClassLoader" ./apk_decoded/smali*/
# Search for crypto operations
grep -rn "Ljavax/crypto" ./apk_decoded/smali*/
# Search for command execution (root exploits)
grep -rn "Runtime;->exec\|\"su\"" ./apk_decoded/smali*/
# Decode obfuscated strings — common pattern: XOR or Base64 in static init
grep -rn "const-string\|sget-object" ./apk_decoded/smali*/ | grep -i "Base64\|decode"
4. Dynamic Analysis with Frida
Instrument the running APK to intercept behavior.
# Start Frida server on rooted device/emulator
adb push frida-server-<version>-android-<arch> /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server && /data/local/tmp/frida-server &"
# List running apps
frida-ps -Ua
# Hook encryption calls to capture plaintext
frida -U -l hook_crypto.js -f <PACKAGE_NAME> --no-pause
// hook_crypto.js — intercept javax.crypto.Cipher
Java.perform(function() {
var Cipher = Java.use("javax.crypto.Cipher");
Cipher.doFinal.overload("[B").implementation = function(input) {
console.log("[*] Cipher.doFinal called");
console.log(" Algorithm: " + this.getAlgorithm());
console.log(" Input: " + bytesToHex(input));
var result = this.doFinal(input);
console.log(" Output: " + bytesToHex(result));
return result;
};
// Hook URL connections for C2 traffic
var URL = Java.use("java.net.URL");
URL.$init.overload("java.lang.String").implementation = function(url) {
console.log("[*] URL: " + url);
return this.$init(url);
};
// Hook SMS sending
var SmsManager = Java.use("android.telephony.SmsManager");
SmsManager.sendTextMessage.implementation = function(dest, sc, text, sentPI, delPI) {
console.log("[!] SMS to: " + dest + " body: " + text);
// Don't actually send
// return this.sendTextMessage(dest, sc, text, sentPI, delPI);
};
});
function bytesToHex(bytes) {
var hex = [];
for (var i = 0; i < bytes.length; i++) {
hex.push(('0' + (bytes[i] & 0xFF).toString(16)).slice(-2));
}
return hex.join('');
}
# SSL pinning bypass for traffic interception
frida -U -l ssl_bypass.js -f <PACKAGE_NAME> --no-pause
# Then proxy through Burp/mitmproxy on port 8080
# Trace all JNI calls (native ↔ Java bridge)
frida-trace -U -i "JNI_*" -f <PACKAGE_NAME>
5. Network Traffic Analysis
# Set up transparent proxy on emulator
adb shell settings put global http_proxy <HOST_IP>:8080
# mitmproxy with Android CA installed
mitmproxy --mode transparent --listen-port 8080
# For certificate-pinned apps, use Frida SSL bypass first
# Then capture traffic in mitmproxy/Burp
# tcpdump on device (root required)
adb shell tcpdump -i any -w /sdcard/capture.pcap
adb pull /sdcard/capture.pcap
wireshark capture.pcap
# Analyze DNS queries
tshark -r capture.pcap -Y "dns.qr == 0" -T fields -e dns.qry.name | sort -u
6. Native Library Analysis
# List native libraries in APK
unzip -l <TARGET>.apk | grep "\.so$"
# Extract and analyze
unzip <TARGET>.apk lib/ -d ./native_libs/
# Identify architecture
file ./native_libs/lib/*/*.so
# Strings from native libs
strings -n 8 ./native_libs/lib/arm64-v8a/*.so | grep -iE 'http|socket|exec|system|decrypt|key|aes'
# Ghidra/IDA for full native RE
# Focus on: JNI_OnLoad, Java_<package>_<class>_<method> exports
readelf -s ./native_libs/lib/arm64-v8a/*.so | grep -i "java_\|jni_onload"
# Check for packer/protector in native code
# Bangcle, Qihoo 360, Baidu, Tencent packers wrap the real DEX
rabin2 -I ./native_libs/lib/arm64-v8a/*.so
7. Automated Analysis with MobSF
# Run MobSF
docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload via API
curl -F "file=@<TARGET>.apk" http://localhost:8000/api/v1/upload \
-H "Authorization: <API_KEY>"
# Get scan results
curl "http://localhost:8000/api/v1/scan" \
-X POST -d "scan_type=apk&file_name=<TARGET>.apk&hash=<FILE_HASH>" \
-H "Authorization: <API_KEY>"
# MobSF checks:
# - Manifest analysis (permissions, exported components)
# - Code analysis (hardcoded secrets, insecure APIs)
# - Binary analysis (native libs, packer detection)
# - CVSS scoring and compliance mapping
Tools & Resources
| Tool | Purpose | Install |
|---|---|---|
| apktool | APK decode/rebuild (smali + resources) | github.com/iBotPeaches/Apktool |
| jadx | DEX → Java decompiler | github.com/skylot/jadx |
| Frida | Dynamic instrumentation framework | frida.re / pip install frida-tools |
| MobSF | Automated mobile app security scanner | github.com/MobSF/Mobile-Security-Framework-MobSF |
| dex2jar | DEX → JAR converter | github.com/pxb1988/dex2jar |
| Objection | Frida-powered runtime exploration | github.com/sensepost/objection |
| APKiD | APK packer/compiler detector | github.com/rednaga/APKiD |
| mitmproxy | HTTPS traffic interception | mitmproxy.org |
| Drozer | Android security assessment framework | github.com/WithSecureLabs/drozer |
Detection Signatures
| Indicator | Description | Detection |
|---|---|---|
| SEND_SMS + RECEIVE_SMS + INTERNET | SMS interceptor (banking trojan) | Manifest permission analysis |
| BIND_ACCESSIBILITY_SERVICE | Overlay/keylogger credential stealer | Manifest + smali accessibility handler |
| DexClassLoader usage | Dynamic code loading / dropper | jadx/grep for DexClassLoader |
Encrypted assets in /assets/ |
Packed payload or hidden DEX | High entropy file in assets dir |
| Native JNI with obfuscated exports | Anti-analysis native packer | readelf -s + string analysis |
| Self-signed debug certificate | Non-Play-Store distribution | apksigner verify --print-certs |
| Root detection code (su, Magisk) | Anti-analysis / environment check | grep for "su", "magisk", "test-keys" |
Error Handling & Edge Cases
| Issue | Resolution |
|---|---|
| jadx fails on obfuscated DEX | Use --show-bad-code; fall back to smali analysis |
| Multi-DEX APK | apktool handles automatically; jadx merges all DEX files |
| Split APK (APKS/XAPK) | Extract base APK + split APKs; analyze each |
| Native packer (Bangcle/360/Tencent) | Dump unpacked DEX from memory with Frida: frida-dexdump |
| SSL pinning blocks proxy | Frida SSL bypass script; patch smali checkServerTrusted |
| Emulator detection in malware | Use physical rooted device; patch emulator checks via Frida |
| Dropper loads payload from C2 | Intercept download URL via Frida URL hook; fetch payload separately |
| React Native / Flutter / Xamarin | Different analysis: RN → index.android.bundle; Flutter → libapp.so; Xamarin → DLLs in assemblies/ |
Decision Gate
IF APK has suspicious permissions (SMS + Accessibility + Internet):
→ Full static analysis (manifest + jadx + strings)
→ Extract C2 URLs and hardcoded secrets
→ Dynamic analysis with Frida hooks on crypto/network/SMS
ELSE IF APK has native libraries with obfuscated exports:
→ Likely packed — dump real DEX with frida-dexdump
→ Analyze unpacked DEX with jadx
→ RE native lib with Ghidra for JNI logic
ELSE IF APK loads code dynamically (DexClassLoader):
→ Dropper — intercept payload download with Frida
→ Analyze second-stage DEX separately
ELSE:
→ Standard jadx decompilation
→ Behavioral analysis in MobSF
→ Network traffic capture for C2 identification