diff --git a/src/main/java/info/sigterm/deob/Deob.java b/src/main/java/info/sigterm/deob/Deob.java index f27f86ef69..efeaaad804 100644 --- a/src/main/java/info/sigterm/deob/Deob.java +++ b/src/main/java/info/sigterm/deob/Deob.java @@ -1,7 +1,6 @@ package info.sigterm.deob; import info.sigterm.deob.deobfuscators.IllegalStateExceptions; -import info.sigterm.deob.deobfuscators.RenameUnique; import info.sigterm.deob.deobfuscators.RuntimeExceptions; import info.sigterm.deob.deobfuscators.UnusedBlocks; import info.sigterm.deob.deobfuscators.UnusedFields; @@ -9,6 +8,7 @@ import info.sigterm.deob.deobfuscators.UnusedMethods; import info.sigterm.deob.deobfuscators.UnusedParameters; import info.sigterm.deob.deobfuscators.ConstantParameter; import info.sigterm.deob.deobfuscators.MethodInliner; +import info.sigterm.deob.deobfuscators.UnreachedCode; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -36,57 +36,66 @@ public class Deob // bdur = System.currentTimeMillis() - bstart; // System.out.println("rename unique took " + bdur/1000L + " seconds"); -// // remove except RuntimeException -// bstart = System.currentTimeMillis(); -// new RuntimeExceptions().run(group); -// // the blocks of runtime exceptions may contain interesting things like other obfuscations we identify later, but now that -// // it can't be reached by the execution phase, those things become confused. so remove blocks here. -// new UnusedBlocks().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("runtime exception took " + bdur/1000L + " seconds"); -// -// // remove unused methods -// bstart = System.currentTimeMillis(); -// new UnusedMethods().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused methods took " + bdur/1000L + " seconds"); -// -// // remove illegal state exceptions, frees up some parameters -// bstart = System.currentTimeMillis(); -// new IllegalStateExceptions().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("illegal state exception took " + bdur/1000L + " seconds"); -// -// // remove constant logically dead parameters -// bstart = System.currentTimeMillis(); -// new ConstantParameter().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("constant param took " + bdur/1000L + " seconds"); -// -// // remove unhit blocks -// bstart = System.currentTimeMillis(); -// new UnusedBlocks().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused blocks took " + bdur/1000L + " seconds"); -// -// // remove unused parameters + // remove except RuntimeException + bstart = System.currentTimeMillis(); + new RuntimeExceptions().run(group); + // the blocks of runtime exceptions may contain interesting things like other obfuscations we identify later, but now that + // it can't be reached by the execution phase, those things become confused. so remove blocks here. + //new UnusedBlocks().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("runtime exception took " + bdur/1000L + " seconds"); + + // remove unused methods + bstart = System.currentTimeMillis(); + new UnusedMethods().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("unused methods took " + bdur/1000L + " seconds"); + + new UnreachedCode().run(group); + + // remove illegal state exceptions, frees up some parameters + bstart = System.currentTimeMillis(); + new IllegalStateExceptions().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("illegal state exception took " + bdur/1000L + " seconds"); + + // remove constant logically dead parameters + bstart = System.currentTimeMillis(); + new ConstantParameter().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("constant param took " + bdur/1000L + " seconds"); + + // remove unhit blocks + bstart = System.currentTimeMillis(); + new UnreachedCode().run(group); + //new UnusedBlocks().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("unused blocks took " + bdur/1000L + " seconds"); + + // remove unused parameters // bstart = System.currentTimeMillis(); // new UnusedParameters().run(group); // bdur = System.currentTimeMillis() - bstart; // System.out.println("unused params took " + bdur/1000L + " seconds"); -// -// // remove jump obfuscation -// //new Jumps().run(group); -// -// // remove unused fields -// bstart = System.currentTimeMillis(); -// new UnusedFields().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused fields took " + bdur/1000L + " seconds"); + + // remove jump obfuscation + //new Jumps().run(group); + + // remove unused fields + bstart = System.currentTimeMillis(); + new UnusedFields().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("unused fields took " + bdur/1000L + " seconds"); + + // remove unused methods, again? + bstart = System.currentTimeMillis(); + new UnusedMethods().run(group); + bdur = System.currentTimeMillis() - bstart; + System.out.println("unused methods took " + bdur/1000L + " seconds"); //new ModularArithmeticDeobfuscation().run(group); - new MethodInliner().run(group); + //new MethodInliner().run(group); saveJar(group, args[1]); diff --git a/src/main/java/info/sigterm/deob/Method.java b/src/main/java/info/sigterm/deob/Method.java index 7bbbe5c8b4..9ec3c93b53 100644 --- a/src/main/java/info/sigterm/deob/Method.java +++ b/src/main/java/info/sigterm/deob/Method.java @@ -18,6 +18,7 @@ import java.util.List; public class Method { public static final int ACC_STATIC = 0x8; + public static final int ACC_SYNCHRONIZED = 0x20; private Methods methods; @@ -79,6 +80,11 @@ public class Method return (accessFlags & ACC_STATIC) != 0; } + public boolean isSynchronized() + { + return (accessFlags & ACC_SYNCHRONIZED) != 0; + } + public Exceptions getExceptions() { return (Exceptions) attributes.findType(AttributeType.EXCEPTIONS); diff --git a/src/main/java/info/sigterm/deob/attributes/code/Exception.java b/src/main/java/info/sigterm/deob/attributes/code/Exception.java index 5f33b3c91a..d60753c3d2 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/Exception.java +++ b/src/main/java/info/sigterm/deob/attributes/code/Exception.java @@ -57,6 +57,11 @@ public class Exception return start; } + public void setStart(Instruction ins) + { + start = ins; + } + public Instruction getEnd() { return end; diff --git a/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java b/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java index ac03c094fb..da21a02ab0 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java @@ -78,7 +78,7 @@ public class IllegalStateExceptions implements Deobfuscator } if (!found) { - System.out.println("Unable to locate instruction ctx to remove stack for illegalstateexception " + ins + " in " + m); + System.out.println("Unable to locate instruction ctx to remove stack for illegalstateexception " + ins.getType().getName() + " in method " + m.getName() + " class " + m.getMethods().getClassFile().getName()); continue; } diff --git a/src/main/java/info/sigterm/deob/deobfuscators/MethodInliner.java b/src/main/java/info/sigterm/deob/deobfuscators/MethodInliner.java index b33eac4626..9663999a8f 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/MethodInliner.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/MethodInliner.java @@ -87,22 +87,36 @@ public class MethodInliner implements Deobfuscator assert m != invokedMethod; - // XXX do this later -// if (!invokedMethod.getDescriptor().getReturnValue().getType().equals("V") -// || invokedMethod.getDescriptor().size() != 0) -// { -// System.out.println(invokedMethod.getName()); -// continue; -// } int invokeIdx = ins.getInstructions().indexOf(i); assert invokeIdx != -1; - int lvtIndex = code.getMaxLocals(), startLvtIndex = lvtIndex; + int lvtIndex = code.getMaxLocals(), + //startLvtIndex = lvtIndex, + theirLocals = invokedMethod.getCode().getMaxLocals(); + + if (lvtIndex + theirLocals > 127) + continue; + + if (invokedMethod.isSynchronized()) + continue; + + if (!invokedMethod.getCode().getExceptions().getExceptions().isEmpty()) + continue; + // assign variables on stack to lvt Signature descriptor = invokedMethod.getDescriptor(); - for (int j = 0; j < descriptor.size(); ++j) + + Map lvtIndexes = new HashMap<>(); + for (int j = 0, idx = 0; j < descriptor.size(); ++j) + { + lvtIndexes.put(j, idx); + idx += descriptor.getTypeOfArg(j).getSlots(); + } + + for (int j = descriptor.size() - 1; j >= 0; --j) { Type type = descriptor.getTypeOfArg(j); + int paramLvtIndex = lvtIndexes.get(j); // insert instruction to store top of stack in lvt @@ -111,24 +125,25 @@ public class MethodInliner implements Deobfuscator { switch (type.getType()) { + case "B": case "Z": case "C": case "S": case "I": - storeIns = new IStore(ins, lvtIndex); - lvtIndex += type.getSlots(); + storeIns = new IStore(ins, lvtIndex + paramLvtIndex); + //lvtIndex += type.getSlots(); break; case "J": - storeIns = new LStore(ins, lvtIndex); - lvtIndex += type.getSlots(); + storeIns = new LStore(ins, lvtIndex + paramLvtIndex); + //lvtIndex += type.getSlots(); break; case "F": - storeIns = new FStore(ins, lvtIndex); - lvtIndex += type.getSlots(); + storeIns = new FStore(ins, lvtIndex + paramLvtIndex); + //lvtIndex += type.getSlots(); break; case "D": - storeIns = new DStore(ins, lvtIndex); - lvtIndex += type.getSlots(); + storeIns = new DStore(ins, lvtIndex + paramLvtIndex); + //lvtIndex += type.getSlots(); break; } } @@ -136,8 +151,8 @@ public class MethodInliner implements Deobfuscator if (type.getArrayDims() != 0 || type.getType().startsWith("L")) { assert storeIns == null; - storeIns = new AStore(ins, lvtIndex); - lvtIndex += type.getSlots(); + storeIns = new AStore(ins, lvtIndex + paramLvtIndex); + //lvtIndex += type.getSlots(); } assert storeIns != null; @@ -145,7 +160,7 @@ public class MethodInliner implements Deobfuscator ins.getInstructions().add(invokeIdx++, storeIns); } - inline(m, i, invokedMethod, startLvtIndex); + inline(m, i, invokedMethod, /*start*/lvtIndex); ++inlineCount; break; } @@ -236,7 +251,16 @@ public class MethodInliner implements Deobfuscator @Override public void run(ClassGroup group) { - while (pass(group) > 0); + int total = 0; + int i; + do + { + i = pass(group); + total += i; + } + while (i > 0); + + System.out.println("[TOTAL] Inlined " + total + " methods"); } private int pass(ClassGroup group) diff --git a/src/main/java/info/sigterm/deob/deobfuscators/UnreachedCode.java b/src/main/java/info/sigterm/deob/deobfuscators/UnreachedCode.java new file mode 100644 index 0000000000..640b71b3ed --- /dev/null +++ b/src/main/java/info/sigterm/deob/deobfuscators/UnreachedCode.java @@ -0,0 +1,85 @@ +package info.sigterm.deob.deobfuscators; + +import info.sigterm.deob.ClassFile; +import info.sigterm.deob.ClassGroup; +import info.sigterm.deob.Deobfuscator; +import info.sigterm.deob.Method; +import info.sigterm.deob.attributes.code.Instruction; +import info.sigterm.deob.attributes.code.Instructions; +import info.sigterm.deob.execution.Execution; +import java.util.ArrayList; +import java.util.List; + +public class UnreachedCode implements Deobfuscator +{ + private Execution execution; + + private int removeUnused(Method m) + { + Instructions ins = m.getCode().getInstructions(); + + int count = 0; + List insCopy = new ArrayList<>(ins.getInstructions()); + + for (int j = 0; j < insCopy.size(); ++j) + //for (Instruction i : new ArrayList<>(ins.getInstructions())) + { + Instruction i = insCopy.get(j); + + if (!execution.executed.contains(i)) + { + for (Instruction i2 : i.from) + i2.jump.remove(i); + i.from.clear(); // if this is never executed, anything that jumps here ia also never executed? + + // if this is an exception handler, the exception handler is never used... + for (info.sigterm.deob.attributes.code.Exception e : new ArrayList<>(m.getCode().getExceptions().getExceptions())) + { + if (e.getStart() == i) + { + e.setStart(insCopy.get(j + 1)); + + if (e.getStart() == e.getEnd()) + { + m.getCode().getExceptions().remove(e); + continue; + } + } + if (e.getHandler() == i) + { + m.getCode().getExceptions().remove(e); + } + } + + ins.remove(i); + ++count; + } + } + return count; + } + + @Override + public void run(ClassGroup group) + { + group.buildClassGraph(); + + execution = new Execution(group); + execution.populateInitialMethods(); + execution.run(); + + int count = 0; + + for (ClassFile cf : group.getClasses()) + { + for (Method m : cf.getMethods().getMethods()) + { + if (m.getCode() == null) + continue; + + count += removeUnused(m); + } + } + + System.out.println("Removed " + count + " unused instructions"); + } +} diff --git a/src/main/java/info/sigterm/deob/deobfuscators/UnusedBlocks.java b/src/main/java/info/sigterm/deob/deobfuscators/UnusedBlocks.java index 9357f5e068..a117f355f4 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/UnusedBlocks.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/UnusedBlocks.java @@ -21,7 +21,10 @@ public class UnusedBlocks implements Deobfuscator for (Method m : new ArrayList<>(methods)) { if (m.getCode() == null) + { + methods.remove(m); continue; + } Instructions ins = m.getCode().getInstructions(); ins.buildBlocks(); diff --git a/src/main/java/info/sigterm/deob/execution/Execution.java b/src/main/java/info/sigterm/deob/execution/Execution.java index 395286e371..ef8c71c3f1 100644 --- a/src/main/java/info/sigterm/deob/execution/Execution.java +++ b/src/main/java/info/sigterm/deob/execution/Execution.java @@ -4,6 +4,7 @@ import info.sigterm.deob.ClassFile; import info.sigterm.deob.ClassGroup; import info.sigterm.deob.Deob; import info.sigterm.deob.Method; +import info.sigterm.deob.attributes.code.Instruction; import java.util.ArrayList; import java.util.HashSet; @@ -17,6 +18,7 @@ public class Execution processedFrames = new ArrayList<>(); private List pendingMethods = new ArrayList<>(); // pending methods public Set methods = new HashSet<>(); // all methods + public Set executed = new HashSet<>(); // executed instructions public Execution(ClassGroup group) { diff --git a/src/main/java/info/sigterm/deob/execution/Frame.java b/src/main/java/info/sigterm/deob/execution/Frame.java index 5eedf6ad29..8db850b8cd 100644 --- a/src/main/java/info/sigterm/deob/execution/Frame.java +++ b/src/main/java/info/sigterm/deob/execution/Frame.java @@ -139,6 +139,8 @@ public class Frame throw ex; } + execution.executed.add(oldCur); + if (!executing) break;