Why static analysis
Before instrumenting an app with Frida, static analysis lets you map:
- Security implementations (SSL Pinning, root detection, encryption)
- Hardcoded secrets (API keys, tokens, internal endpoints)
- Authentication and authorization logic
- Native libraries requiring analysis with Ghidra
The JADX (Java/Kotlin code) + Ghidra (native C/C++ code) combination covers practically 100% of an APK's attack surface.
JADX — Decompiling Java and Kotlin code
Installation
# Via package manager (Linux)
sudo apt install jadx
# Or download the release at: https://github.com/skylot/jadx/releases
Opening the APK
jadx app.apk -d jadx_output/
jadx-gui app.apk # GUI (recommended for exploration)
Output structure
jadx_output/
├── sources/
│ └── com/example/app/
│ ├── MainActivity.java
│ ├── network/ApiClient.java
│ └── security/PinManager.java
└── resources/
├── AndroidManifest.xml
└── res/values/strings.xml
What to look for in decompiled code
1. Hardcoded secrets
grep -r "api_key\|apiKey\|secret\|password\|token\|Bearer" jadx_output/sources/ -i
grep -r "key\|secret\|endpoint" jadx_output/resources/ -i
2. SSL Pinning implementation
grep -r "CertificatePinner\|checkServerTrusted\|TrustManager\|X509" \
jadx_output/sources/ -l
// PinManager.java
new CertificatePinner.Builder()
.add("api.company.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build();
3. Root and Frida detection
grep -r "su\|superuser\|RootBeer\|frida\|/proc/maps\|magisk" \
jadx_output/sources/ -i -l
4. Cryptography — watch for hardcoded keys
// Vulnerable: hardcoded AES key
byte[] key = "1234567890abcdef".getBytes();
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
AndroidManifest.xml — Permissions and exposed components
cat jadx_output/resources/AndroidManifest.xml
What to check:
android:exported="true"on Activities, Services and BroadcastReceiversandroid:debuggable="true"— critical vulnerability in production- Excessive permissions without justification
Ghidra — Analyzing native libraries (.so)
Apps with native code compile to .so (ARM/ARM64 shared objects). JADX does not decompile C/C++ — this is where Ghidra comes in.
Installation
# Download at: https://ghidra-sre.org/ (requires Java 17+)
unzip ghidra_*.zip
./ghidra_*/ghidraRun
Extracting libs from APK
unzip app.apk "lib/*" -d extracted_libs/
# extracted_libs/lib/arm64-v8a/libnative.so
Ghidra analysis workflow
- New Project → Import File → select
libnative.so - Accept auto-analysis (~30s for large libs)
- In Symbol Tree →
Functionsto see exported functions
JNI function naming pattern
Java_com_example_app_SecurityManager_validateToken
Java_com_example_app_CryptoUtils_decrypt
Example: hardcoded AES key in native code
// Decompiled by Ghidra
void Java_com_example_CryptoUtils_decrypt(JNIEnv *env, jobject obj) {
char key[16] = {0x41, 0x42, 0x43, 0x44, ...}; // "ABCD..."
AES_set_decrypt_key(key, 128, &aes_key);
}
Use Search → For Strings to find URLs, keys and internal error messages.
Complete static analysis workflow
1. Extract APK
└── unzip app.apk / adb pull
2. JADX — Java/Kotlin code analysis
├── Hardcoded secrets and keys
├── SSL Pinning implementation (hosts and hashes)
├── Root/Frida detection (methods and libs used)
└── Exported components in Manifest
3. Ghidra — Native library analysis (.so)
├── Exported JNI functions
├── Cryptographic keys in memory
└── Validation logic in C/C++
4. Document findings for report
└── Mapping: attack surface → bypass via Frida
Complementary tools
| Tool | Use |
|------|-----|
| apkanalyzer | Android SDK CLI for quick inspection |
| MobSF | Automated static + dynamic analysis |
| objection | Frida wrapper with ready-to-use pentest commands |
| apksigner | Verifies APK signature and integrity |