diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java b/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java index a4dd108e..b3ef5562 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/Deobfuscator.java @@ -75,6 +75,13 @@ public Deobfuscator(Configuration configuration) { * Must enable for paramorphism obfuscated files. */ private static final boolean PARAMORPHISM = false; + + public List junkFiles = new ArrayList<>(); + + /** + * Must enable for paramorphism v2 obfuscated files. + */ + private static final boolean PARAMORPHISM_V2 = false; public ConstantPool getConstantPool(ClassNode classNode) { return this.constantPools.get(classNode); @@ -168,7 +175,21 @@ private boolean isClassIgnored(ClassNode classNode) { } private void loadInput() throws IOException { - if(PARAMORPHISM) + if(PARAMORPHISM_V2) + { + //Load folder "classes" + try(ZipFile zipIn = new ZipFile(configuration.getInput())) { + Enumeration e = zipIn.entries(); + while(e.hasMoreElements()) { + ZipEntry next = e.nextElement(); + if(next.isDirectory() && next.getName().endsWith(".class/")) { + byte[] data = IOUtils.toByteArray(zipIn.getInputStream(next)); + loadInput(next.getName().substring(0, next.getName().length() - 1), data); + }else if(!next.isDirectory() && next.getName().contains(".class/")) + junkFiles.add(next.getName()); + } + } + }else if(PARAMORPHISM) { Map classNameToName = new HashMap<>(); Map entries = new HashMap<>(); @@ -255,13 +276,19 @@ public void loadInput(String name, byte[] data) { classpath.put(node.name, node); } } catch (IllegalArgumentException x) { - logger.error("Could not parse {} (is it a class file?)", name, x); + if(PARAMORPHISM_V2) + invaildClasses.put(name, data); + else + logger.error("Could not parse {} (is it a class file?)", name, x); } catch (ArrayIndexOutOfBoundsException x) { - logger.error("Could not parse {} (is it a class file?)", name, x); + if(PARAMORPHISM_V2) + invaildClasses.put(name, data); + else + logger.error("Could not parse {} (is it a class file?)", name, x); } } - if (passthrough) { + if (passthrough && !junkFiles.contains(name)) { inputPassthrough.put(name, data); } } @@ -316,11 +343,11 @@ public void start() throws Throwable { logger.info("\t{}", message); logger.info("Recommend transformers:"); - Collection> recommended = rule.getRecommendTransformers(); + Collection>> recommended = rule.getRecommendTransformers(); if (recommended == null) { logger.info("\tNone"); } else { - for (Class transformer : recommended) { + for (Class> transformer : recommended) { logger.info("\t{}", transformer.getName()); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java index 327a0855..aa9a40be 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/defined/types/JavaMethodHandle.java @@ -16,7 +16,7 @@ package com.javadeobfuscator.deobfuscator.executor.defined.types; -public class JavaMethodHandle { +public class JavaMethodHandle extends JavaHandle { public final String clazz; public final String name; public final String desc; diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java index fb43a78d..4dd1daba 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/executor/values/JavaValue.java @@ -20,19 +20,13 @@ import com.google.common.primitives.Primitives; import com.javadeobfuscator.deobfuscator.executor.MethodExecutor; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaClass; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaConstantPool; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaConstructor; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaField; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaMethod; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaMethodHandle; -import com.javadeobfuscator.deobfuscator.executor.defined.types.JavaThread; +import com.javadeobfuscator.deobfuscator.executor.defined.types.*; import com.javadeobfuscator.deobfuscator.executor.exceptions.ExecutionException; public abstract class JavaValue { - - public boolean booleanValue() { - throw new ExecutionException(new UnsupportedOperationException()); + + public boolean booleanValue() { + throw new ExecutionException(new UnsupportedOperationException()); } public int intValue() { @@ -56,6 +50,9 @@ public Object value() { } public T as(Class clazz) { + //TODO: Fix this + if(value() instanceof JavaValue) + return ((JavaValue)value()).as(clazz); if (Primitives.unwrap(clazz) != clazz) { throw new ExecutionException("Cannot call as(Class clazz) with a primitive class"); } @@ -88,7 +85,7 @@ else if(cst.getClass().isArray()) return new JavaObject(cst, "java/lang/Object"); else if(cst instanceof JavaThread) return new JavaObject(cst, "java/lang/Thread"); - else if(cst instanceof JavaMethodHandle) + else if(cst instanceof JavaHandle) return new JavaObject(cst, "java/lang/invoke/MethodHandle"); else if(cst instanceof JavaMethod) return new JavaObject(cst, "java/lang/reflect/Method"); diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/Rule.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/Rule.java index e8eb8fb5..9683cdba 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/Rule.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/Rule.java @@ -27,5 +27,5 @@ public interface Rule extends Opcodes { String test(Deobfuscator deobfuscator); - Collection> getRecommendTransformers(); + Collection>> getRecommendTransformers(); } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleClassGuardPackage.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleClassGuardPackage.java index 402fc24a..639ec23a 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleClassGuardPackage.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleClassGuardPackage.java @@ -41,7 +41,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(EncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleEncryptedClass.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleEncryptedClass.java index 9513367b..5bce1388 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleEncryptedClass.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/classguard/RuleEncryptedClass.java @@ -41,7 +41,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(EncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/dasho/RuleStringDecryptor.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/dasho/RuleStringDecryptor.java index a0bd6d96..bb03a772 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/dasho/RuleStringDecryptor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/dasho/RuleStringDecryptor.java @@ -59,7 +59,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singleton(StringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/generic/RuleIllegalSignature.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/generic/RuleIllegalSignature.java index 49811429..5f279c5d 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/generic/RuleIllegalSignature.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/generic/RuleIllegalSignature.java @@ -64,7 +64,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(IllegalSignatureRemover.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/normalizer/RuleSourceFileAttribute.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/normalizer/RuleSourceFileAttribute.java index 83c9af30..6fd9f31f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/normalizer/RuleSourceFileAttribute.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/normalizer/RuleSourceFileAttribute.java @@ -66,7 +66,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(SourceFileClassNormalizer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic1.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic1.java index 1a41c334..d12ef3a6 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic1.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic1.java @@ -63,7 +63,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(Invokedynamic1Transformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic2.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic2.java index 3f48c715..3fb28e7f 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic2.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleInvokedynamic2.java @@ -64,7 +64,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(Invokedynamic2Transformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptor.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptor.java index de7fa23a..81fa5b30 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptor.java @@ -59,7 +59,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(StringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorV3.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorV3.java index 403d933f..d5a3622c 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorV3.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorV3.java @@ -60,7 +60,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singleton(StringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorWithThread.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorWithThread.java index c63d7f75..e90713e6 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorWithThread.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/stringer/RuleStringDecryptorWithThread.java @@ -60,7 +60,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(StringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleEnhancedStringEncryption.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleEnhancedStringEncryption.java index 163238fb..00ea50c2 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleEnhancedStringEncryption.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleEnhancedStringEncryption.java @@ -72,7 +72,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(EnhancedStringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleMethodParameterChangeStringEncryption.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleMethodParameterChangeStringEncryption.java index 0e32a812..ae4fdeca 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleMethodParameterChangeStringEncryption.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleMethodParameterChangeStringEncryption.java @@ -74,7 +74,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return null; } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleReflectionDecryptor.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleReflectionDecryptor.java index 8c381ef0..9bdf2543 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleReflectionDecryptor.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleReflectionDecryptor.java @@ -58,7 +58,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(ReflectionObfuscationTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSimpleStringEncryption.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSimpleStringEncryption.java index e9f5a7d9..231e3c9e 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSimpleStringEncryption.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSimpleStringEncryption.java @@ -42,7 +42,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return Collections.singletonList(SimpleStringEncryptionTransformer.class); } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSuspiciousClinit.java b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSuspiciousClinit.java index 0ef18146..e5f82eb9 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSuspiciousClinit.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/rules/zelix/RuleSuspiciousClinit.java @@ -57,7 +57,7 @@ public String test(Deobfuscator deobfuscator) { } @Override - public Collection> getRecommendTransformers() { + public Collection>> getRecommendTransformers() { return null; } } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FakeExceptionTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FakeExceptionTransformer.java new file mode 100644 index 00000000..692dcfc3 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FakeExceptionTransformer.java @@ -0,0 +1,55 @@ +package com.javadeobfuscator.deobfuscator.transformers.dasho; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.TryCatchBlockNode; + +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.Utils; + +public class FakeExceptionTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable { + System.out.println("[DashO] [FakeExceptionTransformer] Starting"); + AtomicInteger counter = new AtomicInteger(); + Set fakeExceptionClass = new HashSet<>(); + + classNodes().forEach(classNode -> { + classNode.methods.stream().filter(Utils::notAbstractOrNative).forEach(methodNode -> { + List remove = new ArrayList<>(); + for(TryCatchBlockNode tcbn : methodNode.tryCatchBlocks) + { + String handler = tcbn.type; + if(handler != null && classes.containsKey(handler)) + { + ClassNode handlerClass = classes.get(handler); + if(handlerClass.methods.size() == 0 && handlerClass.superName.equals("java/lang/RuntimeException")) + { + remove.add(tcbn); + fakeExceptionClass.add(handler); + counter.incrementAndGet(); + } + } + } + methodNode.tryCatchBlocks.removeIf(remove::contains); + }); + }); + + fakeExceptionClass.forEach(str -> { + classes.remove(str); + classpath.remove(str); + }); + + System.out.println("[DashO] [FakeExceptionTransformer] Removed " + counter.get() + " fake try-catch blocks"); + System.out.println("[DashO] [FakeExceptionTransformer] Removed " + fakeExceptionClass.size() + " fake exception classes"); + System.out.println("[DashO] [FakeExceptionTransformer] Done"); + return counter.get() > 0; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FlowObfuscationTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FlowObfuscationTransformer.java new file mode 100644 index 00000000..9f45dc75 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/FlowObfuscationTransformer.java @@ -0,0 +1,168 @@ +package com.javadeobfuscator.deobfuscator.transformers.dasho; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.assertj.core.internal.asm.Opcodes; +import org.objectweb.asm.tree.*; + +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.Utils; + +public class FlowObfuscationTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable { + System.out.println("[DashO] [FlowObfuscationTransformer] Starting"); + AtomicInteger counter = new AtomicInteger(); + + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + loop: + for(AbstractInsnNode ain : method.instructions.toArray()) + if(Utils.getIntValue(ain) == -1 && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.ISTORE + && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.LDC + && ((LdcInsnNode)ain.getNext().getNext()).cst.equals("0") + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.IINC + && ((IincInsnNode)ain.getNext().getNext().getNext()).incr == 1 + && ((IincInsnNode)ain.getNext().getNext().getNext()).var == ((VarInsnNode)ain.getNext()).var + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.ASTORE) + { + List remove = new ArrayList<>(); + Map replace = new HashMap<>(); + remove.add(ain); + remove.add(ain.getNext()); + remove.add(ain.getNext().getNext()); + remove.add(ain.getNext().getNext().getNext()); + remove.add(ain.getNext().getNext().getNext().getNext()); + int var1Index = ((VarInsnNode)ain.getNext()).var; + int var1Value = 0; + int var2Index = ((VarInsnNode)ain.getNext().getNext().getNext().getNext()).var; + int var2Value = 0; + + AbstractInsnNode next = ain.getNext().getNext().getNext().getNext(); + int count = 0; + while(true) + { + boolean found = false; + LabelNode lbl = null; + while(next != null && !(next instanceof LabelNode)) + { + if(Utils.getIntValue(next) == -1 && ain.getNext() != null + && next.getNext().getOpcode() == Opcodes.ISTORE + && next.getNext().getNext() != null + && next.getNext().getNext().getOpcode() == Opcodes.LDC + && ((LdcInsnNode)next.getNext().getNext()).cst.equals("0") + && next.getNext().getNext().getNext() != null + && next.getNext().getNext().getNext().getOpcode() == Opcodes.IINC + && ((IincInsnNode)next.getNext().getNext().getNext()).incr == 1 + && ((IincInsnNode)next.getNext().getNext().getNext()).var == ((VarInsnNode)next.getNext()).var + && next.getNext().getNext().getNext().getNext() != null + && next.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.ASTORE) + break; + if(next.getOpcode() == Opcodes.ALOAD && ((VarInsnNode)next).var == var2Index + && next.getNext() != null && next.getNext().getOpcode() == Opcodes.INVOKESTATIC + && ((MethodInsnNode)next.getNext()).name.equals("parseInt") + && ((MethodInsnNode)next.getNext()).owner.equals("java/lang/Integer") + && next.getNext().getNext() != null + && next.getNext().getNext().getOpcode() == Opcodes.TABLESWITCH + && ((TableSwitchInsnNode)next.getNext().getNext()).min == 0 + && ((TableSwitchInsnNode)next.getNext().getNext()).labels.size() == 1) + { + remove.add(next); + remove.add(next.getNext()); + found = true; + lbl = var2Value == 0 ? ((TableSwitchInsnNode)next.getNext().getNext()).labels.get(0) : + ((TableSwitchInsnNode)next.getNext().getNext()).dflt; + replace.put(next.getNext().getNext(), new JumpInsnNode(Opcodes.GOTO, lbl)); + next = lbl.getNext(); + break; + }else if(next.getOpcode() == Opcodes.ILOAD && ((VarInsnNode)next).var == var1Index + && next.getNext() != null && next.getNext().getOpcode() == Opcodes.TABLESWITCH + && ((TableSwitchInsnNode)next.getNext()).min == 0 + && ((TableSwitchInsnNode)next.getNext()).labels.size() == 1) + { + remove.add(next); + found = true; + lbl = var1Value == 0 ? ((TableSwitchInsnNode)next.getNext()).labels.get(0) : + ((TableSwitchInsnNode)next.getNext()).dflt; + replace.put(next.getNext(), new JumpInsnNode(Opcodes.GOTO, lbl)); + next = lbl.getNext(); + break; + } + next = next.getNext(); + } + if(!found) + break; + count++; + boolean found2 = false; + while(next != null && !(next instanceof LabelNode)) + { + if(next.getOpcode() == Opcodes.IINC && ((IincInsnNode)next).var == var1Index + && next.getNext() != null && next.getNext().getOpcode() == Opcodes.LDC + && next.getNext().getNext() != null + && next.getNext().getNext().getOpcode() == Opcodes.ASTORE + && ((VarInsnNode)next.getNext().getNext()).var == var2Index) + { + remove.add(next); + remove.add(next.getNext()); + remove.add(next.getNext().getNext()); + found2 = true; + var1Value += ((IincInsnNode)next).incr; + var2Value = Integer.parseInt((String)((LdcInsnNode)next.getNext()).cst); + if(next.getNext().getNext().getNext().getOpcode() == Opcodes.GOTO) + next = ((JumpInsnNode)next.getNext().getNext().getNext()).label.getNext(); + else if(next.getNext().getNext().getNext() instanceof LabelNode) + next = next.getNext().getNext().getNext().getNext(); + break; + }else if(Utils.isInteger(next) && next.getNext() != null + && next.getNext().getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)next.getNext()).var == var1Index + && next.getNext().getNext() != null + && next.getNext().getNext().getOpcode() == Opcodes.LDC + && next.getNext().getNext().getNext() != null + && next.getNext().getNext().getNext().getOpcode() == Opcodes.ASTORE + && ((VarInsnNode)next.getNext().getNext().getNext()).var == var2Index) + { + remove.add(next); + remove.add(next.getNext()); + remove.add(next.getNext().getNext()); + remove.add(next.getNext().getNext().getNext()); + found2 = true; + var1Value = Utils.getIntValue(next); + var2Value = Integer.parseInt((String)((LdcInsnNode)next.getNext().getNext()).cst); + if(next.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.GOTO) + next = ((JumpInsnNode)next.getNext().getNext().getNext().getNext()).label.getNext(); + else if(next.getNext().getNext().getNext().getNext() instanceof LabelNode) + next = next.getNext().getNext().getNext().getNext().getNext(); + break; + } + next = next.getNext(); + } + if(!found2) + continue loop; + } + + if(count > 0) + { + for(AbstractInsnNode a : remove) + method.instructions.remove(a); + for(Entry en : replace.entrySet()) + method.instructions.set(en.getKey(), en.getValue()); + counter.incrementAndGet(); + } + } + System.out.println("[DashO] [FlowObfuscationTransformer] Removed " + counter.get() + " flow obfuscated chunks"); + System.out.println("[DashO] [FlowObfuscationTransformer] Done"); + return counter.get() > 0; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/TamperObfuscationTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/TamperObfuscationTransformer.java new file mode 100644 index 00000000..78e64b26 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/dasho/TamperObfuscationTransformer.java @@ -0,0 +1,430 @@ +package com.javadeobfuscator.deobfuscator.transformers.dasho; + +import java.lang.reflect.Modifier; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.commons.lang3.tuple.Triple; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.*; + +import com.javadeobfuscator.deobfuscator.analyzer.FlowAnalyzer; +import com.javadeobfuscator.deobfuscator.analyzer.FlowAnalyzer.JumpData; +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.transformers.special.TryCatchFixer.TryCatchChain; + +public class TamperObfuscationTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable { + System.out.println("[DashO] [TamperObfuscationTransformer] Starting"); + AtomicInteger counter = new AtomicInteger(); + for(ClassNode classNode : classNodes()) + methodloop: + for(MethodNode method : classNode.methods) + { + AbstractInsnNode startingPoint = null; + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == Opcodes.ALOAD && ((VarInsnNode)ain).var == 0 && !Modifier.isStatic(method.access) + && ain.getNext() != null && ain.getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext()).name.equals("getClass") + && ((MethodInsnNode)ain.getNext()).owner.equals("java/lang/Object") + && ain.getNext().getNext() != null && ain.getNext().getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext().getNext()).name.equals("getProtectionDomain") + && ((MethodInsnNode)ain.getNext().getNext()).owner.equals("java/lang/Class") + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext().getNext().getNext()).name.equals("getCodeSource") + && ((MethodInsnNode)ain.getNext().getNext().getNext()).owner.equals("java/security/ProtectionDomain") + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext().getNext().getNext().getNext()).name.equals("getCertificates") + && ((MethodInsnNode)ain.getNext().getNext().getNext().getNext()).owner.equals("java/security/CodeSource") + && ain.getPrevious() != null && ain.getPrevious() instanceof LabelNode) + { + startingPoint = ain.getPrevious(); + break; + }else if(ain.getOpcode() == Opcodes.LDC && ((LdcInsnNode)ain).cst instanceof Type + && ((Type)((LdcInsnNode)ain).cst).getInternalName().equals(classNode.name) && Modifier.isStatic(method.access) + && ain.getNext() != null && ain.getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext()).name.equals("getProtectionDomain") + && ((MethodInsnNode)ain.getNext()).owner.equals("java/lang/Class") + && ain.getNext().getNext() != null && ain.getNext().getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext().getNext()).name.equals("getCodeSource") + && ((MethodInsnNode)ain.getNext().getNext()).owner.equals("java/security/ProtectionDomain") + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((MethodInsnNode)ain.getNext().getNext().getNext()).name.equals("getCertificates") + && ((MethodInsnNode)ain.getNext().getNext().getNext()).owner.equals("java/security/CodeSource") + && ain.getPrevious() != null && ain.getPrevious() instanceof LabelNode) + { + startingPoint = ain.getPrevious(); + break; + }else if(ain.getOpcode() == Opcodes.ACONST_NULL && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.ASTORE && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.LDC + && ((LdcInsnNode)ain.getNext().getNext()).cst.equals("java.lang.management.ManagementFactory") + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.INVOKESTATIC + && ((MethodInsnNode)ain.getNext().getNext().getNext()).name.equals("forName") + && ((MethodInsnNode)ain.getNext().getNext().getNext()).owner.equals("java/lang/Class") + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.ASTORE + && ain.getNext().getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getNext().getOpcode() == Opcodes.ALOAD + && ((VarInsnNode)ain.getNext().getNext().getNext().getNext().getNext()).var == + ((VarInsnNode)ain.getNext().getNext().getNext().getNext()).var + && ain.getNext().getNext().getNext().getNext().getNext().getNext().getOpcode() == Opcodes.LDC + && ((LdcInsnNode)ain.getNext().getNext().getNext().getNext().getNext().getNext()).cst.equals("getRuntimeMXBean") + && ain.getPrevious() != null && ain.getPrevious() instanceof LabelNode) + startingPoint = ain.getPrevious(); + } + if(startingPoint == null) + continue; + int mode = -1; + LabelNode jump = null; + LinkedHashMap> flowAnalysis = new FlowAnalyzer(method).analyze( + startingPoint, new ArrayList<>(), new HashMap<>(), false, true); + boolean verify1 = false; + boolean verify2 = false; + for(Entry> entry : flowAnalysis.entrySet()) + for(AbstractInsnNode ain : entry.getValue()) + if(ain.getOpcode() == Opcodes.ALOAD && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.INVOKESTATIC + && ((MethodInsnNode)ain.getNext()).owner.equals("java/util/Arrays") + && ((MethodInsnNode)ain.getNext()).name.equals("equals") + && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.IFNE) + { + jump = ((JumpInsnNode)ain.getNext().getNext()).label; + mode = 0; + }else if(ain instanceof LdcInsnNode && ((LdcInsnNode)ain).cst.equals("java.lang.management.ManagementFactory")) + verify1 = true; + else if(ain instanceof LdcInsnNode && ((LdcInsnNode)ain).cst.equals("getRuntimeMXBean")) + verify2 = true; + else if(verify1 && verify2 && ain.getOpcode() == Opcodes.ILOAD && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.ILOAD + && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.IF_ICMPGE) + { + LabelNode jumpLabel = ((JumpInsnNode)ain.getNext().getNext()).label; + if(jumpLabel != null && jumpLabel.getNext().getOpcode() == Opcodes.ILOAD + && jumpLabel.getNext().getNext() != null + && jumpLabel.getNext().getNext().getOpcode() == Opcodes.IFEQ) + { + jump = ((JumpInsnNode)jumpLabel.getNext().getNext()).label; + mode = 1; + break; + } + } + if(jump == null) + continue; + LinkedHashMap> flowAnalysis2 = new FlowAnalyzer(method).analyze( + startingPoint, Arrays.asList(jump), new HashMap<>(), false, true); + FlowAnalyzer.Result result = new FlowAnalyzer(method).analyze(); + boolean counted = false; + boolean specialCounted = false; + for(Entry, List>>> entry : result.labels.entrySet()) + for(Triple tri : entry.getValue().getValue()) + if(tri.getLeft() == jump) + if(counted) + { + if(mode == 1 && flowAnalysis2.containsKey(entry.getKey()) && !specialCounted) + { + specialCounted = true; + continue; + } + System.out.println("Warning: Unexpected multi-jump at " + classNode.name + ", method " + + method.name + method.desc); + continue methodloop; + }else + counted = true; + if(mode == 1 && !specialCounted) + continue methodloop; + for(Entry> entry : flowAnalysis2.entrySet()) + { + if(entry.getKey() != startingPoint) + method.instructions.remove(entry.getKey()); + for(AbstractInsnNode ain : entry.getValue()) + method.instructions.remove(ain); + } + method.instructions.insert(startingPoint, new JumpInsnNode(Opcodes.GOTO, jump)); + counter.incrementAndGet(); + if(method.tryCatchBlocks.isEmpty()) + continue methodloop; + //Recalcuate exception blocks + LinkedHashMap> resNow = new LinkedHashMap<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain instanceof LabelNode) + if(result.trycatchMap.containsKey(ain)) + resNow.put((LabelNode)ain, result.trycatchMap.get(ain)); + else + resNow.put((LabelNode)ain, new ArrayList<>()); + List chains = new ArrayList<>(); + Map> pass = new HashMap<>(); + List labels = new ArrayList<>(resNow.keySet()); + for(Entry> entry : resNow.entrySet()) + for(TryCatchBlockNode tcbn : entry.getValue()) + if(!pass.containsKey(entry.getKey()) || !pass.get(entry.getKey()).contains(tcbn)) + { + TryCatchChain chain = new TryCatchChain(tcbn.handler, tcbn.type, + tcbn.visibleTypeAnnotations, tcbn.visibleTypeAnnotations); + chains.add(chain); + chain.covered.add(entry.getKey()); + if(labels.indexOf(entry.getKey()) + 1 >= labels.size()) + { + LabelNode lbl = new LabelNode(); + labels.add(lbl); + method.instructions.add(lbl); + } + chain.end = labels.get(labels.indexOf(entry.getKey()) + 1); + pass.putIfAbsent(entry.getKey(), new ArrayList<>()); + pass.get(entry.getKey()).add(tcbn); + for(int i = labels.indexOf(entry.getKey()) + 1; i < labels.size(); i++) + { + List list = resNow.get(labels.get(i)); + boolean found = false; + for(TryCatchBlockNode tcbn2 : list) + if(tcbn.handler.equals(tcbn2.handler) + && ((tcbn.type == null && tcbn2.type == null) || tcbn.type.equals(tcbn2.type)) + && ((tcbn.visibleTypeAnnotations == null && tcbn2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(tcbn2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && tcbn2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(tcbn2.invisibleTypeAnnotations))) + { + chain.covered.add(labels.get(i)); + if(i + 1 >= labels.size()) + { + LabelNode lbl = new LabelNode(); + labels.add(lbl); + method.instructions.add(lbl); + } + chain.end = labels.get(i + 1); + pass.putIfAbsent(labels.get(i), new ArrayList<>()); + pass.get(labels.get(i)).add(tcbn2); + found = true; + break; + } + if(!found) + break; + } + } + Map> splits = new HashMap<>(); + for(int i = 0; i < chains.size(); i++) + for(int i2 = i + 1; i2 < chains.size(); i2++) + { + TryCatchChain chain1 = chains.get(i); + TryCatchChain chain2 = chains.get(i2); + LabelNode start = labels.indexOf(chain1.covered.get(0)) > labels.indexOf(chain2.covered.get(0)) + ? chain1.covered.get(0) : chain2.covered.get(0); + LabelNode end = labels.indexOf(chain1.end) > labels.indexOf(chain2.end) + ? chain2.end : chain1.end; + if(labels.indexOf(start) >= labels.indexOf(end)) + continue; + int index1 = -1; + for(int ii = 0; ii < resNow.get(start).size(); ii++) + { + TryCatchBlockNode tcbn = resNow.get(start).get(ii); + if(tcbn.handler.equals(chain1.handler) + && ((tcbn.type == null && chain1.type == null) || tcbn.type.equals(chain1.type)) + && ((tcbn.visibleTypeAnnotations == null && chain1.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain1.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain1.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain1.invisibleTypeAnnotations))) + { + index1 = ii; + break; + } + } + int index2 = -1; + for(int ii = 0; ii < resNow.get(start).size(); ii++) + { + TryCatchBlockNode tcbn = resNow.get(start).get(ii); + if(tcbn.handler.equals(chain2.handler) + && ((tcbn.type == null && chain2.type == null) || tcbn.type.equals(chain2.type)) + && ((tcbn.visibleTypeAnnotations == null && chain2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain2.invisibleTypeAnnotations))) + { + index2 = ii; + break; + } + } + boolean oneOnTop = index1 > index2; + index1 = -1; + index2 = -1; + for(int ii = labels.indexOf(start); ii < labels.indexOf(end); ii++) + { + LabelNode now = labels.get(ii); + for(int iii = 0; iii < resNow.get(now).size(); iii++) + { + TryCatchBlockNode tcbn = resNow.get(now).get(iii); + if(tcbn.handler.equals(chain1.handler) + && ((tcbn.type == null && chain1.type == null) || tcbn.type.equals(chain1.type)) + && ((tcbn.visibleTypeAnnotations == null && chain1.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain1.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain1.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain1.invisibleTypeAnnotations))) + { + index1 = iii; + break; + } + } + for(int iii = 0; iii < resNow.get(now).size(); iii++) + { + TryCatchBlockNode tcbn = resNow.get(now).get(iii); + if(tcbn.handler.equals(chain2.handler) + && ((tcbn.type == null && chain2.type == null) || tcbn.type.equals(chain2.type)) + && ((tcbn.visibleTypeAnnotations == null && chain2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain2.invisibleTypeAnnotations))) + { + index2 = iii; + break; + } + } + boolean oneOnTopTemp = index1 > index2; + if(oneOnTop != oneOnTopTemp) + { + splits.putIfAbsent(chain1, new HashSet<>()); + splits.get(chain1).add(now); + oneOnTop = oneOnTopTemp; + } + } + } + if(splits.size() > 0) + System.out.println("Irregular exception table at " + classNode.name + ", " + method.name + method.desc); + for(Entry> entry : splits.entrySet()) + { + List orderedSplits = new ArrayList<>(entry.getValue()); + orderedSplits.sort(new Comparator() + { + @Override + public int compare(LabelNode l1, LabelNode l2) + { + return Integer.valueOf(labels.indexOf(l1)).compareTo(labels.indexOf(l2)); + } + }); + List replacements = new ArrayList<>(); + replacements.add(entry.getKey()); + for(LabelNode l : orderedSplits) + { + int lIndex = labels.indexOf(l); + TryCatchChain toModify = null; + for(TryCatchChain ch : replacements) + if(labels.indexOf(ch.covered.get(0)) <= lIndex + && labels.indexOf(ch.covered.get(ch.covered.size() - 1)) >= lIndex) + { + toModify = ch; + break; + } + TryCatchChain split1 = new TryCatchChain(toModify.handler, + toModify.type, toModify.visibleTypeAnnotations, + toModify.invisibleTypeAnnotations); + for(LabelNode lbl : toModify.covered) + { + if(lbl == l) + break; + split1.covered.add(lbl); + } + split1.end = l; + TryCatchChain split2 = new TryCatchChain(toModify.handler, + toModify.type, toModify.visibleTypeAnnotations, + toModify.invisibleTypeAnnotations); + for(int iii = toModify.covered.indexOf(l); iii < toModify.covered.size(); iii++) + split2.covered.add(toModify.covered.get(iii)); + split2.end = toModify.end; + int toModifyIndex = replacements.indexOf(toModify); + replacements.set(toModifyIndex, split2); + replacements.add(toModifyIndex, split1); + } + int chainIndex = chains.indexOf(entry.getKey()); + chains.set(chainIndex, replacements.get(replacements.size() - 1)); + replacements.remove(replacements.size() - 1); + chains.addAll(chainIndex, replacements); + } + List exceptions = new ArrayList<>(); + boolean modified; + do + { + modified = false; + TryCatchChain remove = null; + for(TryCatchChain chain : chains) + { + boolean failed = false; + for(LabelNode lbl : chain.covered) + { + List list = resNow.get(lbl); + if(!(!list.isEmpty() && list.get(0).handler.equals(chain.handler) + && ((list.get(0).type == null && chain.type == null) || list.get(0).type.equals(chain.type)) + && ((list.get(0).visibleTypeAnnotations == null && chain.visibleTypeAnnotations == null) + || list.get(0).visibleTypeAnnotations.equals(chain.visibleTypeAnnotations)) + && ((list.get(0).invisibleTypeAnnotations == null && chain.invisibleTypeAnnotations == null) + || list.get(0).invisibleTypeAnnotations.equals(chain.invisibleTypeAnnotations)))) + { + failed = true; + break; + } + } + if(!failed) + { + TryCatchBlockNode tcbn = new TryCatchBlockNode(chain.covered.get(0), chain.end, + chain.handler, chain.type); + tcbn.visibleTypeAnnotations = chain.visibleTypeAnnotations; + tcbn.invisibleTypeAnnotations = tcbn.invisibleTypeAnnotations; + exceptions.add(tcbn); + remove = chain; + for(LabelNode lbl : chain.covered) + resNow.get(lbl).remove(0); + break; + } + } + if(remove != null) + { + modified = true; + chains.remove(remove); + } + }while(modified); + if(chains.size() > 0) + throw new IllegalStateException("Impossible exception table at " + classNode.name + ", " + method.name + method.desc); + + boolean same = true; + if(method.tryCatchBlocks.size() != exceptions.size()) + same = false; + if(same) + for(int i = 0; i < method.tryCatchBlocks.size(); i++) + { + TryCatchBlockNode tcbn1 = method.tryCatchBlocks.get(i); + TryCatchBlockNode tcbn2 = exceptions.get(i); + if(tcbn1.start != tcbn2.start) + same = false; + else if(tcbn1.end != tcbn2.end) + same = false; + else if(tcbn1.handler != tcbn2.handler) + same = false; + else if(!((tcbn1.type == null && tcbn2.type == null) || tcbn1.type.equals(tcbn2.type))) + same = false; + else if(!((tcbn1.invisibleTypeAnnotations == null && tcbn2.invisibleTypeAnnotations == null) + || tcbn1.invisibleTypeAnnotations.equals(tcbn2.invisibleTypeAnnotations))) + same = false; + else if(!((tcbn1.visibleTypeAnnotations == null && tcbn2.visibleTypeAnnotations == null) + || tcbn1.visibleTypeAnnotations.equals(tcbn2.visibleTypeAnnotations))) + same = false; + if(!same) + break; + } + if(!same) + counter.incrementAndGet(); + method.tryCatchBlocks = exceptions; + } + System.out.println("[DashO] [TamperObfuscationTransformer] Removed " + counter.get() + " anti-tamper calls"); + System.out.println("[DashO] [TamperObfuscationTransformer] Done"); + return counter.get() > 0; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/PeepholeOptimizer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/PeepholeOptimizer.java index f061ba6b..ace26416 100644 --- a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/PeepholeOptimizer.java +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/PeepholeOptimizer.java @@ -49,7 +49,6 @@ public boolean transform() throws Throwable { // PEEPHOLE_TRANSFORMERS.add(TrapHandlerMerger.class); // still experimental PEEPHOLE_TRANSFORMERS.add(GotoRearranger.class); PEEPHOLE_TRANSFORMERS.add(DeadCodeRemover.class); - PEEPHOLE_TRANSFORMERS.add(UnconditionalSwitchRemover.class); PEEPHOLE_TRANSFORMERS.add(LdcSwapInvokeSwapPopRemover.class); PEEPHOLE_TRANSFORMERS.add(ConstantFolder.class); } diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/UnconditionalSwitchRemover.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/UnconditionalSwitchRemover.java deleted file mode 100644 index fff8f94c..00000000 --- a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/general/peephole/UnconditionalSwitchRemover.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2016 Sam Sun - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.javadeobfuscator.deobfuscator.transformers.general.peephole; - -import com.javadeobfuscator.deobfuscator.config.TransformerConfig; -import com.javadeobfuscator.deobfuscator.transformers.Transformer; -import com.javadeobfuscator.deobfuscator.utils.Utils; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.tree.*; - -public class UnconditionalSwitchRemover extends Transformer { - - @Override - public boolean transform() throws Throwable { - AtomicInteger counter = new AtomicInteger(); - classNodes().forEach(classNode -> { - MethodNode clinit = classNode.methods.stream().filter(mn -> mn.name.equals("")).findFirst().orElse(null); - if (clinit != null) { - Map mapping = new HashMap<>(); - InsnList insns = clinit.instructions; - for (int i = 0; i < insns.size(); i++) { - AbstractInsnNode node = insns.get(i); - if (node instanceof LabelNode) { - mapping.put((LabelNode) node, (LabelNode) node); - } - } - for (int i = 0; i < insns.size(); i++) { - AbstractInsnNode node = insns.get(i); - int prev = Utils.iconstToInt(node.getOpcode()); - if (prev == Integer.MIN_VALUE) { - if (node.getOpcode() == Opcodes.BIPUSH || node.getOpcode() == Opcodes.SIPUSH) { - prev = ((IntInsnNode) node).operand; - } - } - if (prev == Integer.MIN_VALUE) { - if (node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof Integer) { - prev = (Integer) ((LdcInsnNode) node).cst; - } - } - if (prev != Integer.MIN_VALUE) { - AbstractInsnNode next = Utils.getNextFollowGoto(node); - if (next instanceof TableSwitchInsnNode) { - TableSwitchInsnNode cast = (TableSwitchInsnNode) next; - int index = prev - cast.min; - LabelNode go = null; - if (index >= 0 && index < cast.labels.size()) { - go = cast.labels.get(index); - } else { - go = cast.dflt; - } - InsnList replace = new InsnList(); - replace.add(new JumpInsnNode(Opcodes.GOTO, go)); - insns.insertBefore(node, replace); - insns.remove(node); - counter.incrementAndGet(); - } - } - } - } - }); - classNodes().forEach(classNode -> { - MethodNode clinit = classNode.methods.stream().filter(mn -> mn.name.equals("")).findFirst().orElse(null); - if (clinit != null) { - Map mapping = new HashMap<>(); - InsnList insns = clinit.instructions; - for (int i = 0; i < insns.size(); i++) { - AbstractInsnNode node = insns.get(i); - if (node instanceof LabelNode) { - mapping.put((LabelNode) node, (LabelNode) node); - } - } - for (int i = 0; i < insns.size(); i++) { - AbstractInsnNode node = insns.get(i); - int prev = Utils.iconstToInt(node.getOpcode()); - if (prev == Integer.MIN_VALUE) { - if (node.getOpcode() == Opcodes.BIPUSH || node.getOpcode() == Opcodes.SIPUSH) { - prev = ((IntInsnNode) node).operand; - } - } - if (prev == Integer.MIN_VALUE) { - if (node instanceof LdcInsnNode && ((LdcInsnNode) node).cst instanceof Integer) { - prev = (Integer) ((LdcInsnNode) node).cst; - } - } - if (prev != Integer.MIN_VALUE) { - AbstractInsnNode next = Utils.getNextFollowGoto(node); - if (next.getOpcode() == Opcodes.SWAP) { - next = Utils.getNextFollowGoto(next); - if (next.getOpcode() == Opcodes.INVOKESTATIC) { - AbstractInsnNode methodNode = next; - next = Utils.getNextFollowGoto(next); - if (next.getOpcode() == Opcodes.SWAP) { - next = Utils.getNextFollowGoto(next); - if (next instanceof TableSwitchInsnNode) { - TableSwitchInsnNode cast = (TableSwitchInsnNode) next; - int index = prev - cast.min; - LabelNode go = null; - if (index >= 0 && index < cast.labels.size()) { - go = cast.labels.get(index); - } else { - go = cast.dflt; - } - InsnList replace = new InsnList(); - replace.add(methodNode.clone(null)); - replace.add(new JumpInsnNode(Opcodes.GOTO, go)); - insns.insertBefore(node, replace); - insns.remove(node); - counter.incrementAndGet(); - } - } - } - } - } - } - } - }); - System.out.println("Removed " + counter.get() + " unconditional switches"); - return counter.get() > 0; - } -} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/FlowObfuscationTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/FlowObfuscationTransformer.java new file mode 100644 index 00000000..1e5ff2aa --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/FlowObfuscationTransformer.java @@ -0,0 +1,303 @@ +package com.javadeobfuscator.deobfuscator.transformers.special; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.AnalyzerException; +import org.objectweb.asm.tree.analysis.BasicInterpreter; +import org.objectweb.asm.tree.analysis.BasicValue; +import org.objectweb.asm.tree.analysis.Frame; +import org.objectweb.asm.tree.analysis.SourceInterpreter; +import org.objectweb.asm.tree.analysis.SourceValue; + +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.InstructionModifier; +import com.javadeobfuscator.deobfuscator.utils.Utils; + +public class FlowObfuscationTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable + { + AtomicInteger count = new AtomicInteger(); + System.out.println("[Special] [FlowObfuscationTransformer] Starting"); + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + { + boolean modified = false; + do + { + modified = false; + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == Opcodes.GOTO) + { + AbstractInsnNode a = Utils.getNext(ain); + AbstractInsnNode b = Utils.getNext(((JumpInsnNode)ain).label); + if(a == b) + { + method.instructions.remove(ain); + modified = true; + } + } + if(willPush(ain) && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.POP) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(willPush(ain) && ain.getNext() != null + && (willPush(ain.getNext()) || ain.getNext().getOpcode() == Opcodes.DUP) && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.POP2) + { + method.instructions.remove(ain.getNext().getNext()); + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(ain.getOpcode() == Opcodes.DUP && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.ACONST_NULL + && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.SWAP + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() >= Opcodes.ISTORE + && ain.getNext().getNext().getNext().getOpcode() <= Opcodes.ASTORE + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.POP + && ain.getNext().getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getNext().getOpcode() == ain.getNext().getNext().getNext().getOpcode() + && ((VarInsnNode)ain.getNext().getNext().getNext().getNext().getNext()).var + == ((VarInsnNode)ain.getNext().getNext().getNext()).var) + { + method.instructions.remove(ain.getNext().getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext()); + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(willPush(ain) && ain.getNext() != null + && willPush(ain.getNext()) + && ain.getNext().getNext() != null + && willPush(ain.getNext().getNext()) + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.SWAP + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.POP + && ain.getNext().getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getNext().getOpcode() == Opcodes.POP2) + { + method.instructions.remove(ain.getNext().getNext().getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext()); + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(willPush(ain) && ain.getNext() != null + && willPush(ain.getNext()) + && ain.getNext().getNext() != null + && ain.getNext().getNext().getOpcode() == Opcodes.SWAP + && ain.getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getOpcode() == Opcodes.POP + && ain.getNext().getNext().getNext().getNext() != null + && ain.getNext().getNext().getNext().getNext().getOpcode() == Opcodes.POP) + { + method.instructions.remove(ain.getNext().getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext()); + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(isEqual(ain) && ain.getNext().getNext() != null && ain.getNext().getNext().getOpcode() == Opcodes.SWAP + && ain.getNext().getNext().getNext() != null && ain.getNext().getNext().getNext().getOpcode() == Opcodes.POP) + { + method.instructions.remove(ain.getNext().getNext().getNext()); + method.instructions.remove(ain.getNext().getNext()); + method.instructions.remove(ain.getNext()); + modified = true; + count.getAndIncrement(); + } + if(ain.getOpcode() == Opcodes.INEG && ain.getNext() != null && ain.getNext().getOpcode() == Opcodes.INEG) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + count.getAndIncrement(); + } + if(Utils.isInteger(ain) && ain.getNext() != null) + { + int res = -1; + int value = Utils.getIntValue(ain); + switch(ain.getNext().getOpcode()) + { + case IFNE: + res = (value != 0) ? 1 : 0; + break; + case IFEQ: + res = (value == 0) ? 1 : 0; + break; + case IFLT: + res = (value < 0) ? 1 : 0; + break; + case IFGE: + res = (value >= 0) ? 1 : 0; + break; + case IFGT: + res = (value > 0) ? 1 : 0; + break; + case IFLE: + res = (value <= 0) ? 1 : 0; + break; + } + if(res == 1) + { + method.instructions.set(ain.getNext(), new JumpInsnNode(Opcodes.GOTO, ((JumpInsnNode)ain.getNext()).label)); + method.instructions.remove(ain); + modified = true; + }else if(res == 0) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + modified = true; + } + count.incrementAndGet(); + } + } + Frame[] frames; + try + { + frames = new Analyzer<>(new BasicInterpreter()).analyze(classNode.name, method); + }catch(AnalyzerException e) + { + continue; + } + InstructionModifier modifier = new InstructionModifier(); + for(int i = 0; i < method.instructions.size(); i++) + { + if (!Utils.isInstruction(method.instructions.get(i))) continue; + if (frames[i] != null) continue; + + modifier.remove(method.instructions.get(i)); + } + modifier.apply(method); + if(method.tryCatchBlocks != null) + { + method.tryCatchBlocks.removeIf(tryCatchBlockNode -> + (Utils.getNext(tryCatchBlockNode.start) == Utils.getNext(tryCatchBlockNode.end) + || tryCatchBlockNode.handler.getNext().getOpcode() == Opcodes.ATHROW)); + } + }while(modified); + } + for(ClassNode classNode : classes.values()) + for(MethodNode method : classNode.methods) + { + Map> frames = new HashMap<>(); + Map replace = new LinkedHashMap<>(); + try + { + Frame[] fr = new Analyzer<>(new SourceInterpreter()).analyze(classNode.name, method); + for(int i = 0; i < fr.length; i++) + { + Frame f = fr[i]; + frames.put(method.instructions.get(i), f); + } + }catch(AnalyzerException e) + { + oops("unexpected analyzer exception", e); + continue; + } + for(AbstractInsnNode ain : method.instructions.toArray()) + { + if(ain.getOpcode() == Opcodes.IADD || ain.getOpcode() == Opcodes.ISUB || ain.getOpcode() == Opcodes.IMUL + || ain.getOpcode() == Opcodes.IDIV || ain.getOpcode() == Opcodes.IREM || ain.getOpcode() == Opcodes.IXOR) + { + Frame f = frames.get(ain); + SourceValue arg1 = f.getStack(f.getStackSize() - 1); + SourceValue arg2 = f.getStack(f.getStackSize() - 2); + if(arg1.insns.size() != 1 || arg2.insns.size() != 1) + continue; + AbstractInsnNode a1 = arg1.insns.iterator().next(); + AbstractInsnNode a2 = arg2.insns.iterator().next(); + for(Entry entry : replace.entrySet()) + if(entry.getKey() == a1) + a1 = entry.getValue(); + else if(entry.getKey() == a2) + a2 = entry.getValue(); + if(Utils.isInteger(a1) && Utils.isInteger(a2)) + { + Integer resultValue; + if((resultValue = doMath(Utils.getIntValue(a1), Utils.getIntValue(a2), ain.getOpcode())) != null) + { + AbstractInsnNode newValue = Utils.getNumberInsn(resultValue); + replace.put(ain, newValue); + method.instructions.set(ain, newValue); + method.instructions.remove(a1); + method.instructions.remove(a2); + } + count.incrementAndGet(); + } + } + } + } + System.out.println("[Special] [FlowObfuscationTransformer] Fixed " + count + " chunks"); + return count.get() > 0; + } + + private Integer doMath(int value1, int value2, int opcode) + { + switch(opcode) + { + case IADD: + return value2 + value1; + case IDIV: + return value2 / value1; + case IREM: + return value2 % value1; + case ISUB: + return value2 - value1; + case IMUL: + return value2 * value1; + case IXOR: + return value2 ^ value1; + } + return null; + } + + private boolean isEqual(AbstractInsnNode ain) + { + if(ain.getNext() == null) + return false; + if(ain.getOpcode() == Opcodes.LDC && ain.getNext().getOpcode() == Opcodes.LDC + && ((LdcInsnNode)ain).cst.equals(((LdcInsnNode)ain.getNext()).cst)) + return true; + if(ain.getOpcode() >= Opcodes.ILOAD && ain.getOpcode() <= Opcodes.ALOAD + && ain.getNext().getOpcode() == ain.getOpcode() + && ((VarInsnNode)ain).var == ((VarInsnNode)ain.getNext()).var) + return true; + return false; + } + + private boolean willPush(AbstractInsnNode ain) + { + if(ain.getOpcode() == Opcodes.LDC && (((LdcInsnNode)ain).cst instanceof Long || ((LdcInsnNode)ain).cst instanceof Double)) + return false; + return (Utils.willPushToStack(ain.getOpcode()) || ain.getOpcode() == Opcodes.NEW) && ain.getOpcode() != Opcodes.GETSTATIC + && ain.getOpcode() != Opcodes.LLOAD && ain.getOpcode() != Opcodes.DLOAD; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/InvaildClassRemover.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/InvaildClassRemover.java new file mode 100644 index 00000000..cc182708 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/InvaildClassRemover.java @@ -0,0 +1,62 @@ +/* + * Copyright 2018 Sam Sun + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.javadeobfuscator.deobfuscator.transformers.special; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import com.javadeobfuscator.deobfuscator.config.*; +import com.javadeobfuscator.deobfuscator.transformers.*; + +public class InvaildClassRemover extends Transformer +{ + @Override + public boolean transform() + { + System.out.println("[Special] [InvaildClassRemover] Starting"); + List patterns = new ArrayList<>(); + if(getDeobfuscator().getConfig().getIgnoredClasses() != null) + for (String ignored : getDeobfuscator().getConfig().getIgnoredClasses()) { + Pattern pattern; + try { + pattern = Pattern.compile(ignored); + } catch (PatternSyntaxException e) { + logger.error("Error while compiling pattern for ignore statement {}", ignored, e); + continue; + } + patterns.add(pattern); + } + int before = getDeobfuscator().getInputPassthrough().size(); + getDeobfuscator().getInputPassthrough().entrySet().removeIf(e -> e.getKey().endsWith(".class") + && !hasClass(e.getKey(), patterns)); + int after = getDeobfuscator().getInputPassthrough().size(); + System.out.println("[Special] [InvaildClassRemover] Removed " + (before - after) + " classes"); + return before - after > 0; + } + + private boolean hasClass(String name, List patterns) + { + int start = name.lastIndexOf(".class"); + String newName = name.substring(0, start); + for(Pattern pattern : patterns) + if(pattern.matcher(newName).find()) + return true; + return false; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/TryCatchFixer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/TryCatchFixer.java new file mode 100644 index 00000000..b122de81 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/TryCatchFixer.java @@ -0,0 +1,308 @@ +package com.javadeobfuscator.deobfuscator.transformers.special; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.LabelNode; +import org.objectweb.asm.tree.MethodNode; +import org.objectweb.asm.tree.TryCatchBlockNode; +import org.objectweb.asm.tree.TypeAnnotationNode; + +import com.javadeobfuscator.deobfuscator.analyzer.FlowAnalyzer; +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; + +public class TryCatchFixer extends Transformer +{ + @Override + public boolean transform() throws Throwable + { + System.out.println("[Special] [TryCatchFixer] Starting"); + AtomicInteger count = new AtomicInteger(); + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + { + if(method.tryCatchBlocks.size() <= 0) + continue; + FlowAnalyzer.Result result = new FlowAnalyzer(method).analyze(); + List chains = new ArrayList<>(); + Map> pass = new HashMap<>(); + List labels = new ArrayList<>(result.trycatchMap.keySet()); + for(Entry> entry : result.trycatchMap.entrySet()) + for(TryCatchBlockNode tcbn : entry.getValue()) + if(!pass.containsKey(entry.getKey()) || !pass.get(entry.getKey()).contains(tcbn)) + { + TryCatchChain chain = new TryCatchChain(tcbn.handler, tcbn.type, + tcbn.visibleTypeAnnotations, tcbn.visibleTypeAnnotations); + chains.add(chain); + chain.covered.add(entry.getKey()); + chain.end = labels.get(labels.indexOf(entry.getKey()) + 1); + pass.putIfAbsent(entry.getKey(), new ArrayList<>()); + pass.get(entry.getKey()).add(tcbn); + for(int i = labels.indexOf(entry.getKey()) + 1; i < labels.size(); i++) + { + List list = result.trycatchMap.get(labels.get(i)); + boolean found = false; + for(TryCatchBlockNode tcbn2 : list) + if(tcbn.handler.equals(tcbn2.handler) + && ((tcbn.type == null && tcbn2.type == null) || tcbn.type.equals(tcbn2.type)) + && ((tcbn.visibleTypeAnnotations == null && tcbn2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(tcbn2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && tcbn2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(tcbn2.invisibleTypeAnnotations))) + { + chain.covered.add(labels.get(i)); + chain.end = labels.get(i + 1); + pass.putIfAbsent(labels.get(i), new ArrayList<>()); + pass.get(labels.get(i)).add(tcbn2); + found = true; + break; + } + if(!found) + break; + } + } + Map> splits = new HashMap<>(); + for(int i = 0; i < chains.size(); i++) + for(int i2 = i + 1; i2 < chains.size(); i2++) + { + TryCatchChain chain1 = chains.get(i); + TryCatchChain chain2 = chains.get(i2); + LabelNode start = labels.indexOf(chain1.covered.get(0)) > labels.indexOf(chain2.covered.get(0)) + ? chain1.covered.get(0) : chain2.covered.get(0); + LabelNode end = labels.indexOf(chain1.end) > labels.indexOf(chain2.end) + ? chain2.end : chain1.end; + if(labels.indexOf(start) >= labels.indexOf(end)) + continue; + int index1 = -1; + for(int ii = 0; ii < result.trycatchMap.get(start).size(); ii++) + { + TryCatchBlockNode tcbn = result.trycatchMap.get(start).get(ii); + if(tcbn.handler.equals(chain1.handler) + && ((tcbn.type == null && chain1.type == null) || tcbn.type.equals(chain1.type)) + && ((tcbn.visibleTypeAnnotations == null && chain1.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain1.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain1.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain1.invisibleTypeAnnotations))) + { + index1 = ii; + break; + } + } + int index2 = -1; + for(int ii = 0; ii < result.trycatchMap.get(start).size(); ii++) + { + TryCatchBlockNode tcbn = result.trycatchMap.get(start).get(ii); + if(tcbn.handler.equals(chain2.handler) + && ((tcbn.type == null && chain2.type == null) || tcbn.type.equals(chain2.type)) + && ((tcbn.visibleTypeAnnotations == null && chain2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain2.invisibleTypeAnnotations))) + { + index2 = ii; + break; + } + } + boolean oneOnTop = index1 > index2; + index1 = -1; + index2 = -1; + for(int ii = labels.indexOf(start); ii < labels.indexOf(end); ii++) + { + LabelNode now = labels.get(ii); + for(int iii = 0; iii < result.trycatchMap.get(now).size(); iii++) + { + TryCatchBlockNode tcbn = result.trycatchMap.get(now).get(iii); + if(tcbn.handler.equals(chain1.handler) + && ((tcbn.type == null && chain1.type == null) || tcbn.type.equals(chain1.type)) + && ((tcbn.visibleTypeAnnotations == null && chain1.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain1.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain1.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain1.invisibleTypeAnnotations))) + { + index1 = iii; + break; + } + } + for(int iii = 0; iii < result.trycatchMap.get(now).size(); iii++) + { + TryCatchBlockNode tcbn = result.trycatchMap.get(now).get(iii); + if(tcbn.handler.equals(chain2.handler) + && ((tcbn.type == null && chain2.type == null) || tcbn.type.equals(chain2.type)) + && ((tcbn.visibleTypeAnnotations == null && chain2.visibleTypeAnnotations == null) + || tcbn.visibleTypeAnnotations.equals(chain2.visibleTypeAnnotations)) + && ((tcbn.invisibleTypeAnnotations == null && chain2.invisibleTypeAnnotations == null) + || tcbn.invisibleTypeAnnotations.equals(chain2.invisibleTypeAnnotations))) + { + index2 = iii; + break; + } + } + boolean oneOnTopTemp = index1 > index2; + if(oneOnTop != oneOnTopTemp) + { + splits.putIfAbsent(chain1, new HashSet<>()); + splits.get(chain1).add(now); + oneOnTop = oneOnTopTemp; + } + } + } + if(splits.size() > 0) + System.out.println("Irregular exception table at " + classNode.name + ", " + method.name + method.desc); + for(Entry> entry : splits.entrySet()) + { + List orderedSplits = new ArrayList<>(entry.getValue()); + orderedSplits.sort(new Comparator() + { + @Override + public int compare(LabelNode l1, LabelNode l2) + { + return Integer.valueOf(labels.indexOf(l1)).compareTo(labels.indexOf(l2)); + } + }); + List replacements = new ArrayList<>(); + replacements.add(entry.getKey()); + for(LabelNode l : orderedSplits) + { + int lIndex = labels.indexOf(l); + TryCatchChain toModify = null; + for(TryCatchChain ch : replacements) + if(labels.indexOf(ch.covered.get(0)) <= lIndex + && labels.indexOf(ch.covered.get(ch.covered.size() - 1)) >= lIndex) + { + toModify = ch; + break; + } + TryCatchChain split1 = new TryCatchChain(toModify.handler, + toModify.type, toModify.visibleTypeAnnotations, + toModify.invisibleTypeAnnotations); + for(LabelNode lbl : toModify.covered) + { + if(lbl == l) + break; + split1.covered.add(lbl); + } + split1.end = l; + TryCatchChain split2 = new TryCatchChain(toModify.handler, + toModify.type, toModify.visibleTypeAnnotations, + toModify.invisibleTypeAnnotations); + for(int iii = toModify.covered.indexOf(l); iii < toModify.covered.size(); iii++) + split2.covered.add(toModify.covered.get(iii)); + split2.end = toModify.end; + int toModifyIndex = replacements.indexOf(toModify); + replacements.set(toModifyIndex, split2); + replacements.add(toModifyIndex, split1); + } + int chainIndex = chains.indexOf(entry.getKey()); + chains.set(chainIndex, replacements.get(replacements.size() - 1)); + replacements.remove(replacements.size() - 1); + chains.addAll(chainIndex, replacements); + } + List exceptions = new ArrayList<>(); + boolean modified; + do + { + modified = false; + TryCatchChain remove = null; + for(TryCatchChain chain : chains) + { + boolean failed = false; + for(LabelNode lbl : chain.covered) + { + List list = result.trycatchMap.get(lbl); + if(!(!list.isEmpty() && list.get(0).handler.equals(chain.handler) + && ((list.get(0).type == null && chain.type == null) || list.get(0).type.equals(chain.type)) + && ((list.get(0).visibleTypeAnnotations == null && chain.visibleTypeAnnotations == null) + || list.get(0).visibleTypeAnnotations.equals(chain.visibleTypeAnnotations)) + && ((list.get(0).invisibleTypeAnnotations == null && chain.invisibleTypeAnnotations == null) + || list.get(0).invisibleTypeAnnotations.equals(chain.invisibleTypeAnnotations)))) + { + failed = true; + break; + } + } + if(!failed) + { + TryCatchBlockNode tcbn = new TryCatchBlockNode(chain.covered.get(0), chain.end, + chain.handler, chain.type); + tcbn.visibleTypeAnnotations = chain.visibleTypeAnnotations; + tcbn.invisibleTypeAnnotations = tcbn.invisibleTypeAnnotations; + exceptions.add(tcbn); + remove = chain; + for(LabelNode lbl : chain.covered) + result.trycatchMap.get(lbl).remove(0); + break; + } + } + if(remove != null) + { + modified = true; + chains.remove(remove); + } + }while(modified); + if(chains.size() > 0) + throw new IllegalStateException("Impossible exception table at " + classNode.name + ", " + method.name + method.desc); + + boolean same = true; + if(method.tryCatchBlocks.size() != exceptions.size()) + same = false; + if(same) + for(int i = 0; i < method.tryCatchBlocks.size(); i++) + { + TryCatchBlockNode tcbn1 = method.tryCatchBlocks.get(i); + TryCatchBlockNode tcbn2 = exceptions.get(i); + if(tcbn1.start != tcbn2.start) + same = false; + else if(tcbn1.end != tcbn2.end) + same = false; + else if(tcbn1.handler != tcbn2.handler) + same = false; + else if(!((tcbn1.type == null && tcbn2.type == null) || tcbn1.type.equals(tcbn2.type))) + same = false; + else if(!((tcbn1.invisibleTypeAnnotations == null && tcbn2.invisibleTypeAnnotations == null) + || tcbn1.invisibleTypeAnnotations.equals(tcbn2.invisibleTypeAnnotations))) + same = false; + else if(!((tcbn1.visibleTypeAnnotations == null && tcbn2.visibleTypeAnnotations == null) + || tcbn1.visibleTypeAnnotations.equals(tcbn2.visibleTypeAnnotations))) + same = false; + if(!same) + break; + } + if(!same) + count.incrementAndGet(); + method.tryCatchBlocks = exceptions; + } + System.out.println("[Special] [TryCatchFixer] Fixed " + count + " methods"); + System.out.println("[Special] [TryCatchFixer] Done"); + return count.get() > 0; + } + + public static class TryCatchChain + { + public List covered; + public LabelNode end; + public LabelNode handler; + public String type; + public List visibleTypeAnnotations; + public List invisibleTypeAnnotations; + + public TryCatchChain(LabelNode handler, String type, List visibleTypeAnnotations, + List invisibleTypeAnnotations) + { + this.handler = handler; + this.type = type; + this.visibleTypeAnnotations = visibleTypeAnnotations; + this.invisibleTypeAnnotations = invisibleTypeAnnotations; + covered = new ArrayList<>(); + } + } +} + \ No newline at end of file diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/UselessArithmeticTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/UselessArithmeticTransformer.java new file mode 100644 index 00000000..5c5d4264 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/UselessArithmeticTransformer.java @@ -0,0 +1,265 @@ +package com.javadeobfuscator.deobfuscator.transformers.special; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; + +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.Utils; + +public class UselessArithmeticTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable + { + System.out.println("[Special] [UselessArithmeticTransformer] Starting"); + AtomicInteger count = new AtomicInteger(); + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + for(AbstractInsnNode ain : method.instructions.toArray()) + if(Utils.isInteger(ain) && Utils.getIntValue(ain) == 0 && ain.getNext() != null + && (ain.getNext().getOpcode() == Opcodes.IADD || ain.getNext().getOpcode() == Opcodes.ISUB)) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + count.incrementAndGet(); + } + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + { + List iincsToRemove = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + { + List possibleInsns = getPossibleInsns(ain); + if(possibleInsns != null) + { + iincsToRemove.add((IincInsnNode)possibleInsns.get(possibleInsns.size() - 1)); + possibleInsns.forEach(a -> method.instructions.remove(a)); + count.incrementAndGet(); + } + } + for(IincInsnNode iinc : iincsToRemove) + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain.getOpcode() == Opcodes.ICONST_0 && ain.getNext() != null + && ain.getNext().getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)ain.getNext()).var == iinc.var) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + break; + } + } + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + for(AbstractInsnNode ain : method.instructions.toArray()) + { + List possibleInsns = getPossibleInsns2(ain); + if(possibleInsns != null) + { + possibleInsns.forEach(a -> method.instructions.remove(a)); + count.incrementAndGet(); + } + } + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + for(AbstractInsnNode ain : method.instructions.toArray()) + { + List possibleInsns = getPossibleInsns3(ain); + if(possibleInsns != null) + { + for(int i = 1; i < possibleInsns.size(); i++) + method.instructions.remove(possibleInsns.get(i)); + method.instructions.set(possibleInsns.get(0), new InsnNode(Opcodes.ICONST_0)); + count.incrementAndGet(); + } + } + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + for(AbstractInsnNode ain : method.instructions.toArray()) + { + List possibleInsns = getPossibleInsns4(ain); + if(possibleInsns != null) + { + for(int i = 1; i < possibleInsns.size(); i++) + method.instructions.remove(possibleInsns.get(i)); + method.instructions.set(possibleInsns.get(0), new InsnNode(Opcodes.ICONST_0)); + count.incrementAndGet(); + } + } + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + for(AbstractInsnNode ain : method.instructions.toArray()) + if(Utils.isInteger(ain) && Utils.getIntValue(ain) == 0 && ain.getNext() != null + && (ain.getNext().getOpcode() == Opcodes.IADD || ain.getNext().getOpcode() == Opcodes.ISUB)) + { + method.instructions.remove(ain.getNext()); + method.instructions.remove(ain); + } + System.out.println("[Special] [UselessArithmeticTransformer] Removed " + count + " instruction sets"); + System.out.println("[Special] [UselessArithmeticTransformer] Done"); + return count.get() > 0; + } + + private List getPossibleInsns(AbstractInsnNode ain) + { + List instrs = new ArrayList<>(); + while(ain != null) + { + if(ain instanceof LineNumberNode || ain instanceof FrameNode) + { + ain = ain.getNext(); + continue; + } + instrs.add(ain); + if(instrs.size() >= 23) + break; + ain = ain.getNext(); + } + if(instrs.size() == 23 && instrs.get(0).getOpcode() == Opcodes.ILOAD + && instrs.get(1).getOpcode() == Opcodes.ICONST_1 + && instrs.get(2).getOpcode() == Opcodes.ISUB + && instrs.get(3).getOpcode() == Opcodes.ISTORE) + { + int var1 = ((VarInsnNode)instrs.get(0)).var; + int var2 = ((VarInsnNode)instrs.get(3)).var; + if(instrs.get(4).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(4)).var == var1 + && instrs.get(5).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(5)).var == var2 + && instrs.get(6).getOpcode() == Opcodes.IMUL + && instrs.get(7).getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)instrs.get(7)).var == var2 + && instrs.get(8).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(8)).var == var2 + && instrs.get(9).getOpcode() == Opcodes.ICONST_2 + && instrs.get(10).getOpcode() == Opcodes.IREM + && instrs.get(11).getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)instrs.get(11)).var == var2 + && instrs.get(12).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(12)).var == var2 + && instrs.get(13).getOpcode() == Opcodes.I2L + && instrs.get(14).getOpcode() == Opcodes.ICONST_0 + && instrs.get(15).getOpcode() == Opcodes.I2L + && instrs.get(16).getOpcode() == Opcodes.LCMP + && instrs.get(17).getOpcode() == Opcodes.ICONST_1 + && instrs.get(18).getOpcode() == Opcodes.IXOR + && instrs.get(19).getOpcode() == Opcodes.ICONST_1 + && instrs.get(20).getOpcode() == Opcodes.IAND + && instrs.get(21).getOpcode() == Opcodes.IFEQ + && instrs.get(22).getOpcode() == Opcodes.IINC) + return instrs; + } + return null; + } + + private List getPossibleInsns2(AbstractInsnNode ain) + { + List instrs = new ArrayList<>(); + while(ain != null) + { + if(ain instanceof LineNumberNode || ain instanceof FrameNode) + { + ain = ain.getNext(); + continue; + } + instrs.add(ain); + if(instrs.size() >= 14) + break; + ain = ain.getNext(); + } + if(instrs.size() == 14 && instrs.get(0).getOpcode() == Opcodes.ILOAD + && instrs.get(1).getOpcode() == Opcodes.ICONST_1 + && instrs.get(2).getOpcode() == Opcodes.ISUB + && instrs.get(3).getOpcode() == Opcodes.ISTORE) + { + int var1 = ((VarInsnNode)instrs.get(0)).var; + int var2 = ((VarInsnNode)instrs.get(3)).var; + if(instrs.get(4).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(4)).var == var1 + && instrs.get(5).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(5)).var == var2 + && instrs.get(6).getOpcode() == Opcodes.IMUL + && instrs.get(7).getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)instrs.get(7)).var == var2 + && instrs.get(8).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(8)).var == var2 + && instrs.get(9).getOpcode() == Opcodes.ICONST_2 + && instrs.get(10).getOpcode() == Opcodes.IREM + && instrs.get(11).getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)instrs.get(11)).var == var2 + && instrs.get(12).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(12)).var == var2 + && instrs.get(13).getOpcode() == Opcodes.IADD) + return instrs; + } + return null; + } + + private List getPossibleInsns3(AbstractInsnNode ain) + { + List instrs = new ArrayList<>(); + while(ain != null) + { + if(ain instanceof LineNumberNode || ain instanceof FrameNode) + { + ain = ain.getNext(); + continue; + } + instrs.add(ain); + if(instrs.size() >= 7) + break; + ain = ain.getNext(); + } + if(instrs.size() == 7 && instrs.get(0).getOpcode() == Opcodes.ILOAD + && instrs.get(1).getOpcode() == Opcodes.ILOAD + && instrs.get(2).getOpcode() == Opcodes.ICONST_1 + && instrs.get(3).getOpcode() == Opcodes.ISUB + && instrs.get(4).getOpcode() == Opcodes.IMUL + && instrs.get(5).getOpcode() == Opcodes.ICONST_2 + && instrs.get(6).getOpcode() == Opcodes.IREM) + return instrs; + return null; + } + + private List getPossibleInsns4(AbstractInsnNode ain) + { + List instrs = new ArrayList<>(); + while(ain != null) + { + if(ain instanceof LineNumberNode || ain instanceof FrameNode) + { + ain = ain.getNext(); + continue; + } + instrs.add(ain); + if(instrs.size() >= 11) + break; + ain = ain.getNext(); + } + if(instrs.size() == 11 && instrs.get(0).getOpcode() == Opcodes.ILOAD + && instrs.get(1).getOpcode() == Opcodes.ICONST_1 + && instrs.get(2).getOpcode() == Opcodes.ISUB + && instrs.get(3).getOpcode() == Opcodes.ISTORE) + { + int var1 = ((VarInsnNode)instrs.get(0)).var; + int var2 = ((VarInsnNode)instrs.get(3)).var; + if(instrs.get(4).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(4)).var == var1 + && instrs.get(5).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(5)).var == var2 + && instrs.get(6).getOpcode() == Opcodes.IMUL + && instrs.get(7).getOpcode() == Opcodes.ISTORE + && ((VarInsnNode)instrs.get(7)).var == var2 + && instrs.get(8).getOpcode() == Opcodes.ILOAD + && ((VarInsnNode)instrs.get(8)).var == var2 + && instrs.get(9).getOpcode() == Opcodes.ICONST_2 + && instrs.get(10).getOpcode() == Opcodes.IREM) + return instrs; + } + return null; + } +} diff --git a/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/WrappedLocalsTransformer.java b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/WrappedLocalsTransformer.java new file mode 100644 index 00000000..97326079 --- /dev/null +++ b/src/main/java/com/javadeobfuscator/deobfuscator/transformers/special/WrappedLocalsTransformer.java @@ -0,0 +1,289 @@ +package com.javadeobfuscator.deobfuscator.transformers.special; + +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.tree.*; +import org.objectweb.asm.tree.analysis.Analyzer; +import org.objectweb.asm.tree.analysis.AnalyzerException; +import org.objectweb.asm.tree.analysis.Frame; +import org.objectweb.asm.tree.analysis.SourceInterpreter; +import org.objectweb.asm.tree.analysis.SourceValue; + +import com.javadeobfuscator.deobfuscator.analyzer.ArgsAnalyzer; +import com.javadeobfuscator.deobfuscator.analyzer.FlowAnalyzer; +import com.javadeobfuscator.deobfuscator.config.TransformerConfig; +import com.javadeobfuscator.deobfuscator.transformers.Transformer; +import com.javadeobfuscator.deobfuscator.utils.Utils; + +public class WrappedLocalsTransformer extends Transformer +{ + @Override + public boolean transform() throws Throwable + { + System.out.println("[Special] [WrappedLocalsTransformer] Starting"); + AtomicInteger count = new AtomicInteger(); + AtomicInteger methods = new AtomicInteger(); + for(ClassNode classNode : classNodes()) + for(MethodNode method : classNode.methods) + { + Set loadInts = new HashSet<>(); + Set loadFloats = new HashSet<>(); + Set loadLongs = new HashSet<>(); + Set loadDoubles = new HashSet<>(); + Set loads = new HashSet<>(); + Map> frames = new HashMap<>(); + try + { + Frame[] fr = new Analyzer<>(new SourceInterpreter()).analyze(classNode.name, method); + for(int i = 0; i < fr.length; i++) + { + Frame f = fr[i]; + frames.put(method.instructions.get(i), f); + } + }catch(AnalyzerException e) + { + oops("unexpected analyzer exception", e); + continue; + } + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain.getOpcode() == Opcodes.ASTORE) + { + SourceValue s = frames.get(ain).getStack(frames.get(ain).getStackSize() - 1); + if(s.insns.size() == 1 && s.insns.iterator().next().getOpcode() == Opcodes.NEW) + { + AbstractInsnNode a = s.insns.iterator().next(); + if(((TypeInsnNode)a).desc.equals("java/lang/Integer")) + loadInts.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Float")) + loadFloats.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Long")) + loadLongs.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Double")) + loadDoubles.add(((VarInsnNode)ain).var); + }else if(s.insns.size() == 1 && (s.insns.iterator().next().getOpcode() == Opcodes.DUP_X1 + || s.insns.iterator().next().getOpcode() == Opcodes.DUP_X2)) + { + SourceValue s1 = frames.get(s.insns.iterator().next()).getStack(frames.get( + s.insns.iterator().next()).getStackSize() - 1); + if(s1.insns.size() == 1 && s1.insns.iterator().next().getOpcode() == Opcodes.NEW) + { + AbstractInsnNode a = s1.insns.iterator().next(); + if(((TypeInsnNode)a).desc.equals("java/lang/Integer")) + loadInts.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Float")) + loadFloats.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Long")) + loadLongs.add(((VarInsnNode)ain).var); + else if(((TypeInsnNode)a).desc.equals("java/lang/Double")) + loadDoubles.add(((VarInsnNode)ain).var); + } + } + } + loads.addAll(loadInts); + loads.addAll(loadFloats); + loads.addAll(loadLongs); + loads.addAll(loadDoubles); + if(loads.isEmpty()) + continue; + //Load, List + Map> loadMap = new HashMap<>(); + //Store, List + Map> storeMap = new HashMap<>(); + for(Integer var : loads) + { + List vars = new ArrayList<>(); + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain.getOpcode() == Opcodes.ASTORE && ((VarInsnNode)ain).var == var) + vars.add(ain); + for(AbstractInsnNode ain : vars) + { + List excluded = new ArrayList<>(vars); + excluded.remove(ain); + LinkedHashMap> map = new FlowAnalyzer(method). + analyze(ain, excluded, new HashMap<>(), false, true); + storeMap.put((VarInsnNode)ain, new ArrayList<>()); + for(Entry> entry : map.entrySet()) + for(AbstractInsnNode a : entry.getValue()) + if(a.getOpcode() == Opcodes.ALOAD && ((VarInsnNode)a).var == var) + { + loadMap.putIfAbsent((VarInsnNode)a, new ArrayList<>()); + loadMap.get(a).add((VarInsnNode)ain); + storeMap.get(ain).add((VarInsnNode)a); + } + } + } + Map succeededStores = new HashMap<>(); + for(VarInsnNode store : storeMap.keySet()) + { + if(succeededStores.containsKey(store)) + continue; + Map tried = new HashMap<>(); + if(!isFail(method, store, frames, loadMap, storeMap, tried, -1)) + succeededStores.putAll(tried); + } + TreeSet doubleWidth = new TreeSet<>(); + for(VarInsnNode store : succeededStores.keySet()) + { + //Revert the store itself + if(store.getPrevious().getOpcode() == Opcodes.INVOKESPECIAL + && store.getPrevious().getPrevious() != null + && store.getPrevious().getPrevious().getOpcode() == Opcodes.POP + && store.getPrevious().getPrevious().getPrevious() != null + && (store.getPrevious().getPrevious().getPrevious().getOpcode() == Opcodes.DUP_X1 + || store.getPrevious().getPrevious().getPrevious().getOpcode() == Opcodes.DUP_X2) + && store.getPrevious().getPrevious().getPrevious().getPrevious() != null + && (store.getPrevious().getPrevious().getPrevious().getPrevious().getOpcode() == Opcodes.DUP_X1 + || store.getPrevious().getPrevious().getPrevious().getPrevious().getOpcode() == Opcodes.DUP_X2) + && store.getPrevious().getPrevious().getPrevious().getPrevious().getPrevious() != null + && store.getPrevious().getPrevious().getPrevious().getPrevious().getPrevious().getOpcode() == Opcodes.NEW) + { + method.instructions.remove(store.getPrevious().getPrevious().getPrevious().getPrevious().getPrevious()); + method.instructions.remove(store.getPrevious().getPrevious().getPrevious().getPrevious()); + method.instructions.remove(store.getPrevious().getPrevious().getPrevious()); + method.instructions.remove(store.getPrevious().getPrevious()); + method.instructions.remove(store.getPrevious()); + }else + { + SourceValue v = frames.get(store).getStack(frames.get(store).getStackSize() - 1); + AbstractInsnNode newOpcode = v.insns.iterator().next(); + method.instructions.remove(Utils.getNext(newOpcode)); + method.instructions.remove(newOpcode); + method.instructions.remove(Utils.getPrevious(store)); + } + switch(succeededStores.get(store)) + { + case 0: + store.setOpcode(Opcodes.ISTORE); + break; + case 1: + store.setOpcode(Opcodes.FSTORE); + break; + case 2: + store.setOpcode(Opcodes.LSTORE); + doubleWidth.add(store.var); + break; + case 3: + store.setOpcode(Opcodes.DSTORE); + doubleWidth.add(store.var); + break; + } + //Revert all loads + for(VarInsnNode load : storeMap.get(store)) + if(load.getOpcode() == Opcodes.ALOAD) + { + ArgsAnalyzer.Result res = new ArgsAnalyzer(load.getNext(), 1, ArgsAnalyzer.Mode.FORWARDS).lookupArgs(); + method.instructions.remove(res.getFirstArgInsn()); + switch(succeededStores.get(store)) + { + case 0: + load.setOpcode(Opcodes.ILOAD); + break; + case 1: + load.setOpcode(Opcodes.FLOAD); + break; + case 2: + load.setOpcode(Opcodes.LLOAD); + break; + case 3: + load.setOpcode(Opcodes.DLOAD); + break; + } + } + } + if(!doubleWidth.isEmpty()) + { + //Calculate new locals (ASM will calculate new stack size) + Map oldToNewLocals = new HashMap<>(); + for(int i = 0; i < method.maxLocals; i++) + oldToNewLocals.put(i, i); + for(Integer i : doubleWidth) + for(Entry entry : oldToNewLocals.entrySet()) + if(entry.getKey() > i) + entry.setValue(entry.getValue() + 1); + for(AbstractInsnNode ain : method.instructions.toArray()) + if(ain instanceof VarInsnNode) + ((VarInsnNode)ain).var = oldToNewLocals.get(((VarInsnNode)ain).var); + else if(ain instanceof IincInsnNode) + ((IincInsnNode)ain).var = oldToNewLocals.get(((IincInsnNode)ain).var); + } + if(succeededStores.size() > 0) + { + count.getAndAdd(succeededStores.size()); + methods.incrementAndGet(); + } + } + System.out.println("[Special] [WrappedLocalsTransformer] Cleaned " + count + " locals"); + System.out.println("[Special] [WrappedLocalsTransformer] Fixed " + methods + " methods"); + System.out.println("[Special] [WrappedLocalsTransformer] Done"); + return count.get() > 0; + } + + private boolean isFail(MethodNode method, VarInsnNode store, Map> frames, + Map> loadMap, Map> storeMap, Map tried, int varType) + { + if(tried.containsKey(store)) + return false; + //Check if this store loads wrapped number + int type = -1; + SourceValue s = frames.get(store).getStack(frames.get(store).getStackSize() - 1); + if(s.insns.size() == 1 && s.insns.iterator().next().getOpcode() == Opcodes.NEW) + { + AbstractInsnNode a = s.insns.iterator().next(); + if(((TypeInsnNode)a).desc.equals("java/lang/Integer")) + type = 0; + else if(((TypeInsnNode)a).desc.equals("java/lang/Float")) + type = 1; + else if(((TypeInsnNode)a).desc.equals("java/lang/Long")) + type = 2; + else if(((TypeInsnNode)a).desc.equals("java/lang/Double")) + type = 3; + }else if(s.insns.size() == 1 && (s.insns.iterator().next().getOpcode() == Opcodes.DUP_X1 + || s.insns.iterator().next().getOpcode() == Opcodes.DUP_X2)) + { + SourceValue s1 = frames.get(s.insns.iterator().next()).getStack(frames.get( + s.insns.iterator().next()).getStackSize() - 1); + if(s1.insns.size() == 1 && s1.insns.iterator().next().getOpcode() == Opcodes.NEW) + { + AbstractInsnNode a = s1.insns.iterator().next(); + if(((TypeInsnNode)a).desc.equals("java/lang/Integer")) + type = 0; + else if(((TypeInsnNode)a).desc.equals("java/lang/Float")) + type = 1; + else if(((TypeInsnNode)a).desc.equals("java/lang/Long")) + type = 2; + else if(((TypeInsnNode)a).desc.equals("java/lang/Double")) + type = 3; + } + } + if(type == -1 || (varType != -1 && type != varType)) + return true; + tried.put(store, type); + //Now check all the loads + for(VarInsnNode load : storeMap.get(store)) + { + //Check if load is followed by unboxing + boolean unbox = false; + ArgsAnalyzer.Result res = new ArgsAnalyzer(load.getNext(), 1, ArgsAnalyzer.Mode.FORWARDS).lookupArgs(); + if(!(res instanceof ArgsAnalyzer.FailedResult) && res.getFirstArgInsn().getOpcode() == Opcodes.INVOKEVIRTUAL + && ((((MethodInsnNode)res.getFirstArgInsn()).owner.equals("java/lang/Integer") + && ((MethodInsnNode)res.getFirstArgInsn()).name.equals("intValue") && type == 0) + || (((MethodInsnNode)res.getFirstArgInsn()).owner.equals("java/lang/Float") + && ((MethodInsnNode)res.getFirstArgInsn()).name.equals("floatValue") && type == 1) + || (((MethodInsnNode)res.getFirstArgInsn()).owner.equals("java/lang/Long") + && ((MethodInsnNode)res.getFirstArgInsn()).name.equals("longValue") && type == 2) + || (((MethodInsnNode)res.getFirstArgInsn()).owner.equals("java/lang/Double") + && ((MethodInsnNode)res.getFirstArgInsn()).name.equals("doubleValue") && type == 3))) + unbox = true; + if(!unbox) + return true; + //Go through loadmap and check all stores there + for(VarInsnNode otherStore : loadMap.get(load)) + if(otherStore != store && isFail(method, otherStore, frames, loadMap, storeMap, tried, type)) + return true; + } + return false; + } +}