Skip to content

JADX helper development

The standalone source is maintained in nl2sh/jadx-helper. Clone that repository before running its build commands.

This module builds an unsigned Android APK and repackages its runtime entries into jadx-helper.jar. It contains classes.dex and a narrow com.nl2sh.jadx.Main entrypoint for app_process; it is never installed as an Android application. The entrypoint accepts an APK path, an exact class name, and an output Java file path. nl2sh provides bounded arguments and output, a timeout, SHA-256 verification, and strong confirmation.

The build pins JADX core and DEX input to 1.5.1, matching the upstream Android library example, and targets Android API 26+. It uses Android Gradle Plugin 8.7.3, Gradle Wrapper 8.9, JDK 17, and Android SDK API 35. Run ./build-helper.sh on Linux/macOS or .\build-helper.ps1 in Windows PowerShell; no system Gradle installation is required. The Wrapper verifies the downloaded Gradle distribution with its published SHA-256. Gradle repackages all required runtime entries with reproducible ordering, timestamps, and compression. Reproducibility comparisons require the same Git revision and toolchain; fixed ZIP settings alone do not guarantee identical artifacts across source revisions. Both scripts write dist/jadx-helper.jar, its SHA-256 file, and metadata.json; dist/ is not committed. Include the applicable JADX and dependency licenses when distributing the JAR.

Single-class smoke tests passed on API 35 x86_64, API 28 ARMv7, and API 26 x86_64 ART. Native releases select the helper through an authenticated compatibility manifest; the historical v1.0.4 asset is no longer the default policy. Source builds without an embedded signed policy fall back to the compile-time pinned v0.2.0 helper URL and SHA-256. Explicit offline DEX helpers or custom HTTPS URLs with independent digests remain supported; an invalid embedded policy never falls back. jadx_check inspects the source and cache read-only. jadx_install is the only network acquisition entry and requires mutation approval; decompile_apk_class only uses an installed helper and requires strong confirmation. A manual invocation must provide a private writable temporary directory, for example CLASSPATH=/data/local/tmp/jadx-helper.jar /system/bin/app_process -Djava.io.tmpdir=/data/local/tmp/jadx-work / com.nl2sh.jadx.Main <apk> <class> <output>. Larger multidex APKs, memory use, and timeout cleanup need broader coverage.

Reproducibility comparisons require the same Git revision and pinned toolchain because AGP packages revision metadata. The build packages assets/nl2sh-runtime.json; packaging scripts derive metadata.json from those bytes and add the actual SHA-256 and size. Runtime --info and packaged metadata share build version/protocol fields. Tagged releases set versions explicitly. The main native release signs the aggregated JAR with its release trust root.

Entrypoint contract and failures

Invoke com.nl2sh.jadx.Main with exactly three arguments: input APK, original fully qualified class name, and output Java file. The output parent and java.io.tmpdir must already exist and be writable. Output is UTF-8 and an existing output file is overwritten. The helper uses one worker, SIMPLE decompilation, no code cache, no imports/debug information/inlining/renaming, and a filter for the exact class plus its $ inner classes. Resources are skipped and XML parsing is explicitly disabled. It does not export an entire project or guarantee recompilable source.

Exit Meaning
0 Source was written
2 Argument count is not three
3 JADX returned empty source
4 Original class name was not found
5 Loading, decompilation or file writing raised an exception; inspect stderr

These codes describe the helper itself. Native nl2sh applies its own timeout and bounded output handling. A manual app_process invocation does not pass through nl2sh approval or inherit its bounds; use the native tool for the normal Agent workflow. For a missing class, verify its original name rather than its display/deobfuscated alias. A DEX/JAR loading error requires checking the artifact format and device compatibility; a temporary-directory error requires a private writable directory.

Runtime information

CLASSPATH=/data/local/tmp/jadx-helper.jar /system/bin/app_process / com.nl2sh.jadx.Main --info returns one JSON object with protocol: 1, the build's helper_version, pinned jadx_core, and features: ["single_class", "inner_classes"]. It exits successfully without opening an APK or writing files. Clients must reject unsupported protocol versions before decompilation. SHA-256 and DEX validation remain required; runtime information does not authenticate downloaded code.

Protocol 1 --info, SHA-256 and the existing three-argument single-class entrypoint passed on API 26 x86_64 ART. ./build-helper.sh and ./gradlew --no-daemon :app:lintRelease passed.