JADX: Complete Guide to Android APK Decompilation and Source Code Analysis Using Kali Linux

JADX: Complete Guide to Android APK Decompilation and Source Code Analysis Using Kali Linux

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:

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:

OptionDescription
-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-resDo not decode resources
-s, --no-srcDo 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-gradleSave as an Android Gradle project
--export-gradle-type {auto|android-app|android-library|simple-java}Gradle project template used for export

Processing Options:

OptionDescription
-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-codeShow inconsistent code (incorrectly decompiled sections) instead of hiding them
--no-importsDisable use of imports, always output fully qualified names
--no-debug-infoDisable debug info processing (line numbers, local variable names)
--no-inline-anonymousDisable inlining of anonymous classes
--no-replace-constsDisable replacement of static field values with their constants
--escape-unicodeEscape non-ASCII (Unicode) characters in output
--respect-bytecode-access-modifiersDo not change methods/fields access modifiers to match original bytecode exactly
--deobfActivate 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:

OptionDescription
-v, --verboseVerbose output
-q, --quietSuppress console output
-h, --helpPrint help information
--versionPrint the current jadx version
--cfgSave the method control flow graph (CFG) files
--raw-cfgSave the raw, unprocessed CFG files

Environment Variables:

VariableDescription
JADX_DISABLE_XML_SECURITYSet to true to disable security checks for XML files
JADX_DISABLE_ZIP_SECURITYSet to true to disable security checks for zip files
JADX_ZIP_MAX_ENTRIES_COUNTMaximum allowed number of entries in zip files (default 100,000)
JADX_CONFIG_DIRCustom configuration directory
JADX_CACHE_DIRCustom cache directory
JADX_TMP_DIRCustom 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

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

Troubleshooting

ProblemCauseSolution
OutOfMemoryError on large APKsDefault JVM heap too smallIncrease heap manually: JAVA_OPTS="-Xmx4g" jadx -d out big_app.apk
Some methods show /* JADX ERROR */Bytecode too complex/obfuscated to restructure cleanlyUse --show-bad-code or switch to -m fallback for raw instructions
GUI won’t launch, no error shownMissing or incompatible Java versionVerify java -version shows Java 11+ 64-bit; install default-jdk if missing
Decompiled code doesn’t match app behavior exactlyCompiler optimizations, R8/ProGuard shrinkingCross-check suspicious logic against Smali via Apktool for ground truth
Zip entries count exceeds limit errorAPK is a zip bomb or unusually large archiveSet JADX_ZIP_MAX_ENTRIES_COUNT environment variable higher if the file is legitimate
jadx: command not foundNot installed or not in PATHRun sudo apt install jadx or verify the symlink in /usr/local/bin
Decompilation extremely slowSingle-threaded default or very large multi-dex APKIncrease -j thread count; use -r to skip resources if not needed

References

Exit mobile version