Skip to content

Disassembly

Disassembly is a reconstruction of a Java class file into a human-readable representation or into Java source code (decompilation), this is the main view you’ll see in slicer.

Custom ones can be added through the Disassembler API (popular ones are here).

The currently supported decompilers are:

CFR is a general-purpose Java decompiler developed by Lee Benfield.

It supports Java features up to Java 14, it is quite fast and produces decently accurate output. However, when it comes to obfuscation, the output accuracy is not great, and it quickly resorts to producing unreadable pseudocode.

Example output
package sample.math;
public class HeapSort {
public void sort(int[] arr) {
int i;
int n = arr.length;
for (i = n / 2 - 1; i >= 0; --i) {
this.heapify(arr, n, i);
}
for (i = n - 1; i >= 0; --i) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
this.heapify(arr, i, 0);
}
}
void heapify(int[] arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest]) {
largest = l;
}
if (r < n && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
this.heapify(arr, n, largest);
}
}
static void printArray(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; ++i) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = new int[]{12, 11, 13, 5, 6, 7};
HeapSort ob = new HeapSort();
ob.sort(arr);
System.out.println("Sorted array is:");
HeapSort.printArray(arr);
}
}
Example output (obfuscated; ZKM 17.0)
package sample.math;
public class HeapSort {
public static boolean F;
private static final String a;
public void N(Object[] objectArray) {
int n;
long l = (Long)objectArray[0];
int[] nArray = (int[])objectArray[1];
long l2 = l ^ 0x75AF228E50B2L;
int n2 = nArray.length;
for (n = n2 / 2 - 1; n >= 0; --n) {
Object[] objectArray2 = new Object[4];
objectArray2[3] = n;
objectArray2[2] = n2;
objectArray2[1] = l2;
objectArray2[0] = nArray;
this.q(objectArray2);
}
for (n = n2 - 1; n >= 0; --n) {
int n3 = nArray[0];
nArray[0] = nArray[n];
nArray[n] = n3;
Object[] objectArray3 = new Object[4];
objectArray3[3] = 0;
objectArray3[2] = n;
objectArray3[1] = l2;
objectArray3[0] = nArray;
this.q(objectArray3);
}
}
void q(Object[] objectArray) {
block14: {
int n;
int n2;
long l;
int n3;
int[] nArray;
block13: {
int n4;
int n5;
block11: {
boolean bl;
long l2;
block12: {
boolean bl2;
int n6;
block10: {
block8: {
block9: {
nArray = (int[])objectArray[0];
l2 = (Long)objectArray[1];
n3 = (Integer)objectArray[2];
n5 = (Integer)objectArray[3];
l = l2 ^ 0L;
n2 = n5;
int n7 = 2 * n5 + 1;
bl = F;
n6 = 2 * n5 + 2;
n = n7;
n4 = n3;
if (bl) break block8;
if (n >= n4) break block9;
n = nArray[n7];
n4 = nArray[n2];
bl2 = bl;
if (l2 < 0L) break block10;
if (bl2) break block8;
if (n > n4) {
n2 = n7;
}
}
n = n6;
n4 = n3;
}
bl2 = bl;
}
if (bl2) break block11;
if (n >= n4) break block12;
n = nArray[n6];
n4 = nArray[n2];
if (l2 <= 0L || bl) break block11;
if (n > n4) {
n2 = n6;
}
}
n = n2;
n4 = bl ? 1 : 0;
if (l2 <= 0L) break block11;
if (n4 != 0) break block13;
n4 = n5;
}
if (n == n4) break block14;
n = nArray[n5];
}
int n8 = n;
nArray[n5] = nArray[n2];
nArray[n2] = n8;
Object[] objectArray2 = new Object[4];
objectArray2[3] = n2;
objectArray2[2] = n3;
objectArray2[1] = l;
objectArray2[0] = nArray;
this.q(objectArray2);
}
}
static void t(Object[] objectArray) {
int[] nArray = (int[])objectArray[0];
long l = (Long)objectArray[1];
int n = nArray.length;
for (int i = 0; i < n; ++i) {
System.out.print(nArray[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
long l;
long l2 = l = 54536233763080L;
long l3 = l2 ^ 0x56D9CC7B6245L;
long l4 = l2 ^ 0x767115DAF19BL;
int[] nArray = new int[]{12, 11, 13, 5, 6, 7};
HeapSort heapSort = new HeapSort();
Object[] objectArray = new Object[2];
objectArray[1] = nArray;
objectArray[0] = l4;
heapSort.N(objectArray);
System.out.println(a);
Object[] objectArray2 = new Object[2];
objectArray2[1] = l3;
objectArray2[0] = nArray;
HeapSort.t(objectArray2);
}
/*
* Handled impossible loop by duplicating code
* Enabled aggressive block sorting
*/
static {
char[] cArray;
block12: {
int n;
int n2;
char[] cArray2;
int n3;
block11: {
char[] cArray3 = ">LT4+ )\fQT!7d`\u001e\u0019".toCharArray();
n3 = 0;
int n4 = cArray3.length;
cArray2 = cArray3;
n2 = n4;
if (n4 <= 1) break block11;
cArray = cArray2;
n = n2;
if (n2 <= n3) break block12;
}
do {
char[] cArray4 = cArray2;
char[] cArray5 = cArray2;
int n5 = n3;
while (true) {
int n6;
char c = cArray4[n5];
switch (n3 % 7) {
case 0: {
n6 = 109;
break;
}
case 1: {
n6 = 35;
break;
}
case 2: {
n6 = 38;
break;
}
case 3: {
n6 = 64;
break;
}
case 4: {
n6 = 78;
break;
}
case 5: {
n6 = 68;
break;
}
default: {
n6 = 9;
}
}
cArray4[n5] = (char)(c ^ n6);
++n3;
cArray2 = cArray5;
n2 = n2;
if (n2 != 0) break;
cArray5 = cArray2;
n = n2;
n5 = n2;
cArray4 = cArray2;
}
cArray = cArray2;
n = n2;
} while (n2 > n3);
}
a = new String(cArray).intern();
}
}
NameTypeDefaultDescription
stringbufferBooleanfalse if class file from version JAVA_5 or greaterConvert new StringBuffer().append.append.append to string + string + string
stringbuilderBooleantrue if class file from version JAVA_5 or greaterConvert new StringBuilder().append.append.append to string + string + string
stringconcatBooleantrue if class file from version JAVA_9 or greaterConvert usages of StringConcatFactor to string + string + string
decodeenumswitchBooleantrue if class file from version JAVA_5 or greaterRe-sugar switch on enum
sugarenumsBooleantrue if class file from version JAVA_5 or greaterRe-sugar enums
decodestringswitchBooleantrue if class file from version JAVA_7 or greaterRe-sugar switch on String
previewfeaturesBooleantrueDecompile preview features if class was compiled with ‘javac —enable-preview’
sealedBooleantrue if class file from version JAVA_18 or greater, or experimental in JAVA_16, JAVA_17Decompile ‘sealed’ constructs
switchexpressionBooleantrue if class file from version JAVA_14 or greater, or experimental in JAVA_12, JAVA_13Re-sugar switch expression
recordtypesBooleantrue if class file from version JAVA_16 or greater, or experimental in JAVA_14, JAVA_15Re-sugar record types
instanceofpatternBooleantrue if class file from version JAVA_16 or greater, or experimental in JAVA_14, JAVA_15Re-sugar instanceof pattern matches
arrayiterBooleantrue if class file from version JAVA_5 or greaterRe-sugar array based iteration
collectioniterBooleantrue if class file from version JAVA_5 or greaterRe-sugar collection based iteration
tryresourcesBooleantrue if class file from version JAVA_7 or greaterReconstruct try-with-resources
decodelambdasBooleantrue if class file from version JAVA_8 or greaterRe-build lambda functions
innerclassesBooleantrueDecompile inner classes
forbidmethodscopedclassesBooleanfalseDon’t allow method scoped classes
forbidanonymousclassesBooleanfalseDon’t allow anonymous classes
hideutfBooleantrueHide UTF8 characters - quote them instead of showing the raw characters
hidelongstringsBooleanfalseHide very long strings - useful if obfuscators have placed fake code in strings
removeboilerplateBooleantrueRemove boilderplate functions - constructor boilerplate, lambda deserialisation etc.
removeinnerclasssyntheticsBooleantrueRemove (where possible) implicit outer class references in inner classes
relinkconstBooleantrueRelink constants - if there is an inlined reference to a field, attempt to de-inline
relinkconststringBooleanValue of option ‘relinkconst’Relink constant strings
liftconstructorinitBooleantrueLift initialisation code common to all constructors into member initialisation
removedeadmethodsBooleantrueRemove pointless methods - default constructor etc.
removebadgenericsBooleantrueHide generics where we’ve obviously got it wrong, and fallback to non-generic
sugarassertsBooleantrueRe-sugar assert calls
sugarboxingBooleantrueWhere possible, remove pointless boxing wrappers
sugarretrolambdaBooleanfalseWhere possible, resugar uses of retro lambda
decodefinallyBooleantrueRe-sugar finally statements
tidymonitorsBooleantrueRemove support code for monitors - e.g. catch blocks just to exit a monitor
commentmonitorsBooleanfalseReplace monitors with comments - useful if we’re completely confused
lenientBooleanfalseBe a bit more lenient in situations where we’d normally throw an exception
commentsBooleantrueOutput comments describing decompiler status, fallback flags etc.
forcetopsortBoolean or nullnullForce basic block sorting
forceclassfileverString or nullnullForce the version of the classfile
forloopaggcaptureBoolean or nullnullAllow for loops to aggressively roll mutations into update section
forcetopsortaggressBoolean or nullnullForce extra aggressive topsort options
forcetopsortnopullBoolean or nullnullForce topsort not to pull try blocks
forcecondpropagateBoolean or nullnullPull results of deterministic jumps back through some constant assignments
reducecondscopeBoolean or nullnullReduce the scope of conditionals, possibly generating more anonymous blocks
forcereturningifsBoolean or nullnullMove return up to jump site
ignoreexceptionsalwaysBooleanfalseDrop exception information (WARNING : changes semantics, dangerous!)
antiobfBooleanfalseUndo various obfuscations
obfcontrolBooleanValue of option ‘antiobf’Undo control flow obfuscation
obfattrBooleanValue of option ‘antiobf’Undo attribute obfuscation
constobfBooleanValue of option ‘antiobf’Undo constant obfuscation
hidebridgemethodsBoolean!Value of option ‘obfattr’Hide bridge methods
ignoreexceptionsBooleanfalseDrop exception information if completely stuck
forceexceptionpruneBoolean or nullnullRemove nested exception handlers if they don’t change semantics
aexaggBoolean or nullnullTry to extend and merge exceptions more aggressively
aexagg2Boolean or nullnullTry to extend and merge exceptions more aggressively (may change semantics)
recovertypeclashBoolean or nullnullSplit lifetimes where analysis caused type clash
recovertypehintsBoolean or nullnullRecover type hints for iterators from first pass
recoverBooleantrueAllow more and more aggressive options to be set if decompilation fails
eclipseBooleantrueEnable transformations to handle Eclipse code better
overrideBooleantrue if class file from version JAVA_6 or greaterGenerate @Override annotations
showinferrableBooleanfalse if class file from version JAVA_7 or greaterDecorate methods with explicit types if not implied by arguments
allowcorrectingBooleantrueAllow transformations which correct errors
labelledblocksBooleantrueAllow code to be emitted which uses labelled blocks
j14classobjBooleanfalse if class file from version JAVA_5 or greaterReverse java 1.4 class object construction
hidelangimportsBooleantrueHide imports from java.lang.
recpassInteger0Decompile specifically with recovery options from pass #X
renameBooleanfalseSynonym for ‘renamedupmembers’ + ‘renameillegalidents’ + ‘renameenumidents’
renamedupmembersBooleanValue of option 'rename'Rename ambiguous/duplicate fields
renamesmallmembersInteger0Rename small members
renameillegalidentsBooleanValue of option 'rename'Rename identifiers which are not valid java identifiers
renameenumidentsBooleanValue of option 'rename'Rename ENUM identifiers which do not match their ‘expected’ string names
removedeadconditionalsBoolean or nullnullRemove code that can’t be executed
aggressivedoextensionBoolean or nullnullFold impossible jumps into do loops with ‘first’ test
aggressiveduffBoolean or nullnullFold duff device style switches with additional control
aggressivedocopyInteger0Clone code from impossible jumps into loops with ‘first’ test
aggressivesizethresholdInteger13000Opcode count at which to trigger aggressive reductions
staticinitreturnBooleantrueTry to remove return from static init
usenametableBooleantrueUse local variable name table if present
pullcodecaseBooleanfalsePull code into case statements agressively
allowmalformedswitchBoolean or nullnullAllow potentially malformed switch statements
elidescalaBooleanfalseElide things which aren’t helpful in scala output
usesignaturesBooleantrueUse signatures in addition to descriptors
lomemBooleanfalseBe more agressive about uncaching in order to reduce memory footprint
importfilterString or nullnullSubstring regex - import classes only when fqn matches this pattern
trackbytecodelocBooleanfalsePropagate bytecode location info
dumpexceptionstacktraceBooleantrueWhether to dump the stack trace of exceptions which occurred during decompilation

Vineflower is a fork of the Fernflower analytical decompiler, developed by an independent team.

It improves on the already great accuracy of Fernflower and also supports most modern Java features, e.g. records, enhanced pattern matching and much more. It also includes rudimentary support for resugaring Kotlin class files.

Example output
package sample.math;
public class HeapSort {
public void sort(int[] arr) {
int n = arr.length;
for (int i = n / 2 - 1; i >= 0; i--) {
this.heapify(arr, n, i);
}
for (int i = n - 1; i >= 0; i--) {
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
this.heapify(arr, i, 0);
}
}
void heapify(int[] arr, int n, int i) {
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[i]) {
largest = l;
}
if (r < n && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
this.heapify(arr, n, largest);
}
}
static void printArray(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = new int[]{12, 11, 13, 5, 6, 7};
HeapSort ob = new HeapSort();
ob.sort(arr);
System.out.println("Sorted array is:");
printArray(arr);
}
}
Example output (obfuscated; ZKM 17.0)
package sample.math;
public class HeapSort {
public static boolean F;
private static final String a;
public void N(Object[] var1) {
long var2 = (Long)var1[0];
int[] var4 = (int[])var1[1];
long var5 = var2 ^ 129395059478706L;
int var7 = var4.length;
for (int var8 = var7 / 2 - 1; var8 >= 0; var8--) {
Object[] var10006 = new Object[]{null, null, null, var8};
var10006[2] = var7;
var10006[1] = var5;
var10006[0] = var4;
this.q(var10006);
}
for (int var10 = var7 - 1; var10 >= 0; var10--) {
int var9 = var4[0];
var4[0] = var4[var10];
var4[var10] = var9;
Object[] var11 = new Object[]{null, null, null, 0};
var11[2] = var10;
var11[1] = var5;
var11[0] = var4;
this.q(var11);
}
}
void q(Object[] var1) {
int[] var2;
long var3;
int var5;
int var6;
long var7;
byte var9;
int var10;
int var12;
int var10000;
int var10001;
byte var10002;
label69: {
var2 = (int[])var1[0];
var3 = (Long)var1[1];
var5 = (Integer)var1[2];
var6 = (Integer)var1[3];
var7 = var3 ^ 0L;
var10 = var6;
int var11 = 2 * var6 + 1;
var9 = F;
var12 = 2 * var6 + 2;
var10000 = var11;
var10001 = var5;
label68:
if (var9 == 0) {
if (var11 < var5) {
var10000 = var2[var11];
var10001 = var2[var6];
var10002 = var9;
if (var3 < 0L) {
break label69;
}
if (var9 != 0) {
break label68;
}
if (var10000 > var10001) {
var10 = var11;
}
}
var10000 = var12;
var10001 = var5;
}
var10002 = var9;
}
label73: {
label56:
if (var10002 == 0) {
if (var10000 < var10001) {
var10000 = var2[var12];
var10001 = var2[var10];
if (var3 <= 0L || var9 != 0) {
break label56;
}
if (var10000 > var10001) {
var10 = var12;
}
}
var10000 = var10;
var10001 = var9;
if (var3 > 0L) {
if (var9 != 0) {
break label73;
}
var10001 = var6;
}
}
if (var10000 == var10001) {
return;
}
var10000 = var2[var6];
}
int var13 = var10000;
var2[var6] = var2[var10];
var2[var10] = var13;
Object[] var10006 = new Object[]{null, null, null, var10};
var10006[2] = var5;
var10006[1] = var7;
var10006[0] = var2;
this.q(var10006);
}
static void t(Object[] var0) {
int[] var1 = (int[])var0[0];
long var2 = (Long)var0[1];
int var4 = var1.length;
for (int var5 = 0; var5 < var4; var5++) {
System.out.print(var1[var5] + " ");
}
System.out.println();
}
public static void main(String[] args) {
long var1 = 54536233763080L;
long var3 = var1 ^ 95493438530117L;
long var5 = var1 ^ 130228070052251L;
int[] var7 = new int[]{12, 11, 13, 5, 6, 7};
HeapSort var8 = new HeapSort();
var8.N(new Object[]{var5, var7});
System.out.println(a);
t(new Object[]{var7, var3});
}
// $VF: Irreducible bytecode was duplicated to produce valid code
static {
char[] var10001 = ">LT4+ )\fQT!7d`\u001e\u0019".toCharArray();
int var10003 = var10001.length;
int var0 = 0;
char[] var10 = var10001;
int var3 = var10003;
char[] var16;
int var10004;
if (var10003 <= 1) {
var16 = var10001;
var10004 = var0;
} else {
var10 = var10001;
var3 = var10003;
if (var10003 <= var0) {
String var24 = new String(var10001).intern();
byte var8 = -1;
a = var24;
return;
}
var16 = var10001;
var10004 = var0;
}
while (true) {
char var10005 = var16[var10004];
byte var10006;
switch (var0 % 7) {
case 0:
var10006 = 109;
break;
case 1:
var10006 = 35;
break;
case 2:
var10006 = 38;
break;
case 3:
var10006 = 64;
break;
case 4:
var10006 = 78;
break;
case 5:
var10006 = 68;
break;
default:
var10006 = 9;
}
var16[var10004] = (char)(var10005 ^ var10006);
var0++;
if (var3 == 0) {
var10004 = var3;
var16 = var10;
} else {
if (var3 <= var0) {
String var23 = new String(var10).intern();
byte var7 = -1;
a = var23;
return;
}
var16 = var10;
var10004 = var0;
}
}
}
}
NameTypeDefaultDescription
remove-bridgeBoolean1Removes any methods that are marked as bridge from the decompiled output.
remove-syntheticBoolean1Removes any methods and fields that are marked as synthetic from the decompiled output.
decompile-innerBoolean1Process inner classes and add them to the decompiled output.
decompile-java4Boolean1Resugar the Java 1-4 class reference format instead of leaving the synthetic code.
decompile-assertBoolean1Decompile assert statements.
hide-empty-superBoolean1Hide super() calls with no parameters.
hide-default-constructorBoolean1Hide constructors with no parameters and no code.
decompile-genericsBoolean1Decompile generics in classes, methods, fields, and variables.
incorporate-returnsBoolean1Integrate returns better in try-catch blocks instead of storing them in a temporary variable.
ensure-synchronized-monitorsBoolean1If a synchronized block has a monitorenter without any corresponding monitorexit, try to deduce where one should be to ensure the synchronized is correctly decompiled.
decompile-enumsBoolean1Decompile enums.
decompile-previewBoolean1Decompile features marked as preview or incubating in the latest Java versions.
remove-getclassBoolean1Remove synthetic getClass() calls created by code such as ‘obj.new Inner()’.
keep-literalsBoolean0Keep NaN, infinities, and pi values as is without resugaring them.
boolean-as-intBoolean1Represent integers 0 and 1 as booleans.
ascii-stringsBoolean0Encode non-ASCII characters in string and character literals as Unicode escapes.
synthetic-not-setBoolean0Treat some known structures as synthetic even when not explicitly set.
undefined-as-objectBoolean1Treat nameless types as java.lang.Object.
use-lvt-namesBoolean1Use LVT names for local variables and parameters instead of var_.
use-method-parametersBoolean1Use method parameter names, as given in the MethodParameters attribute.
remove-empty-try-catchBoolean1Remove try-catch blocks with no code.
decompile-finallyBoolean1Decompile finally blocks.
lambda-to-anonymous-classBoolean0Decompile lambda expressions as anonymous classes.
bytecode-source-mappingBoolean0Map Bytecode to source lines.
ignore-invalid-bytecodeBoolean0Ignore bytecode that is malformed.
verify-anonymous-classesBoolean0Verify that anonymous classes are local.
ternary-constant-simplificationBoolean0Fold branches of ternary expressions that have boolean true and false constants.
pattern-matchingBoolean1Decompile with if and switch pattern matching enabled.
try-loop-fixBoolean1Fixes rare cases of malformed decompilation when try blocks are found inside of while loops
ternary-in-ifBoolean1Tries to collapse if statements that have a ternary in their condition.
decompile-switch-expressionsBoolean1Decompile switch expressions in modern Java class files.
show-hidden-statementsBoolean0Display hidden code blocks for debugging purposes.
override-annotationBoolean1Display override annotations for methods known to the decompiler.
simplify-stackBoolean1Simplify variables across stack bounds to resugar complex statements.
verify-mergesBoolean0Tries harder to verify the validity of variable merges. If there are strange variable recompilation issues, this is a good place to start.
old-try-dedupBoolean0Use the old try deduplication algorithm for methods with obfuscated exceptions, which inserts dummy exception handlers instead of duplicating blocks
explicit-genericsBoolean0Put explicit diamond generic arguments on method calls.
inline-simple-lambdasBoolean1Remove braces on simple, one line, lambda expressions.
rename-membersBoolean0Rename classes, fields, and methods with a number suffix to help in deobfuscation.
new-line-separatorBoolean1Use \n instead of \r\n for new lines. Deprecated, do not use.
indent-stringString" "A string of spaces or tabs that is placed for each indent level.
preferred-line-lengthInteger160Max line length before formatting is applied.
bannerString""A message to display at the top of the decompiled file.
error-messageString"Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!)"Message to display when an error occurs in the decompiler.
warn-inconsistent-inner-attributesBoolean1Warn about inconsistent inner class attributes
dump-bytecode-on-errorBoolean1Put the bytecode in the method body when an error occurs.
dump-exception-on-errorBoolean1Put the exception message in the method body or source file when an error occurs.
decompiler-commentsBoolean1Sometimes, odd behavior of the bytecode or unfixable problems occur. This enables or disables the adding of those to the decompiled output.
sourcefile-commentsBoolean0Add debug comments showing the class SourceFile attribute if present.
decompile-complex-constant-dynamicBoolean0Some constant-dynamic expressions can’t be converted to a single Java expression with identical run-time behaviour. This decompiles them to a similar non-lazy expression, marked with a comment.
force-jsr-inlineBoolean0Forces the processing of JSR instructions even if the class files shouldn’t contain it (Java 7+)
dump-text-tokensBoolean0Dump Text Tokens on each class file
remove-importsBoolean0Remove import statements from the decompiled code
mark-corresponding-syntheticsBoolean0Mark lambdas and anonymous and local classes with their respective synthetic constructs
validate-inner-classes-namesBoolean1Validates that the inner class name is correct (if it is separated using ”$” for example BaseClass$InnerClass). If not then inner class won’t be processed.
kt-show-publicBooleantrueIf a construct is public, show the public keyword
kt-enableBooleantrueDecompile Kotlin classes as Kotlin instead of Java
kt-unknown-defaultsString...String to use for unknown default arguments, or empty to not indicate unknown defaults
kt-collapse-string-concatBooleantrueConvert string concatenations to Kotlin string templates.
variable-renamingString or nullnullUse a custom renamer for variable names. Built-in options include “jad” and “tiny”.
rename-parametersBooleanfalseUse the custom renamer for parameters in addition to locals.

Procyon is a suite of metaprogramming tools for Java, including a decompiler, developed by Mike Strobel.

It can be quite slow, especially with larger class files, however it excels at decompiling obfuscated class files into readable output, thanks to its large collection of optimization passes done on its internal bytecode representation.

Example output
package sample.math;
public class HeapSort
{
public void sort(final int[] arr) {
final int n = arr.length;
for (int i = n / 2 - 1; i >= 0; --i) {
this.heapify(arr, n, i);
}
for (int i = n - 1; i >= 0; --i) {
final int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
this.heapify(arr, i, 0);
}
}
void heapify(final int[] arr, final int n, final int i) {
int largest = i;
final int l = 2 * i + 1;
final int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest]) {
largest = l;
}
if (r < n && arr[r] > arr[largest]) {
largest = r;
}
if (largest != i) {
final int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
this.heapify(arr, n, largest);
}
}
static void printArray(final int[] arr) {
for (int n = arr.length, i = 0; i < n; ++i) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(final String[] args) {
final int[] arr = { 12, 11, 13, 5, 6, 7 };
final HeapSort ob = new HeapSort();
ob.sort(arr);
System.out.println("Sorted array is:");
printArray(arr);
}
}
Example output (obfuscated; ZKM 17.0)
package sample.math;
public class HeapSort
{
public static boolean F;
private static final String a;
public void N(final Object[] array) {
final long longValue = (long)array[0];
final int[] array2 = (int[])array[1];
final long n = longValue ^ 0x75AF228E50B2L;
final int length = array2.length;
for (int i = length / 2 - 1; i >= 0; --i) {
this.q(new Object[] { array2, n, length, i });
}
for (int j = length - 1; j >= 0; --j) {
final int n2 = array2[0];
array2[0] = array2[j];
array2[j] = n2;
this.q(new Object[] { array2, n, j, 0 });
}
}
void q(final Object[] array) {
final int[] array2 = (int[])array[0];
final long longValue = (long)array[1];
final int intValue = (int)array[2];
final int intValue2 = (int)array[3];
final long n = longValue ^ 0x0L;
int n2 = intValue2;
final boolean f = HeapSort.F;
final int n3 = 2 * intValue2 + 1;
final boolean b = f;
final int n4 = 2 * intValue2 + 2;
int n6;
final int n5 = n6 = n3;
final int n8;
final int n7 = n8 = intValue;
if (b || n5 >= n7) {
goto Label_0129;
}
final int n9 = array2[n3];
final int n10 = array2[n2];
final boolean b2 = b;
if (longValue < 0L || b2) {
int n13 = 0;
Label_0205: {
Label_0194: {
if (!b2) {
if (n5 < n7) {
final int n11 = array2[n4];
final int n12 = array2[n2];
if (longValue <= 0L || b) {
break Label_0194;
}
if (n11 > n12) {
n2 = n4;
}
}
n13 = (n6 = n2);
final boolean b3 = b;
if (longValue > 0L && b3) {
break Label_0205;
}
}
}
if (n9 == n10) {
return;
}
n13 = array2[intValue2];
}
final int n14 = n13;
array2[intValue2] = array2[n2];
array2[n2] = n14;
this.q(new Object[] { array2, n, intValue, n2 });
return;
}
if (n9 > n10) {
n2 = n3;
goto Label_0125;
}
goto Label_0125;
}
static void t(final Object[] array) {
final int[] array2 = (int[])array[0];
((Long)array[1]).longValue();
for (int length = array2.length, i = 0; i < length; ++i) {
System.out.print(array2[i] + " ");
}
System.out.println();
}
public static void main(final String[] args) {
final long n = 54536233763080L;
final long n2 = n ^ 0x56D9CC7B6245L;
final long n3 = n ^ 0x767115DAF19BL;
final int[] array = { 12, 11, 13, 5, 6, 7 };
new HeapSort().N(new Object[] { n3, array });
System.out.println(HeapSort.a);
t(new Object[] { array, n2 });
}
static {
final char[] charArray = ">LT4+ )\fQT!7d`\u001e\u0019".toCharArray();
int length;
int n2;
final int n = n2 = (length = charArray.length);
int n3 = 0;
while (true) {
Label_0120: {
if (n > 1) {
break Label_0120;
}
length = (n2 = n3);
do {
final char c = charArray[n2];
charArray[length] = (char)(c ^ switch (n3 % 7) {
case 0 -> 'm';
case 1 -> '#';
case 2 -> '&';
case 3 -> '@';
case 4 -> 'N';
case 5 -> 'D';
default -> '\t';
});
++n3;
} while (n == 0);
}
if (n <= n3) {
a = new String(charArray).intern();
return;
}
continue;
}
}
}
NameTypeDefaultDescription
always-generate-exception-variable-for-catch-blocksBooleantrueAlways generate an exception variable for catch blocks, even if it is unused
disable-for-each-transformsBooleanfalseDisable transforms for for-each loops
exclude-nested-typesBooleanfalseExclude nested types from decompilation
flatten-switch-blocksBooleanfalseDrop unnecessary braces in switch blocks
force-explicit-importsBooleantrueForce fully qualified type names in output
force-explicit-type-argumentsBooleanfalseForce explicit type arguments on method calls
force-fully-qualified-referencesBooleanfalseForce fully qualified references
include-error-diagnosticsBooleantrueInclude error diagnostics in output
include-line-numbers-in-bytecodeBooleantrueInclude line numbers in bytecode comments
merge-variablesBooleanfalseMerge variables with non-overlapping scopes
preview-features-enabledBooleanfalseEnable decompilation of preview features
retain-pointless-switchesBooleanfalseRetain switches that could be replaced with if statements
retain-redundant-castsBooleanfalseRetain redundant type casts
show-debug-line-numbersBooleanfalseShow line numbers from debug info in comments
show-synthetic-membersBooleanfalseShow synthetic (compiler-generated) members
simplify-member-referencesBooleanfalseSimplify member references by removing redundant qualifiers
text-block-line-minimumInteger3Minimum number of lines required for text block formatting
unicode-output-enabledBooleanfalseEnable unicode characters in output

The currently supported disassemblers are:

JASM is a modular (Java and soon Dalvik) disassembler, developed by the team behind Recaf.

It produces clean and easily comprehensible output with a focus on obfuscation resilience.

Example output
.sourcefile "HeapSort.java"
.super java/lang/Object
.class public super sample/math/HeapSort {
.method public <init> ()V {
parameters: { this },
code: {
A:
line 8
aload this
invokespecial java/lang/Object.<init> ()V
return
B:
}
}
.method public sort ([I)V {
parameters: { this, arr },
code: {
A:
line 16
aload arr
arraylength
istore n
B:
line 19
iload n
iconst_2
idiv
iconst_1
isub
istore i
C:
iload i
iflt F
D:
line 20
aload this
aload arr
iload n
iload i
invokevirtual sample/math/HeapSort.heapify ([III)V
E:
line 19
iinc i -1
goto C
F:
line 23
iload n
iconst_1
isub
istore i
G:
iload i
iflt M
H:
line 25
aload arr
iconst_0
iaload
istore temp
I:
line 26
aload arr
iconst_0
aload arr
iload i
iaload
iastore
J:
line 27
aload arr
iload i
iload temp
iastore
K:
line 30
aload this
aload arr
iload i
iconst_0
invokevirtual sample/math/HeapSort.heapify ([III)V
L:
line 23
iinc i -1
goto G
M:
line 32
return
N:
}
}
.method heapify ([III)V {
parameters: { this, arr, n, i },
code: {
A:
line 45
iload i
istore largest
B:
line 46
iconst_2
iload i
imul
iconst_1
iadd
istore l
C:
line 47
iconst_2
iload i
imul
iconst_2
iadd
istore r
D:
line 50
iload l
iload n
if_icmpge E
aload arr
iload l
iaload
aload arr
iload largest
iaload
if_icmple E
iload l
istore largest
E:
line 53
iload r
iload n
if_icmpge F
aload arr
iload r
iaload
aload arr
iload largest
iaload
if_icmple F
iload r
istore largest
F:
line 56
iload largest
iload i
if_icmpeq K
G:
line 57
aload arr
iload i
iaload
istore swap
H:
line 58
aload arr
iload i
aload arr
iload largest
iaload
iastore
I:
line 59
aload arr
iload largest
iload swap
iastore
J:
line 62
aload this
aload arr
iload n
iload largest
invokevirtual sample/math/HeapSort.heapify ([III)V
K:
line 64
return
L:
}
}
.method static printArray ([I)V {
parameters: { arr },
code: {
A:
line 73
aload arr
arraylength
istore n
B:
line 74
iconst_0
istore i
C:
iload i
iload n
if_icmpge F
D:
line 75
getstatic java/lang/System.out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
invokespecial java/lang/StringBuilder.<init> ()V
aload arr
iload i
iaload
invokevirtual java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
ldc " "
invokevirtual java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder.toString ()Ljava/lang/String;
invokevirtual java/io/PrintStream.print (Ljava/lang/String;)V
E:
line 74
iinc i 1
goto C
F:
line 76
getstatic java/lang/System.out Ljava/io/PrintStream;
invokevirtual java/io/PrintStream.println ()V
G:
line 77
return
H:
}
}
.method public static main ([Ljava/lang/String;)V {
parameters: { args },
code: {
A:
line 81
bipush 6
newarray int
dup
iconst_0
bipush 12
iastore
dup
iconst_1
bipush 11
iastore
dup
iconst_2
bipush 13
iastore
dup
iconst_3
iconst_5
iastore
dup
iconst_4
bipush 6
iastore
dup
iconst_5
bipush 7
iastore
astore arr
B:
line 83
new sample/math/HeapSort
dup
invokespecial sample/math/HeapSort.<init> ()V
astore ob
C:
line 84
aload ob
aload arr
invokevirtual sample/math/HeapSort.sort ([I)V
D:
line 86
getstatic java/lang/System.out Ljava/io/PrintStream;
ldc "Sorted array is:"
invokevirtual java/io/PrintStream.println (Ljava/lang/String;)V
E:
line 87
aload arr
invokestatic sample/math/HeapSort.printArray ([I)V
F:
line 88
return
G:
}
}
}
NameTypeDefaultDescription
indentString" "The indentation string to use when formatting output.

slicer includes a basic disassembler to power several bytecode analysis features, such as:

  • the class view (prettifying class file constructs)
  • the flow graph (prettifying instructions and computing the graph itself)
  • the search (prettifying constant pool entries)
  • the pseudocode disassembler
Example output
// Source file: HeapSort.java
package sample.math;
public super class HeapSort extends java.lang.Object {
public HeapSort() {
// 0: aload this Lsample/math/HeapSort;
// 1: invokespecial java/lang/Object <init> ()V
// 4: return
}
public void sort(int[] arr) {
// 0: aload arr [I
// 1: arraylength
// 2: istore n I
// 3: iload n I
// 4: iconst_2
// 5: idiv
// 6: iconst_1
// 7: isub
// 8: istore i I
// 9: iload i I
// 10: iflt 26
// 13: aload this Lsample/math/HeapSort;
// 14: aload arr [I
// 15: iload n I
// 16: iload i I
// 17: invokevirtual sample/math/HeapSort heapify ([III)V
// 20: iinc i I -1
// 23: goto 9
// 26: iload n I
// 27: iconst_1
// 28: isub
// 29: istore i I
// 30: iload i I
// 31: iflt 63
// 34: aload arr [I
// 35: iconst_0
// 36: iaload
// 37: istore temp I
// 39: aload arr [I
// 40: iconst_0
// 41: aload arr [I
// 42: iload i I
// 43: iaload
// 44: iastore
// 45: aload arr [I
// 46: iload i I
// 47: iload temp I
// 49: iastore
// 50: aload this Lsample/math/HeapSort;
// 51: aload arr [I
// 52: iload i I
// 53: iconst_0
// 54: invokevirtual sample/math/HeapSort heapify ([III)V
// 57: iinc i I -1
// 60: goto 30
// 63: return
}
void heapify(int[] arr, int n, int i) {
// 0: iload i I
// 1: istore largest I
// 3: iconst_2
// 4: iload i I
// 5: imul
// 6: iconst_1
// 7: iadd
// 8: istore l I
// 10: iconst_2
// 11: iload i I
// 12: imul
// 13: iconst_2
// 14: iadd
// 15: istore r I
// 17: iload l I
// 19: iload n I
// 20: if_icmpge 38
// 23: aload arr [I
// 24: iload l I
// 26: iaload
// 27: aload arr [I
// 28: iload largest I
// 30: iaload
// 31: if_icmple 38
// 34: iload l I
// 36: istore largest I
// 38: iload r I
// 40: iload n I
// 41: if_icmpge 59
// 44: aload arr [I
// 45: iload r I
// 47: iaload
// 48: aload arr [I
// 49: iload largest I
// 51: iaload
// 52: if_icmple 59
// 55: iload r I
// 57: istore largest I
// 59: iload largest I
// 61: iload i I
// 62: if_icmpeq 91
// 65: aload arr [I
// 66: iload i I
// 67: iaload
// 68: istore swap I
// 70: aload arr [I
// 71: iload i I
// 72: aload arr [I
// 73: iload largest I
// 75: iaload
// 76: iastore
// 77: aload arr [I
// 78: iload largest I
// 80: iload swap I
// 82: iastore
// 83: aload this Lsample/math/HeapSort;
// 84: aload arr [I
// 85: iload n I
// 86: iload largest I
// 88: invokevirtual sample/math/HeapSort heapify ([III)V
// 91: return
}
static void printArray(int[] arr) {
// 0: aload arr [I
// 1: arraylength
// 2: istore n I
// 3: iconst_0
// 4: istore i I
// 5: iload i I
// 6: iload n I
// 7: if_icmpge 43
// 10: getstatic java/lang/System out Ljava/io/PrintStream;
// 13: new java/lang/StringBuilder
// 16: dup
// 17: invokespecial java/lang/StringBuilder <init> ()V
// 20: aload arr [I
// 21: iload i I
// 22: iaload
// 23: invokevirtual java/lang/StringBuilder append (I)Ljava/lang/StringBuilder;
// 26: ldc " "
// 28: invokevirtual java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
// 31: invokevirtual java/lang/StringBuilder toString ()Ljava/lang/String;
// 34: invokevirtual java/io/PrintStream print (Ljava/lang/String;)V
// 37: iinc i I 1
// 40: goto 5
// 43: getstatic java/lang/System out Ljava/io/PrintStream;
// 46: invokevirtual java/io/PrintStream println ()V
// 49: return
}
public static void main(java.lang.String[] args) {
// 0: bipush 6
// 2: newarray t
// 4: dup
// 5: iconst_0
// 6: bipush 12
// 8: iastore
// 9: dup
// 10: iconst_1
// 11: bipush 11
// 13: iastore
// 14: dup
// 15: iconst_2
// 16: bipush 13
// 18: iastore
// 19: dup
// 20: iconst_3
// 21: iconst_5
// 22: iastore
// 23: dup
// 24: iconst_4
// 25: bipush 6
// 27: iastore
// 28: dup
// 29: iconst_5
// 30: bipush 7
// 32: iastore
// 33: astore arr [I
// 34: new sample/math/HeapSort
// 37: dup
// 38: invokespecial sample/math/HeapSort <init> ()V
// 41: astore ob Lsample/math/HeapSort;
// 42: aload ob Lsample/math/HeapSort;
// 43: aload arr [I
// 44: invokevirtual sample/math/HeapSort sort ([I)V
// 47: getstatic java/lang/System out Ljava/io/PrintStream;
// 50: ldc "Sorted array is:"
// 52: invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
// 55: aload arr [I
// 56: invokestatic sample/math/HeapSort printArray ([I)V
// 59: return
}
}
NameTypeDefaultDescription
indentString" "The indentation string to use when formatting output.
fullyQualifiedBooleantrueWhether class references should be fully qualified, i.e. contain the package name in-line rather than in an import.
verboseBooleantrueWhether a verbose disassembly should be produced, i.e. contain attribute info.

Constant pool entries are represented in a format consisting of the entry type identifier and the stringified value of the entry.

  • UTF8 <value> - any string-like value defined in the class file (names, descriptors, strings, …)
  • INTEGER/FLOAT/LONG/DOUBLE <value> - primitives defined in the class file
  • CLASS <class name in internal format, UTF8 value> - class reference
  • STRING "<UTF8 value>" - string literal defined in the class file
  • METHOD_TYPE <method descriptor, UTF8 value> - method descriptor reference
  • MODULE/PACKAGE <module/package name, UTF8 value> - module-info/package-info name data
  • FIELDREF/METHODREF/INTERFACE_METHODREF <owner name, CLASS value> <member name and descriptor, NAME_AND_TYPE value> - class member reference
  • NAME_AND_TYPE <name, UTF8 value> <descriptor, UTF8 value> - member name and descriptor reference
  • DYNAMIC/INVOKE_DYNAMIC <bootstrap method index> <member name and descriptor, NAME_AND_TYPE value>
  • METHOD_HANDLE <handle kind> <METHODREF value>
    • handle kinds: GET_FIELD, GET_STATIC, PUT_FIELD, PUT_STATIC, INVOKE_VIRTUAL, INVOKE_STATIC, INVOKE_SPECIAL, NEW_INVOKE_SPECIAL, INVOKE_INTERFACE

The disassembler produces pseudocode similar to Java, which allows you to view the rough structure of classes in a familiar way.

It does not resugar many code constructs, such as if statements and variable declarations, so knowledge of the JVM instruction set is highly necessary.

// Source file: StringsDuplicates.java
package sample.string;
public super class StringsDuplicates extends java.lang.Object {
private static final java.lang.String F = "Hello this is a duplicate string";
public StringsDuplicates() {
// 0: aload this Lsample/string/StringsDuplicates;
// 1: invokespecial java/lang/Object <init> ()V
// 4: return
}
public static void main(java.lang.String[] args) {
// 0: ldc "Hello this is a duplicate string"
// 2: astore s Ljava/lang/String;
// 3: ldc "Hello this is a duplicate string"
// 5: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 8: aload s Ljava/lang/String;
// 9: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 12: invokestatic sample/string/StringsDuplicates duplicate ()Ljava/lang/String;
// 15: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 18: ldc "Hello this is a duplicate string"
// 20: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 23: return
}
private static java.lang.String duplicate() {
// 0: ldc "Hello this is a duplicate string"
// 2: areturn
}
public static void p(java.lang.String s) {
// 0: getstatic java/lang/System out Ljava/io/PrintStream;
// 3: aload s Ljava/lang/String;
// 4: invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
// 7: return
}
}

Dissecting the class header, we can make out that we’re looking at a regular class named StringsDuplicates with the modifiers ACC_PUBLIC and ACC_SUPER, and a super class of java.lang.Object.

Furthermore, it’s located in the sample.string package and was compiled from a StringsDuplicates.java source file.

// Source file: StringsDuplicates.java
package sample.string;
public super class StringsDuplicates extends java.lang.Object {
// ...
}

Our class contains a couple of methods and one self-explanatory field, with one method resugared to its original constructor syntax. The field has an assigned value, showing a value from its ConstantValue attribute.

Code inside the methods is represented via string representations of individual instructions and their offsets.

Instructions may also be encased in a try-catch statement with a virtual goto in the handler, signifying an entry in the exception handler table.

// ...
private static final java.lang.String F = "Hello this is a duplicate string";
public StringsDuplicates() {
// 0: aload this Lsample/string/StringsDuplicates;
// 1: invokespecial java/lang/Object <init> ()V
// 4: return
}
public static void main(java.lang.String[] args) {
// 0: ldc "Hello this is a duplicate string"
// 2: astore s Ljava/lang/String;
// 3: ldc "Hello this is a duplicate string"
// 5: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 8: aload s Ljava/lang/String;
// 9: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 12: invokestatic sample/string/StringsDuplicates duplicate ()Ljava/lang/String;
// 15: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 18: ldc "Hello this is a duplicate string"
// 20: invokestatic sample/string/StringsDuplicates p (Ljava/lang/String;)V
// 23: return
}
// ...