jadx (Dex to Java Decompiler) is a command-line and GUI tool, maintained by Skylot, that produces readable Java source code directly from Android Dalvik bytecode (.dex), APK, AAR, JAR, ZIP, AAB, and even smali files. Unlike Apktool — which disassembles bytecode into low-level Smali — jadx performs true decompilation, reconstructing high-level, near-original Java syntax (loops, if/else blocks, switch statements, and method structures) directly from the bytecode. This makes it dramatically faster and easier to read application logic compared to manually reading Smali.
jadx ships two components:
- jadx (CLI) – A command-line batch decompiler, ideal for scripting, automation, and quickly dumping an entire application’s source tree to disk.
- jadx-gui – An interactive graphical interface with syntax highlighting, “jump to declaration,” “find usages,” full-text search across the entire decompiled project, and an integrated Smali debugger for attaching to a running process.
Key features include decoding AndroidManifest.xml and other resources from the binary resources.arsc format, an included deobfuscator for renaming obfuscated class/method/field names to more readable forms, and support for exporting a decompiled APK as a ready-to-import Android Gradle project.
Important limitation: jadx cannot always decompile 100% of an application’s bytecode correctly — some methods, especially those affected by aggressive obfuscation, code shrinking (R8/ProGuard), or unusual compiler output, may show as “inconsistent code” or fail to decompile cleanly. This is expected and normal; cross-reference difficult sections with Apktool’s Smali output when needed.
How to Install
jadx is available in the Kali Linux repositories and can also be installed manually for the latest version.
Method 1 – APT (Kali Linux default repositories):
sudo apt update
sudo apt install jadx -y
Method 2 – Manual installation (latest release from GitHub):
# Ensure Java 11 or later (64-bit) is installed
sudo apt install default-jdk -y
java -version
# Download the latest release zip (check GitHub releases for current version)
wget https://github.com/skylot/jadx/releases/download/v1.5.0/jadx-1.5.0.zip
# Unpack
unzip jadx-1.5.0.zip -d jadx
sudo mv jadx /opt/jadx
# Create symlinks for easy CLI access
sudo ln -s /opt/jadx/bin/jadx /usr/local/bin/jadx
sudo ln -s /opt/jadx/bin/jadx-gui /usr/local/bin/jadx-gui
Method 3 – Arch/other distros with jadx packaged (for reference):
sudo pacman -S jadx # Arch Linux
brew install jadx # macOS via Homebrew
Verify installation:
$ jadx --version
1.5.0
Syntax
jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)
Where <input files> can be one or more paths, and [command] (currently plugins) is optional and used to manage jadx plugins.
All Command-Line Options
Output Options:
| Option | Description |
|---|---|
-d, --output-dir <DIR> | Output directory (both sources and resources) |
-ds, --output-dir-src <DIR> | Output directory for decompiled Java sources only |
-dr, --output-dir-res <DIR> | Output directory for decoded resources only |
-r, --no-res | Do not decode resources |
-s, --no-src | Do not decompile source code |
--single-class <NAME> | Decompile a single class (full name, raw or alias) |
--single-class-output <PATH> | File or directory to write when decompiling a single class |
--output-format {java|json} | Output format; default is java |
-e, --export-gradle | Save as an Android Gradle project |
--export-gradle-type {auto|android-app|android-library|simple-java} | Gradle project template used for export |
Processing Options:
| Option | Description |
|---|---|
-j, --threads-count <N> | Number of processing threads (default: 4, newer versions default 16) |
-m, --decompilation-mode {auto|restructure|simple|fallback} | Code output mode; auto tries the best options, restructure restores normal Java control flow, simple gives linear instructions with gotos, fallback gives raw unmodified instructions |
--show-bad-code | Show inconsistent code (incorrectly decompiled sections) instead of hiding them |
--no-imports | Disable use of imports, always output fully qualified names |
--no-debug-info | Disable debug info processing (line numbers, local variable names) |
--no-inline-anonymous | Disable inlining of anonymous classes |
--no-replace-consts | Disable replacement of static field values with their constants |
--escape-unicode | Escape non-ASCII (Unicode) characters in output |
--respect-bytecode-access-modifiers | Do not change methods/fields access modifiers to match original bytecode exactly |
--deobf | Activate the deobfuscator |
--deobf-min <N> | Minimum name length before renaming; default 3 |
--deobf-max <N> | Maximum name length before renaming; default 64 |
--deobf-whitelist <LIST> | Space-separated classes/packages excluded from deobfuscation |
--deobf-cfg-file <FILE> | Deobfuscation mappings file |
--rename-flags {case|valid|printable|none} | Controls what gets renamed for invalid identifiers |
--rename-mappings-file <FILE> | Load rename mappings from a mapping file |
--rename-mappings.format <FORMAT> | Mapping format: AUTO, TINY_FILE, TINY_2_FILE, ENIGMA_FILE, PROGUARD_FILE, SRG_FILE, and more |
--rename-mappings.invert {yes|no} | Invert the mapping on load |
--smali-input.api-level <N> | Android API level used for smali input; default 27 |
Logging and Miscellaneous Options:
| Option | Description |
|---|---|
-v, --verbose | Verbose output |
-q, --quiet | Suppress console output |
-h, --help | Print help information |
--version | Print the current jadx version |
--cfg | Save the method control flow graph (CFG) files |
--raw-cfg | Save the raw, unprocessed CFG files |
Environment Variables:
| Variable | Description |
|---|---|
JADX_DISABLE_XML_SECURITY | Set to true to disable security checks for XML files |
JADX_DISABLE_ZIP_SECURITY | Set to true to disable security checks for zip files |
JADX_ZIP_MAX_ENTRIES_COUNT | Maximum allowed number of entries in zip files (default 100,000) |
JADX_CONFIG_DIR | Custom configuration directory |
JADX_CACHE_DIR | Custom cache directory |
JADX_TMP_DIR | Custom temp directory |
Basic Usage (Expected Output in Bash)
Decompile an APK to a folder:
$ jadx -d out app.apk
INFO - loading ...
INFO - processing ...
INFO - done in 4200ms
INFO - processing classes finished in 3812ms
INFO - saving sources ...
INFO - save finished, elapsed time: 8123ms
Launch the GUI:
$ jadx-gui app.apk
(No terminal output; the GUI window opens showing the package tree, decompiled Java classes, and search box.)
Practical Examples with Output
Example 1 — Decompile an APK to the default folder name:
$ jadx InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - done in 3921ms
INFO - saving sources ...
INFO - save finished, elapsed time: 6532ms
$ ls
InsecureBankv2.apk InsecureBankv2/
Example 2 — Decompile to a custom output directory:
$ jadx -d insecurebank_src InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - done in 3701ms
INFO - saving sources ...
INFO - save finished, elapsed time: 6104ms
$ ls insecurebank_src
resources sources
Example 3 — Decompile without resources (source code only, faster):
$ jadx -r -d insecurebank_nores InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - done in 2210ms
INFO - saving sources ...
INFO - save finished, elapsed time: 3944ms
Example 4 — Decompile resources only (no Java source):
$ jadx -s -d insecurebank_resonly InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - done in 1890ms
INFO - saving resources ...
INFO - save finished, elapsed time: 2517ms
Example 5 — Decompile a single specific class:
$ jadx --single-class com.android.insecurebankv2.LoginActivity -d single_class_out InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - saving sources ...
INFO - save finished, elapsed time: 1120ms
$ cat single_class_out/sources/com/android/insecurebankv2/LoginActivity.java | head -5
package com.android.insecurebankv2;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
Example 6 — Increase processing threads for faster decompilation on multi-core systems:
$ jadx -j 8 -d fast_out InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - done in 1544ms
INFO - saving sources ...
INFO - save finished, elapsed time: 2988ms
Example 7 — Enable the deobfuscator on an obfuscated APK:
$ jadx --deobf -d deobf_out obfuscated_app.apk
INFO - loading ...
INFO - processing ...
INFO - renaming 1832 obfuscated identifiers ...
INFO - done in 5210ms
INFO - saving sources ...
INFO - save finished, elapsed time: 9012ms
Example 8 — Show inconsistent/bad code instead of hiding it:
$ jadx --show-bad-code -d debug_out InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
WARN - Method decompilation error: com.android.insecurebankv2.CryptoUtil.decrypt
INFO - done in 3765ms
INFO - saving sources ...
INFO - save finished, elapsed time: 6210ms
Example 9 — Export as an Android Gradle project:
$ jadx -e -d gradle_export InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - exporting as gradle project ...
INFO - done in 4102ms
$ ls gradle_export
app build.gradle settings.gradle
Example 10 — Search for hardcoded strings (e.g., API keys) in decompiled output:
$ jadx -d out InsecureBankv2.apk
$ grep -rn "AIzaSy" out/sources/
out/sources/com/android/insecurebankv2/Constants.java:14: public static final String API_KEY = "AIzaSyD4-example-hardcoded-key123";
Example 11 — Decompile in fallback mode for heavily obfuscated bytecode:
$ jadx -m fallback -d fallback_out packed_app.apk
INFO - loading ...
INFO - processing ...
INFO - decompiling using fallback mode
INFO - done in 2988ms
INFO - saving sources ...
INFO - save finished, elapsed time: 4501ms
Example 12 — Output in JSON format for programmatic parsing:
$ jadx --output-format json -d json_out InsecureBankv2.apk
INFO - loading ...
INFO - processing ...
INFO - saving sources (json format) ...
INFO - save finished, elapsed time: 5844ms
$ head -c 200 json_out/sources/com/android/insecurebankv2/LoginActivity.json
{"class":"com.android.insecurebankv2.LoginActivity","methods":[{"name":"onCreate","...
Common Use Cases
- Source code review for insecure logic – Reading reconstructed Java to identify insecure cryptography, hardcoded credentials, improper input validation, or unsafe WebView/Intent handling.
- Finding hardcoded secrets and endpoints – Using
grep/ripgrepacross the decompiledsources/tree to quickly locate API keys, backend URLs, or debug flags. - Understanding obfuscated malware samples – Applying
--deobfto make renamed classes/methods more approachable when triaging a malicious APK. - Preparing input for Frida/Objection hooking – Reading method signatures and class names from jadx output to construct accurate Frida hook scripts or Objection
android hooking watch class_methodcommands. - Cross-referencing with Apktool – Using jadx for readable logic understanding while using Apktool’s Smali output for the actual byte-level patching, since jadx output typically does not recompile back to a working APK.
Automation with Bash
The following script batch-decompiles every APK in a directory and searches the output for common secret patterns.
#!/bin/bash
# jadx_batch_secrets.sh - Batch decompile APKs and scan for hardcoded secrets
INPUT_DIR="./apks"
OUTPUT_DIR="./jadx_out"
PATTERNS=("api_key" "apikey" "secret" "password" "AKIA" "AIzaSy" "-----BEGIN")
mkdir -p "$OUTPUT_DIR"
for apk in "$INPUT_DIR"/*.apk; do
name=$(basename "$apk" .apk)
echo "[*] Decompiling $name..."
jadx -q -r -d "${OUTPUT_DIR}/${name}" "$apk"
echo "[*] Scanning $name for potential secrets..."
for pattern in "${PATTERNS[@]}"; do
matches=$(grep -rin "$pattern" "${OUTPUT_DIR}/${name}/sources" 2>/dev/null)
if [ -n "$matches" ]; then
echo " [!] Pattern '${pattern}' found:"
echo "$matches" | sed 's/^/ /'
fi
done
echo "----------------------------------------"
done
Sample run:
$ chmod +x jadx_batch_secrets.sh
$ ./jadx_batch_secrets.sh
[*] Decompiling InsecureBankv2...
[*] Scanning InsecureBankv2 for potential secrets...
[!] Pattern 'password' found:
sources/com/android/insecurebankv2/DoLogin.java:41: String hardcodedPassword = "Admin@123";
[!] Pattern 'AIzaSy' found:
sources/com/android/insecurebankv2/Constants.java:14: public static final String API_KEY = "AIzaSyD4-example-hardcoded-key123";
----------------------------------------
Tips and Best Practices
- Use
-r(no-res) whenever you only need source code — it noticeably speeds up decompilation, especially on larger APKs with many drawable/image resources. - Increase
-j(threads-count) on multi-core machines to significantly reduce decompilation time for large applications. - Always pair jadx output with Apktool’s decoded manifest — jadx’s manifest decoding is good, but Apktool’s resource decoding (styles, layouts) is generally more complete for UI-related review.
- When a method shows as
// jadx: was uncatched...or similar error comments, use--show-bad-codeto see the raw output, then cross-reference the equivalent Smali using Apktool for a byte-accurate view. - Use
--deobfcautiously on legitimate (non-malicious) apps that are ProGuard-obfuscated for release — it can make code much more readable, but renamed identifiers are heuristic guesses, not the original names. - Prefer jadx-gui for interactive exploration (jump-to-definition, find usages) and the
jadxCLI for batch/automated processing or CI pipelines. - Combine
--single-classwith automation to selectively re-decompile only classes of interest after making changes to deobfuscation mappings, saving time compared to a full re-run.
Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
OutOfMemoryError on large APKs | Default JVM heap too small | Increase heap manually: JAVA_OPTS="-Xmx4g" jadx -d out big_app.apk |
Some methods show /* JADX ERROR */ | Bytecode too complex/obfuscated to restructure cleanly | Use --show-bad-code or switch to -m fallback for raw instructions |
| GUI won’t launch, no error shown | Missing or incompatible Java version | Verify java -version shows Java 11+ 64-bit; install default-jdk if missing |
| Decompiled code doesn’t match app behavior exactly | Compiler optimizations, R8/ProGuard shrinking | Cross-check suspicious logic against Smali via Apktool for ground truth |
Zip entries count exceeds limit error | APK is a zip bomb or unusually large archive | Set JADX_ZIP_MAX_ENTRIES_COUNT environment variable higher if the file is legitimate |
jadx: command not found | Not installed or not in PATH | Run sudo apt install jadx or verify the symlink in /usr/local/bin |
| Decompilation extremely slow | Single-threaded default or very large multi-dex APK | Increase -j thread count; use -r to skip resources if not needed |
References
- jadx GitHub Repository:
https://github.com/skylot/jadx - jadx README (full CLI options):
https://github.com/skylot/jadx/blob/master/README.md - Kali Linux Tools – jadx:
https://www.kali.org/tools/jadx/ - jadx Wiki (GUI features, key bindings):
https://github.com/skylot/jadx/wiki - OWASP MASTG – Android Static Analysis:
https://mas.owasp.org/MASTG/Android/0x05c-Reverse-Engineering-and-Static-Analysis/