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
# jadx-gui (graphical interface) or jadx (CLI)
Opening the APK
# CLI: exports to directory
jadx app.apk -d jadx_output/
# GUI (recommended for exploration)
jadx-gui app.apk
Output structure
jadx_output/
├── sources/
│ └── com/example/app/
│ ├── MainActivity.java # Activities
│ ├── network/ApiClient.java # HTTP client
│ └── security/PinManager.java
└── resources/
├── AndroidManifest.xml
└── res/values/strings.xml # App strings
What to look for in decompiled code
1. Hardcoded secrets
# Search for tokens, keys and passwords in the code
grep -r "api_key\|apiKey\|secret\|password\|token\|Bearer" jadx_output/sources/ -i
# Search in strings.xml and other resources
grep -r "key\|secret\|endpoint" jadx_output/resources/ -i
Common finding example:
// Found in ApiClient.java
private static final String API_KEY = "sk-live-xxxxxxxxxxx";
private static final String BASE_URL = "https://internal-api.company.com";
2. SSL Pinning implementation
grep -r "CertificatePinner\|checkServerTrusted\|TrustManager\|X509" \
jadx_output/sources/ -l
Example to identify which hosts have active pinning:
// 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
grep -r "AES\|DES\|RSA\|Cipher\|SecretKey\|KeyStore" \
jadx_output/sources/ -i
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 BroadcastReceivers — can be called by other appsandroid:debuggable="true"— enables debugging in production (critical vulnerability)- Excessive permissions (READ_CONTACTS, CAMERA, etc. without justification)
<!-- Exported component without permission — vulnerable to intent injection -->
<activity android:name=".DeepLinkActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="app" android:host="open"/>
</intent-filter>
</activity>
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
# APK is a ZIP — extract directly
unzip app.apk "lib/*" -d extracted_libs/
# Structure:
# extracted_libs/lib/
# ├── arm64-v8a/libnative.so
# └── armeabi-v7a/libnative.so
Ghidra analysis workflow
- New Project → Import File → select
libnative.so - Ghidra automatically detects ARM/ARM64
- Accept auto-analysis (~30s for large libs)
- In the Symbol Tree, go to
Functionsto see exported functions
Identifying JNI functions
Functions called by Java follow the pattern Java_package_Class_method:
Java_com_example_app_SecurityManager_validateToken
Java_com_example_app_CryptoUtils_decrypt
Reading decompiled code (Decompiler view)
Example of 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);
// ...
}
Searching for suspicious strings
In Ghidra: Search → For Strings — lists all strings present in the lib:
- Endpoint URLs
- Keys and tokens
- 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 |
References
Did you enjoy the content?
If you're building a system in this area, we can help. Talk to a specialist.
Schedule Consultation