From 516fa7805fec150f50a618e82604146cb13cc09f Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 1 Aug 2015 21:35:02 -0400 Subject: [PATCH] I dont know if this works or is correct but it runs? --- .../instruction/types/JumpingInstruction.java | 5 + .../attributes/code/instructions/Goto.java | 8 + .../attributes/code/instructions/GotoW.java | 8 + .../deob/attributes/code/instructions/If.java | 7 +- .../attributes/code/instructions/If0.java | 7 +- .../code/instructions/LookupSwitch.java | 11 ++ .../code/instructions/TableSwitch.java | 10 + .../deob/deobfuscators/ConstantParameter.java | 180 ++++++++++++++++-- .../deobfuscators/IllegalStateExceptions.java | 12 +- .../sigterm/deob/execution/Execution.java | 2 - 10 files changed, 223 insertions(+), 27 deletions(-) diff --git a/src/main/java/info/sigterm/deob/attributes/code/instruction/types/JumpingInstruction.java b/src/main/java/info/sigterm/deob/attributes/code/instruction/types/JumpingInstruction.java index 03bd521bcd..dcbd6f23dc 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instruction/types/JumpingInstruction.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instruction/types/JumpingInstruction.java @@ -1,6 +1,11 @@ package info.sigterm.deob.attributes.code.instruction.types; +import info.sigterm.deob.attributes.code.Instruction; +import java.util.List; + public interface JumpingInstruction { public void buildJumpGraph(); + + List getJumps(); } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/Goto.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/Goto.java index 325b07dbb5..e01a056893 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/Goto.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/Goto.java @@ -9,6 +9,8 @@ import info.sigterm.deob.execution.Frame; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.List; public class Goto extends Instruction implements JumpingInstruction { @@ -71,4 +73,10 @@ public class Goto extends Instruction implements JumpingInstruction if (to == oldi) to = newi; } + + @Override + public List getJumps() + { + return Arrays.asList(to); + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/GotoW.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/GotoW.java index f01c31f3ee..05f996b58b 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/GotoW.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/GotoW.java @@ -9,6 +9,8 @@ import info.sigterm.deob.execution.Frame; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.List; public class GotoW extends Instruction implements JumpingInstruction { @@ -61,4 +63,10 @@ public class GotoW extends Instruction implements JumpingInstruction if (to == oldi) to = newi; } + + @Override + public List getJumps() + { + return Arrays.asList(to); + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/If.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/If.java index b689fb309f..3538ecb99b 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/If.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/If.java @@ -13,6 +13,8 @@ import info.sigterm.deob.execution.StackContext; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.List; public class If extends Instruction implements JumpingInstruction, ComparisonInstruction { @@ -71,8 +73,9 @@ public class If extends Instruction implements JumpingInstruction, ComparisonIns to = newi; } - public Instruction getTo() + @Override + public List getJumps() { - return to; + return Arrays.asList(to); } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java index 5a10f75ee9..f0f111c716 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/If0.java @@ -13,6 +13,8 @@ import info.sigterm.deob.execution.StackContext; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.List; public class If0 extends Instruction implements JumpingInstruction, ComparisonInstruction { @@ -70,8 +72,9 @@ public class If0 extends Instruction implements JumpingInstruction, ComparisonIn to = newi; } - public Instruction getTo() + @Override + public List getJumps() { - return to; + return Arrays.asList(to); } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/LookupSwitch.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/LookupSwitch.java index ad63129169..0b83e21ccc 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/LookupSwitch.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/LookupSwitch.java @@ -13,6 +13,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class LookupSwitch extends Instruction implements JumpingInstruction @@ -132,4 +133,14 @@ public class LookupSwitch extends Instruction implements JumpingInstruction if (branchi.get(i) == oldi) branchi.set(i, newi); } + + @Override + public List getJumps() + { + List list = new ArrayList<>(); + for (Instruction i : branchi) + list.add(i); + list.add(defi); + return list; + } } diff --git a/src/main/java/info/sigterm/deob/attributes/code/instructions/TableSwitch.java b/src/main/java/info/sigterm/deob/attributes/code/instructions/TableSwitch.java index 890e131c48..cfed4fc342 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/instructions/TableSwitch.java +++ b/src/main/java/info/sigterm/deob/attributes/code/instructions/TableSwitch.java @@ -129,4 +129,14 @@ public class TableSwitch extends Instruction implements JumpingInstruction if (branchi.get(i) == oldi) branchi.set(i, newi); } + + @Override + public List getJumps() + { + List list = new ArrayList<>(); + for (Instruction i : branchi) + list.add(i); + list.add(defi); + return list; + } } diff --git a/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java b/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java index 3f54675cf2..a6038727ce 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java @@ -4,10 +4,13 @@ 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.attributes.code.instruction.types.ComparisonInstruction; import info.sigterm.deob.attributes.code.instruction.types.InvokeInstruction; +import info.sigterm.deob.attributes.code.instruction.types.JumpingInstruction; import info.sigterm.deob.attributes.code.instruction.types.LVTInstruction; import info.sigterm.deob.attributes.code.instruction.types.PushConstantInstruction; +import info.sigterm.deob.attributes.code.instructions.Goto; import info.sigterm.deob.attributes.code.instructions.If; import info.sigterm.deob.attributes.code.instructions.If0; import info.sigterm.deob.execution.Execution; @@ -16,8 +19,11 @@ import info.sigterm.deob.execution.InstructionContext; import info.sigterm.deob.execution.StackContext; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; @@ -85,6 +91,7 @@ class MethodGroup public class ConstantParameter implements Deobfuscator { private List parameters = new ArrayList<>(); + private MultiValueMap nonconst = new MultiValueMap<>(); // methods with non const parameters //private MultiValueMap parameters = new MultiValueMap<>(); // methods can be in more than one group because of multiple inheritance with interfaces //private MultiValueMap methodGroups = new MultiValueMap<>(); @@ -142,8 +149,28 @@ public class ConstantParameter implements Deobfuscator if (c.equals(cmp)) continue outer; + if (method.getMethods().getClassFile().getName().equals("gy") && method.getName().equals("y")) + { + int i = 5; + } + parameters.add(cmp); } + else + { + nonconst.put(method, parameterIndex); + + // remove from parameters as is not always const + for (Iterator it = parameters.iterator(); it.hasNext();) + { + ConstantMethodParameter c = it.next(); + + if (c.methods.equals(methods) && c.lvtIndex == lvtOffset) + { + it.remove();; + } + } + } } } @@ -247,9 +274,37 @@ public class ConstantParameter implements Deobfuscator } } - private boolean isLogicallyDead(Execution execution, Method method, int paramIndex, int lvtIndex, Object value) + private static class LogicallyDeadOp + { + InstructionContext compCtx; // logically dead comparison + boolean branch; // branch which is always taken + + @Override + public boolean equals(Object obj) + { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final LogicallyDeadOp other = (LogicallyDeadOp) obj; + if (!Objects.equals(this.compCtx.getInstruction(), other.compCtx.getInstruction())) { + return false; + } + if (this.branch != other.branch) { + return false; + } + return true; + } + + + } + + private List isLogicallyDead(Execution execution, Method method, int lvtIndex, Object value) { Boolean branch = null; + List ops = new ArrayList<>(); // find if instruction // one side must be constant, other must be parameterIndex @@ -263,6 +318,7 @@ public class ConstantParameter implements Deobfuscator for (InstructionContext ins : frame.getInstructions()) { + // XXX this should look for field accesses and see hwats done with it. // if (ins.getInstruction() instanceof LVTInstruction) // { // LVTInstruction lvtins = (LVTInstruction) ins.getInstruction(); @@ -296,14 +352,17 @@ public class ConstantParameter implements Deobfuscator // find if one is a lvt ins LVTInstruction lvt = null; + StackContext other = null; if (one.getPushed().getInstruction() instanceof LVTInstruction) { lvt = (LVTInstruction) one.getPushed().getInstruction(); + other = two; } else if (two != null && two.getPushed().getInstruction() instanceof LVTInstruction) { lvt = (LVTInstruction) two.getPushed().getInstruction(); + other = one; } assert lvt == null || !lvt.store(); @@ -315,43 +374,94 @@ public class ConstantParameter implements Deobfuscator if (two != null) // two is null for if0 { - if (!(two.getPushed().getInstruction() instanceof PushConstantInstruction)) + if (!(other.getPushed().getInstruction() instanceof PushConstantInstruction)) continue; - PushConstantInstruction pc = (PushConstantInstruction) two.getPushed().getInstruction(); + PushConstantInstruction pc = (PushConstantInstruction) other.getPushed().getInstruction(); otherValue = pc.getConstant().getObject(); } - // results must all be the same - boolean logicallyDead = doLogicalComparison(value, comp, otherValue); - if (branch == null) - branch = logicallyDead; - else if (branch != logicallyDead) - return false; + // the result of the comparison doesn't matter, only that it always goes the same direction for every invocation + boolean result = doLogicalComparison(value, comp, otherValue); + /*if (branch == null) + branch = result; + else if (branch != result) + return null;*/ + + LogicallyDeadOp deadOp = new LogicallyDeadOp(); + deadOp.compCtx = ins; + deadOp.branch = result; + ops.add(deadOp); //if (!logicallyDead) // return false; // if one frame finds it not logically dead, then stop } } - return branch != null ? true /* always logically taking the same branch */ : false /* totally unused XXX */; + return ops; + //return branch != null ? true /* always logically taking the same branch */ : false /* totally unused XXX */; //return true; } + private void removeLogicallyDead(Execution execution, Method method, int lvtIndex) + { + + } + + private Map > deadops = new HashMap<>(); + private Set invalidDeadops = new HashSet<>(); + private void findLogicallyDeadOperations(Execution execution) { outer: for (ConstantMethodParameter cmp : parameters) { +// Method method2 = cmp.methods.get(0); +// if (method2.getMethods().getClassFile().getName().equals("gy") && method2.getName().equals("y")) +// { +// int i = 5; +// } for (Method method : cmp.methods) { - boolean isDead = isLogicallyDead(execution, method, cmp.paramIndex, cmp.lvtIndex, cmp.value); - if (!isDead) - continue outer; + if (invalidDeadops.contains(method)) + continue; + + // the dead comparisons must be the same and branch the same way for every call to this method. + List deadOps = isLogicallyDead(execution, method, cmp.lvtIndex, cmp.value); + + List existing = deadops.get(method); + if (existing != null) + if (!existing.equals(deadOps)) + { + deadops.remove(method); + invalidDeadops.add(method); + continue; + } + else + { + continue; + } + + deadops.put(method, deadOps); + //if (deadOps == null) + //continue; + //if (isDead == null || !isDead) + // continue outer; } // param is logically dead for all possible methods - Method method = cmp.methods.get(0); - System.out.println(method.getMethods().getClassFile().getName() + "." + method.getName() + " has dead param " + cmp.paramIndex); + //Method method = cmp.methods.get(0); + //System.out.println(method.getMethods().getClassFile().getName() + "." + method.getName() + " has dead param " + cmp.paramIndex); + + for (Method method : cmp.methods) + { + // remove dead parameter. + + // I already have an unused parameter deob which detects them and removes them, so don't have to clean up those. + // Additionally there is an unused block deob which can remove unused blocks of code, so + + // remove conditional jump, insert goto? + //removeLogicallyDead(execution, method, cmp.lvtIndex); + } } // for (MethodGroup group : methodGroups) // //for (Object ogroup : methodGroups.values()) @@ -395,6 +505,43 @@ public class ConstantParameter implements Deobfuscator // } } + private void removeDeadOperations() + { + for (Method method : deadops.keySet()) + { + List ops = deadops.get(method); + + for (LogicallyDeadOp op : ops) + { + InstructionContext ctx = op.compCtx; // comparison + boolean branch = op.branch; + + Instructions instructions = ctx.getInstruction().getInstructions(); + + // remove the if + if (ctx.getInstruction() instanceof If) + ctx.removeStack(1); + ctx.removeStack(0); + + int idx = instructions.getInstructions().indexOf(ctx.getInstruction()); + if (idx == -1) + continue; // already removed? + + JumpingInstruction jumpIns = (JumpingInstruction) ctx.getInstruction(); + assert jumpIns.getJumps().size() == 1; + Instruction to = jumpIns.getJumps().get(0); + + instructions.remove(ctx.getInstruction()); + + if (branch) + { + // insert goto + instructions.getInstructions().add(idx, new Goto(instructions, to)); + } + } + } + } + @Override public void run(ClassGroup group) { @@ -406,6 +553,9 @@ public class ConstantParameter implements Deobfuscator findParameters(execution); findLogicallyDeadOperations(execution); + removeDeadOperations(); + + //System.out.println(deadops.size() + " deadops"); // System.out.println(parameters.size() + " params"); // for (ConstantMethodParameter p : parameters) diff --git a/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java b/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java index 0778ee195d..79b6ac8b71 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/IllegalStateExceptions.java @@ -9,6 +9,8 @@ import info.sigterm.deob.Method; import info.sigterm.deob.attributes.Code; import info.sigterm.deob.attributes.code.Instruction; import info.sigterm.deob.attributes.code.Instructions; +import info.sigterm.deob.attributes.code.instruction.types.ComparisonInstruction; +import info.sigterm.deob.attributes.code.instruction.types.JumpingInstruction; import info.sigterm.deob.attributes.code.instructions.AThrow; import info.sigterm.deob.attributes.code.instructions.Goto; import info.sigterm.deob.attributes.code.instructions.If; @@ -43,7 +45,7 @@ public class IllegalStateExceptions implements Deobfuscator { Instruction ins = ilist.get(i); - if (!(ins instanceof If) && !(ins instanceof If0)) + if (!(ins instanceof ComparisonInstruction)) continue; Instruction ins2 = ilist.get(i + 1); @@ -55,11 +57,9 @@ public class IllegalStateExceptions implements Deobfuscator if (!clazz.getName().equals("java/lang/IllegalStateException")) continue; - Instruction to = null; - if (ins instanceof If) - to = ((If) ins).getTo(); - else if (ins instanceof If0) - to = ((If0) ins).getTo(); + JumpingInstruction jumpIns = (JumpingInstruction) ins; + assert jumpIns.getJumps().size() == 1; + Instruction to = jumpIns.getJumps().get(0); // remove stack of if. boolean found = false; diff --git a/src/main/java/info/sigterm/deob/execution/Execution.java b/src/main/java/info/sigterm/deob/execution/Execution.java index 5f3a71ca08..395286e371 100644 --- a/src/main/java/info/sigterm/deob/execution/Execution.java +++ b/src/main/java/info/sigterm/deob/execution/Execution.java @@ -50,8 +50,6 @@ public class Execution public void run() { - // XXX update pc? some instructiosn rely on it still. - int count = 0, fcount = 0; while (!pendingMethods.isEmpty()) {