localVariablesInUse = new ArrayList<>();
+
+ // Find all local variables in use
+ for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
+ if (insn instanceof VarInsnNode varInsnNode && insn.getOpcode() >= ILOAD && insn.getOpcode() <= SALOAD) {
+ localVariablesInUse.add(varInsnNode.var);
+ }
+ }
+
+ // Remove all local variables that are not in use
+ for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
+ if (insn instanceof VarInsnNode varInsnNode && insn.getOpcode() >= ISTORE && insn.getOpcode() <= SASTORE) {
+ if (!localVariablesInUse.contains(varInsnNode.var)) {
+ // Pop the value from the stack
+ methodNode.instructions.set(insn, new InsnNode(POP));
+
+ removedVars.incrementAndGet();
+ }
+ }
+ }
+ }));
+
+ LOGGER.info("Removed {} unused local variables", removedVars.get());
+
+ return removedVars.get() > 0;
+ }
+}
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessJumpsCleanTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessJumpsCleanTransformer.java
new file mode 100644
index 0000000..44c95a6
--- /dev/null
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessJumpsCleanTransformer.java
@@ -0,0 +1,91 @@
+package uwu.narumi.deobfuscator.core.other.impl.clean.peephole;
+
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
+import uwu.narumi.deobfuscator.api.context.Context;
+import uwu.narumi.deobfuscator.api.transformer.Transformer;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Remove useless GOTO jumps
+ *
+ * A:
+ * ...
+ * goto B
+ * B:
+ * ...
+ *
+ */
+public class UselessJumpsCleanTransformer extends Transformer {
+ private boolean changed = false;
+
+ @Override
+ protected boolean transform(ClassWrapper scope, Context context) throws Exception {
+ context.classes(scope).forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> {
+ for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
+ if (insn.getOpcode() == GOTO) {
+ JumpInsnNode jumpInsn = (JumpInsnNode) insn;
+ if (jumpInsn.getNext() instanceof LabelNode labelNode && jumpInsn.label == labelNode) {
+
+ // Check if the label is used only by the jump instruction
+ if (!isLabelUsedOnlyByInstructions(methodNode, jumpInsn.label)) continue;
+
+ List labelUsedInsns = Arrays.stream(methodNode.instructions.toArray())
+ .filter(newInsn -> checkIfJumpHappens(newInsn, labelNode))
+ .toList();
+
+ boolean labelUsedOnlyOnce = labelUsedInsns.size() == 1;
+
+ if (labelUsedOnlyOnce) {
+ // Remove the goto and the label
+ methodNode.instructions.remove(labelNode);
+ methodNode.instructions.remove(jumpInsn);
+ changed = true;
+ }
+ }
+ }
+ }
+ }));
+
+ return changed;
+ }
+
+ private boolean isLabelUsedOnlyByInstructions(MethodNode methodNode, LabelNode labelNode) {
+ if (methodNode.tryCatchBlocks != null) {
+ boolean usedByTryCatchBlock = methodNode.tryCatchBlocks.stream()
+ .anyMatch(tryCatchBlockNode -> tryCatchBlockNode.start == labelNode || tryCatchBlockNode.end == labelNode || tryCatchBlockNode.handler == labelNode);
+
+ if (usedByTryCatchBlock) {
+ return false;
+ }
+ }
+
+ if (methodNode.localVariables != null) {
+ boolean usedByLocalVariable = methodNode.localVariables.stream()
+ .anyMatch(localVariableNode -> localVariableNode.start == labelNode || localVariableNode.end == labelNode);
+
+ if (usedByLocalVariable) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkIfJumpHappens(AbstractInsnNode insn, LabelNode labelNode) {
+ if (insn instanceof JumpInsnNode jumpInsn) {
+ return jumpInsn.label == labelNode;
+ } else if (insn instanceof LookupSwitchInsnNode lookupSwitchInsn) {
+ return lookupSwitchInsn.labels.contains(labelNode) || lookupSwitchInsn.dflt == labelNode;
+ } else if (insn instanceof TableSwitchInsnNode tableSwitchInsn) {
+ return tableSwitchInsn.labels.contains(labelNode) || tableSwitchInsn.dflt == labelNode;
+ }
+ return false;
+ }
+}
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessPopCleanTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessPopCleanTransformer.java
new file mode 100644
index 0000000..ab0573c
--- /dev/null
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessPopCleanTransformer.java
@@ -0,0 +1,34 @@
+package uwu.narumi.deobfuscator.core.other.impl.clean.peephole;
+
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Frame;
+import org.objectweb.asm.tree.analysis.OriginalSourceValue;
+import uwu.narumi.deobfuscator.api.asm.ClassWrapper;
+import uwu.narumi.deobfuscator.api.transformer.FramedInstructionsTransformer;
+
+import java.util.stream.Stream;
+
+public class UselessPopCleanTransformer extends FramedInstructionsTransformer {
+
+ @Override
+ protected Stream getInstructionsStream(Stream stream) {
+ return stream
+ .filter(insn -> insn.getOpcode() == POP);
+ }
+
+ @Override
+ protected boolean transformInstruction(ClassWrapper classWrapper, MethodNode methodNode, AbstractInsnNode insn, Frame frame) {
+ OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1);
+ for (AbstractInsnNode producer : sourceValue.insns) {
+ // If the producer is a constant, remove the pop and the constant
+ if (producer.isConstant()) {
+ methodNode.instructions.remove(producer);
+ methodNode.instructions.remove(insn);
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineLocalVariablesTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineLocalVariablesTransformer.java
index cb8e3f7..3b2e173 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineLocalVariablesTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineLocalVariablesTransformer.java
@@ -36,8 +36,6 @@ private void inlineLocalVariables(ClassWrapper classWrapper, MethodNode methodNo
Map> frames = analyzeOriginalSource(classWrapper.getClassNode(), methodNode);
if (frames == null) return;
- List toRemoveInsns = new ArrayList<>();
-
// Inline static local variables
for (AbstractInsnNode insn : methodNode.instructions.toArray()) {
if (insn.getOpcode() == ILOAD) {
@@ -59,19 +57,9 @@ private void inlineLocalVariables(ClassWrapper classWrapper, MethodNode methodNo
AbstractInsnNode clone = valueInsn.clone(null);
methodNode.instructions.set(insn, clone);
- if (!toRemoveInsns.contains(storeVarSourceValue.getProducer())) {
- toRemoveInsns.add(storeVarSourceValue.getProducer());
- }
- if (!toRemoveInsns.contains(valueSourceValue.getProducer())) {
- toRemoveInsns.add(valueSourceValue.getProducer());
- }
-
changed = true;
}
}
}
-
- // Cleanup
- toRemoveInsns.forEach(methodNode.instructions::remove);
}
}
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineStaticFieldTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineStaticFieldTransformer.java
index ac25eff..a22d9b0 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineStaticFieldTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/pool/InlineStaticFieldTransformer.java
@@ -100,8 +100,6 @@ protected boolean transform(ClassWrapper scope, Context context) throws Exceptio
}
});
- List toRemove = new ArrayList<>();
-
LOGGER.info("Collected {} numbers from {} classes", index.get(), context.classes().size());
context.classes(scope).stream()
.flatMap(classWrapper -> classWrapper.methods().stream())
@@ -133,20 +131,9 @@ protected boolean transform(ClassWrapper scope, Context context) throws Exceptio
methodNode.instructions.set(node, getNumber(character));
}
- FieldRef fieldRef = new FieldRef(node.owner, node.name, node.desc);
- if (!toRemove.contains(fieldRef)) {
- toRemove.add(fieldRef);
- }
-
inline.incrementAndGet();
}));
- // Cleanup
- toRemove.forEach(fieldRef -> {
- ClassWrapper owner = context.get(fieldRef.owner).get();
- owner.fields().removeIf(fieldNode -> fieldNode.name.equals(fieldRef.name) && fieldNode.desc.equals(fieldRef.desc));
- });
-
// values.clear();
LOGGER.info("Inlined {} numbers in {} classes", inline.get(), context.classes().size());
diff --git a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathOperationsTransformer.java b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathOperationsTransformer.java
index 160358c..3efb8e9 100644
--- a/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathOperationsTransformer.java
+++ b/deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/MathOperationsTransformer.java
@@ -36,7 +36,15 @@ protected boolean transformInstruction(ClassWrapper classWrapper, MethodNode met
Number value1 = value1Insn.asNumber();
Number value2 = value2Insn.asNumber();
- methodNode.instructions.set(insn, AsmHelper.getNumber(AsmMathHelper.mathOperation(value1, value2, insn.getOpcode())));
+ Number result;
+ try {
+ result = AsmMathHelper.mathOperation(value1, value2, insn.getOpcode());
+ } catch (ArithmeticException e) {
+ // Skip division by zero
+ return false;
+ }
+
+ methodNode.instructions.set(insn, AsmHelper.getNumber(result));
methodNode.instructions.remove(value1SourceValue.getProducer());
methodNode.instructions.remove(value2SourceValue.getProducer());
diff --git a/testData/results/custom-classes/FlowObfSample.dec b/testData/results/custom-classes/FlowObfSample.dec
index 2861acb..fb67904 100644
--- a/testData/results/custom-classes/FlowObfSample.dec
+++ b/testData/results/custom-classes/FlowObfSample.dec
@@ -3,6 +3,8 @@ import org.bukkit.event.Listener;
public class IIIIIIIIlIlIIIIlIIlIIIIIIIIlIlIllllIIIlIIIlIIllIllllIIIIllIIIIllIIllllIIIIllllIIIIIIIllIllIIIlllIlIIllIlIIllIlIIIIIlIIIIIllllIlIIllIllllIIlIIIIIIlIllIlIIIIlIlIIlIIlllIIIIlIIlIIllIlIIIlIlIlIIIIllIlllll
implements Listener {
+ static long llllIIIIlllIlllIlIllIIlllIlIIlIIlllIlllIlllIIIllIlIIIIlIIIllIlIIlIIlIllIIlIlIIIIIIllIlIlIlIlIIllIlIllIlIIIllllllIlIIllIIllIlIlIIlIllIllIIIlIIlllIIIIIIIlIIIIIIIllIlIIIIIIlIIlIlIIlllIIIlllllIIIIlIIIlIlI;
+
public static int lIIIIlllIlIIllIIIlllllIlllIIllIllIIlIIllIIIllIIlIllllIIIIllIIlIIIIlIllIIlllIllIllllIIIlllIllIllIlllIlllllIIlIIIlllllIIIlIlIllllIllllIllllIIlIIllIlIIllIIllIIlIllIIllllIlllIIIIlIIllIIIIIIIIllllIlIllllIl(
int var0
) {
@@ -67,7 +69,6 @@ public class IIIIIIIIlIlIIIIlIIlIIIIIIIIlIlIllllIIIlIIIlIIllIllllIIIIllIIIIllIIl
while (true) {
switch ((int)var10001) {
case -1254534848:
- byte var5 = -1;
var10001 = -6562666170258139970L;
break;
case -253412202:
@@ -75,15 +76,12 @@ public class IIIIIIIIlIlIIIIlIIlIIIIIIIIlIlIllllIIIlIIIlIIllIllllIIIIllIIIIllIIl
super();
return;
case 485227004:
- boolean var4 = true;
var10001 = 8779745570450297940L;
break;
case 1712111765:
- boolean var3 = true;
var10001 = -8855161723369167601L;
}
- boolean var6 = true;
var10001 /= 8016067872500700090L;
}
}
diff --git a/testData/results/java/TestInlineStaticFields.dec b/testData/results/java/TestInlineStaticFields.dec
new file mode 100644
index 0000000..d396e4c
--- /dev/null
+++ b/testData/results/java/TestInlineStaticFields.dec
@@ -0,0 +1,15 @@
+public class TestInlineStaticFields {
+ public static int TEST1 = 123;
+ public static String TEST2 = "placki";
+ public static boolean TEST3 = true;
+
+ public static void test() {
+ byte var10001 = 123;
+ String var10003 = "placki";
+ boolean var10005 = true;
+ }
+
+ public static void modifyStatic() {
+ TEST1 = 321;
+ }
+}
diff --git a/testData/results/java/TestSimpleFlowObfuscation.dec b/testData/results/java/TestSimpleFlowObfuscation.dec
index f8d5080..d14a946 100644
--- a/testData/results/java/TestSimpleFlowObfuscation.dec
+++ b/testData/results/java/TestSimpleFlowObfuscation.dec
@@ -1,8 +1,16 @@
public class TestSimpleFlowObfuscation {
public void testFlow() {
- int a = 3;
if (System.currentTimeMillis() == 123L) {
System.out.println("123");
}
}
+
+ public void compareTest() {
+ int a = 123;
+ System.out.println("a is not 100");
+
+ while (a * 321 == 100) {
+ a++;
+ }
+ }
}
diff --git a/testData/results/java/TestUniversalNumberTransformer.dec b/testData/results/java/TestUniversalNumberTransformer.dec
new file mode 100644
index 0000000..64cb9c4
--- /dev/null
+++ b/testData/results/java/TestUniversalNumberTransformer.dec
@@ -0,0 +1,17 @@
+public class TestUniversalNumberTransformer {
+ public void testNumbers1() {
+ int a = 3;
+ int b = 19;
+ double c = 1.5159303447561417E10;
+ float d = 9977.5625F;
+ System.out.println(1.5159313447123917E10);
+ }
+
+ public void divideByZero() {
+ int a = 2;
+ if (a == 0) {
+ int b = 9 / 0;
+ System.out.println(b);
+ }
+ }
+}
diff --git a/testData/src/java/src/main/java/TestInlineStaticFields.java b/testData/src/java/src/main/java/TestInlineStaticFields.java
new file mode 100644
index 0000000..df1965f
--- /dev/null
+++ b/testData/src/java/src/main/java/TestInlineStaticFields.java
@@ -0,0 +1,16 @@
+public class TestInlineStaticFields {
+ public static int TEST1 = 123;
+ public static String TEST2 = "placki";
+ public static boolean TEST3 = true;
+
+ public static void test() {
+ System.out.println(TEST1);
+ System.out.println(TEST2);
+ System.out.println(TEST3);
+ }
+
+ public static void modifyStatic() {
+ // TODO: Account for field modification
+ TEST1 = 321;
+ }
+}
diff --git a/testData/src/java/src/main/java/TestSimpleFlowObfuscation.java b/testData/src/java/src/main/java/TestSimpleFlowObfuscation.java
index c6590f1..074f2a4 100644
--- a/testData/src/java/src/main/java/TestSimpleFlowObfuscation.java
+++ b/testData/src/java/src/main/java/TestSimpleFlowObfuscation.java
@@ -30,4 +30,19 @@ public void testFlow() {
throw new RuntimeException();
}
}
+
+ public void compareTest() {
+ int a = 123;
+
+ if (a == 100) {
+ System.out.println("a is 100");
+ } else {
+ System.out.println("a is not 100");
+ }
+
+ // TODO: Simplify also this
+ while (a * 321 == 100) {
+ a += 1;
+ }
+ }
}
diff --git a/testData/src/java/src/main/java/TestUniversalNumberTransformer.java b/testData/src/java/src/main/java/TestUniversalNumberTransformer.java
new file mode 100644
index 0000000..654bc9a
--- /dev/null
+++ b/testData/src/java/src/main/java/TestUniversalNumberTransformer.java
@@ -0,0 +1,19 @@
+public class TestUniversalNumberTransformer {
+ public void testNumbers1() {
+ int a = 1 + 2;
+ int b = 235434535 / 12323432;
+ double c = 123123.123123 * 123123.123123;
+ float d = 123123.123123f / 12.34f;
+
+ System.out.println(a + b + c + d);
+ }
+
+ public void divideByZero() {
+ int a = 2;
+ if (a == 0) {
+ // Transformer shouldn't touch it
+ int b = 9 / 0;
+ System.out.println(b);
+ }
+ }
+}