Skip to content

Bleh-something/deobfuscator

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

205 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Java Deobfuscator — modern-JDK compatible fork

This is a fork of java-deobfuscator/deobfuscator.

Every change in this fork was made by Claude (Anthropic's Claude Code) — the debugging, the fixes, and full end-to-end build-and-verify against a real Zelix KlassMaster–obfuscated jar running on JDK 25.

The deobfuscator itself is the original work of samczsun and the java-deobfuscator contributors and stays Apache-2.0 licensed. This fork only adds compatibility and robustness fixes on top — it does not claim authorship of the upstream project.

This project deobfuscates most commercially-available Java obfuscators (Zelix KlassMaster, Stringer, Allatori, DashO, DexGuard, …).


Why this fork exists

Upstream no longer builds or runs cleanly on current JDKs. On JDK 25 (and any modular JDK 9+) it crashes in several places, and the build fails because one of its dependencies is hosted on a repository that is now offline. This fork fixes all of that and makes the whole generic + peephole + zelix transformer set run without aborting.

What changed

🟢 Runs on modern JDKs (tested on JDK 25)

  • ASM 9.5 cannot parse the running JDK's own runtime classes once they're newer than Java 21 (JDK 25 = class-file major version 69), which crashed hierarchy resolution and class writing. Deobfuscator.pullFromRuntime now reads runtime classes version-tolerantly — it only needs the type hierarchy, so it downgrades the class-file version bytes before reading. Works on any JDK, current or future.
  • The class writer now falls back to COMPUTE_MAXS on an AssertionError from ASM frame computation (instead of printing a stack trace and writing a half-computed class).

🩹 Crash fixes — no more aborted runs

  • MethodAnalyzer (the abstract interpreter behind ConstantFolder etc.): synthesizes an unknown value when a local is read before being written on an obfuscated/dead path instead of throwing an NPE, and catches abstract operand-stack underflow (POP2 / DUP_x / array / math) so it degrades gracefully.
  • MethodExecutor (the Zelix string emulator): throws a clean, catchable ExecutionException instead of a raw NPE on an uninitialized local load.
  • Zelix StringEncryptionTransformer: isolates each class — one ZKM variant it can't emulate is skipped (with the temporary <clinit> scaffolding rolled back) instead of aborting the entire run.
  • Peephole LdcSwapInvokeSwapPopRemover and RedundantGotoRemover: null-guarded against truncated/obfuscated instruction tails.

🏗️ Build fix

  • The javavm dependency was hosted on repo.samczsun.com, which is offline (dead DNS). The build now pulls it from JitPack (com.github.java-deobfuscator:javavm), so mvn package works again.

🧹 Quieter output

  • Missing-rt.jar and unsupported-pattern code paths now log a single concise line instead of dumping full stack traces, so a clean run reads as a clean run.

⚠️ The JRE 8 thing (read this)

Two Zelix transformers — zelix.string.SimpleStringEncryptionTransformer and zelix.string.EnhancedStringEncryptionTransformer — spin up a full emulating VM (javavm) that needs the JDK's rt.jar / jce.jar / jsse.jar. Those files only exist on JDK 8; they were removed when the JDK became modular in Java 9.

  • On JDK 9+ (including 25) these two transformers skip gracefully with a clear message — everything else still runs.
  • To actually run them, launch the deobfuscator with a JDK 8 runtime (e.g. Adoptium Temurin 8):
    "C:\Program Files\Eclipse Adoptium\jdk-8.x.x-hotspot\bin\java.exe" -Xss512m -jar deobfuscator.jar --config config.yml
    
    You can build with any modern JDK and only run with JDK 8.

The main zelix.StringEncryptionTransformer does not need JDK 8 — it uses the lightweight MethodExecutor and decrypts the common ZKM string-encryption modes on any JDK. In practice it already handles the bulk of Zelix string encryption; the JDK-8-only Simple/Enhanced transformers are for additional/legacy ZKM variants.


New transformers added in this fork

Extra deobfuscation passes written for this fork:

  • zelix.InvokedynamicTransformer — devirtualizes ZKM's invokedynamic obfuscation. ZKM hides calls behind an indy whose bootstrap installs a lazy linker; this resolves the real target statically (bootstrap → linker → target) and rewrites each indy to a plain invokestatic. Genuine Java lambdas (LambdaMetafactory) are left untouched. No VM, runs on any JDK.
  • zelix.NumberEncryptionTransformer — reverses ZKM long/number encryption (decryptor.a(seedA, seedB, null).a(encrypted) → the literal long) by emulating the decryptor in the VM. Needs JDK 8 (like the Simple/Enhanced string transformers).
  • zelix.EnumObfuscationTransformer — restores the ACC_ENUM flag on enum classes and their constant fields when an obfuscator has stripped it. Without it, decompilers emit illegal new TheEnum("X", 0) field initializers instead of proper enum constants. No VM, any JDK.
  • normalizer.CaseClassNormalizer — renames classes whose names differ only by case (e.g. Foo / foo), which otherwise overwrite each other when a jar is extracted or decompiled on a case-insensitive filesystem (Windows/macOS). All references are rewritten. Run it last. No VM, any JDK.
  • normalizer.ReturnTypeOverloadNormalizer — renames methods that share a name and parameter types but differ only in return type (legal in bytecode, illegal in Java source, so decompiled output won't compile). Renames each colliding variant consistently across every declaration and call site, so override chains stay intact — and needs no class hierarchy/libraries on the classpath. Run it last. No VM, any JDK.

On a real Zelix-obfuscated sample these recover every ZKM invokedynamic call and encrypted long, restore all enum constants, resolve every case-only class-name collision, and disambiguate every return-type method overload — alongside the strings handled by the main string transformer.


Build

mvn -DskipTests package
# -> target/deobfuscator-1.0.0.jar

Quick Start

  • If you know which obfuscators were used, skip the detect step.
  • Create detect.yml (replace input.jar):
input: input.jar
detect: true
  • Run java -jar deobfuscator.jar --config detect.yml to identify the obfuscators.
  • Create config.yml:
input: input.jar
output: output.jar
transformers:
  - [fully-qualified-name-of-transformer]
  - ... etc
  • Run java -jar deobfuscator.jar --config config.yml
  • Re-run detection if the JAR wasn't fully deobfuscated — obfuscations can be layered.

See USAGE.md for more. Transformer names are relative to com.javadeobfuscator.deobfuscator.transformers (e.g. zelix.FlowObfuscationTransformer, general.peephole.ConstantFolder).

Supported Obfuscators

FAQs

"Could not locate a class file"

You need to add the JARs the input references via path: / libraries: in the config. You'll almost always need rt.jar (JDK 8). This is not a crash — the deobfuscator falls back to COMPUTE_MAXS and continues.

"A StackOverflowError occurred during deobfuscation"

Increase your stack size, e.g. java -Xss128m -jar deobfuscator.jar.

Licensing

Apache 2.0, same as upstream. The original deobfuscator is © samczsun and the java-deobfuscator contributors. The fork's modifications were made by Claude (Anthropic).

About

AI Fork of Java-Deobfuscator/deobfuscator

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages