diff --git a/src/main/java/info/sigterm/deob/attributes/code/InstructionType.java b/src/main/java/info/sigterm/deob/attributes/code/InstructionType.java index 7c5be9dc4c..124902a3be 100644 --- a/src/main/java/info/sigterm/deob/attributes/code/InstructionType.java +++ b/src/main/java/info/sigterm/deob/attributes/code/InstructionType.java @@ -166,9 +166,9 @@ public enum InstructionType IF_ICMPEQ(0x9f, "if_icmpeq", If.class), IF_ICMPNE(0xa0, "if_icmpne", If.class), IF_ICMPLT(0xa1, "if_cmplt", If.class), - IF_CMPGE(0xa2, "if_cmpge", If.class), - IF_CMPGT(0xa3, "if_cmpgt", If.class), - IF_CMPLE(0xa4, "if_cmple", If.class), + IF_ICMPGE(0xa2, "if_icmpge", If.class), + IF_ICMPGT(0xa3, "if_icmpgt", If.class), + IF_ICMPLE(0xa4, "if_icmple", If.class), IF_ACMPEQ(0xa5, "if_acmpeq", If.class), IF_ACMPNE(0xa6, "if_acmpne", If.class), GOTO(0xa7, "goto", Goto.class), diff --git a/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java b/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java index 9c05ac615b..3f54675cf2 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/ConstantParameter.java @@ -3,8 +3,13 @@ package info.sigterm.deob.deobfuscators; 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.instruction.types.ComparisonInstruction; import info.sigterm.deob.attributes.code.instruction.types.InvokeInstruction; +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.If; +import info.sigterm.deob.attributes.code.instructions.If0; import info.sigterm.deob.execution.Execution; import info.sigterm.deob.execution.Frame; import info.sigterm.deob.execution.InstructionContext; @@ -13,16 +18,52 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.map.MultiValueMap; class ConstantMethodParameter { - public Method method; - public int paramNum; + public List methods; + public int paramIndex; + public int lvtIndex; public Object value; - public InstructionContext invoke; // invoking instruction + //public InstructionContext invoke; // invoking instruction + + @Override + public int hashCode() + { + int hash = 3; + hash = 47 * hash + Objects.hashCode(this.methods); + hash = 47 * hash + this.paramIndex; + hash = 47 * hash + Objects.hashCode(this.value); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ConstantMethodParameter other = (ConstantMethodParameter) obj; + if (!Objects.equals(this.methods, other.methods)) { + return false; + } + if (this.paramIndex != other.paramIndex) { + return false; + } + if (!Objects.equals(this.value, other.value)) { + return false; + } + return true; + } + + } class MethodGroup @@ -31,33 +72,57 @@ class MethodGroup public Collection constantParameters; // parameters which are always constant for all invocations public List cmps = new ArrayList<>(); // cmps for all methods in the group, which hold the values. - public List getConstantParametersFor(Method m, int parameter) - { - List out = new ArrayList<>(); - for (ConstantMethodParameter c : cmps) - if (c.method == m && c.paramNum == parameter) - out.add(c); - return out; - } +// public List getConstantParametersFor(Method m, int parameter) +// { +// List out = new ArrayList<>(); +// for (ConstantMethodParameter c : cmps) +// if (c.method == m && c.paramNum == parameter) +// out.add(c); +// return out; +// } } public class ConstantParameter implements Deobfuscator { - private MultiValueMap parameters = new MultiValueMap<>(); + private List parameters = new ArrayList<>(); + //private MultiValueMap parameters = new MultiValueMap<>(); // methods can be in more than one group because of multiple inheritance with interfaces //private MultiValueMap methodGroups = new MultiValueMap<>(); private List methodGroups = new ArrayList<>(); - private void findConstantParameter(Execution execution, Method method, InstructionContext invokeCtx) + private void checkMethodsAreConsistent(List methods) { - List pops = invokeCtx.getPops(); - for (int i = 0; i < method.getDescriptor().size(); ++i) + Method prev = null; + for (Method m : methods) { - // object is poped first + if (prev != null) + { + assert prev.getDescriptor().equals(m.getDescriptor()); + assert prev.isStatic() == m.isStatic(); + } + prev = m; + } + } + + private void findConstantParameter(Execution execution, List methods, InstructionContext invokeCtx) + { + checkMethodsAreConsistent(methods); + + Method method = methods.get(0); // all methods must have the same signature etc + int offset = method.isStatic() ? 0 : 1; + + List pops = invokeCtx.getPops(); + + outer: + // object is popped first, then param 1, 2, 3, etc. double and long take two slots. + for (int lvtOffset = offset, parameterIndex = 0; + parameterIndex < method.getDescriptor().size(); + lvtOffset += method.getDescriptor().getTypeOfArg(parameterIndex++).getSlots()) + { + // get(0) == first thing popped which is the last parameter, + // get(descriptor.size() - 1) == first parameter + StackContext ctx = pops.get(method.getDescriptor().size() - 1 - parameterIndex); - int offset = method.isStatic() ? 0 : 1; - - StackContext ctx = pops.get(offset + i); if (ctx.getPushed().getInstruction() instanceof PushConstantInstruction) { PushConstantInstruction pc = (PushConstantInstruction) ctx.getPushed().getInstruction(); @@ -66,29 +131,35 @@ public class ConstantParameter implements Deobfuscator continue; ConstantMethodParameter cmp = new ConstantMethodParameter(); - cmp.method = method; - cmp.paramNum = i; + cmp.methods = methods; + cmp.paramIndex = parameterIndex; + cmp.lvtIndex = lvtOffset; cmp.value = pc.getConstant().getObject(); - cmp.invoke = invokeCtx; + //cmp.invoke = invokeCtx; - parameters.put(method, cmp); + // already exists? + for (ConstantMethodParameter c : parameters) + if (c.equals(cmp)) + continue outer; + + parameters.add(cmp); } } } - private Collection getParametersFor(Method method) - { - Collection params = parameters.getCollection(method); - Collection out = new ArrayList<>(); - - if (params != null) - for (ConstantMethodParameter p : params) - if (!out.contains(p.paramNum)) - out.add(p.paramNum); - - return out; - } - +// private Collection getParametersFor(Method method) +// { +// Collection params = parameters.getCollection(method); +// Collection out = new ArrayList<>(); +// +// if (params != null) +// for (ConstantMethodParameter p : params) +// if (!out.contains(p.paramNum)) +// out.add(p.paramNum); +// +// return out; +// } +// private void findParameters(Execution execution) { for (Frame frame : execution.processedFrames) @@ -98,9 +169,12 @@ public class ConstantParameter implements Deobfuscator continue; List methods = ((InvokeInstruction) ins.getInstruction()).getMethods(); - for (Method m : methods) - findConstantParameter(execution, m, ins); + if (methods.isEmpty()) + continue; + findConstantParameter(execution, methods, ins); + + /* // get common constant indexes from all methods that can possibly be called Collection parameterIndexes = null; for (Method m : methods) @@ -131,29 +205,196 @@ public class ConstantParameter implements Deobfuscator methodGroups.add(group); //for (Method m : methods) // methodGroups.put(m, group); + */ } } - private void findLogicallyDeadOperations() + private boolean doLogicalComparison(Object value, ComparisonInstruction comparison, Object otherValue) { - for (MethodGroup group : methodGroups) - //for (Object ogroup : methodGroups.values()) + Instruction ins = (Instruction) comparison; + + assert (comparison instanceof If0) == (otherValue == null); + assert otherValue == null || otherValue instanceof Integer; + + switch (ins.getType()) { - // MethodGroup group = (MethodGroup) ogroup; - for (Method m : group.methods) - for (int parameterIndex : group.constantParameters) - { - // constants used in this parameter index when calling this method - List cmps = group.getConstantParametersFor(m, parameterIndex); - - // iterate instructions of method and find comparisons to parameter - // remove if all are logically dead. rely on unused parameter deob to remove - // the parameter. - System.out.println(cmps.size() + " calls to " + m.getMethods().getClassFile().getName() + "." + m.getName() + " with index " + parameterIndex); - } + case IFEQ: + return value.equals(0); + case IFNE: + return !value.equals(0); + case IFLT: + return (int) value < 0; + case IFGE: + return (int) value >= 0; + case IFGT: + return (int) value > 0; + case IFLE: + return (int) value < 0; + case IF_ICMPEQ: + return value.equals(otherValue); + case IF_ICMPNE: + return !value.equals(otherValue); + case IF_ICMPLT: + return (int) value < (int) otherValue; + case IF_ICMPGE: + return (int) value >= (int) otherValue; + case IF_ICMPGT: + return (int) value > (int) otherValue; + case IF_ICMPLE: + return (int) value <= (int) otherValue; + default: + throw new RuntimeException("Unknown constant comparison instructuction"); } } + private boolean isLogicallyDead(Execution execution, Method method, int paramIndex, int lvtIndex, Object value) + { + Boolean branch = null; + // find if instruction + // one side must be constant, other must be parameterIndex + + //int offset = method.isStatic() ? 0 : 1; + //int variableIndex = paramIndex + offset; + + for (Frame frame : execution.processedFrames) + { + if (frame.getMethod() != method) + continue; + + for (InstructionContext ins : frame.getInstructions()) + { +// if (ins.getInstruction() instanceof LVTInstruction) +// { +// LVTInstruction lvtins = (LVTInstruction) ins.getInstruction(); +// +// if (lvtins.getVariableIndex() != variableIndex) +// continue; +// } + + if (!(ins.getInstruction() instanceof ComparisonInstruction)) + continue; + + // assume that this will always be variable index #paramIndex comp with a constant. + + ComparisonInstruction comp = (ComparisonInstruction) ins.getInstruction(); + + StackContext one, two = null; + + if (comp instanceof If0) + { + one = ins.getPops().get(0); + } + else if (comp instanceof If) + { + one = ins.getPops().get(0); + two = ins.getPops().get(1); + } + else + { + throw new RuntimeException("Unknown comp ins"); + } + + // find if one is a lvt ins + LVTInstruction lvt = null; + + if (one.getPushed().getInstruction() instanceof LVTInstruction) + { + lvt = (LVTInstruction) one.getPushed().getInstruction(); + } + else if (two != null && two.getPushed().getInstruction() instanceof LVTInstruction) + { + lvt = (LVTInstruction) two.getPushed().getInstruction(); + } + + assert lvt == null || !lvt.store(); + + if (lvt == null || lvt.getVariableIndex() != lvtIndex) + continue; + + Object otherValue = null; + + if (two != null) // two is null for if0 + { + if (!(two.getPushed().getInstruction() instanceof PushConstantInstruction)) + continue; + + PushConstantInstruction pc = (PushConstantInstruction) two.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; + //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 true; + } + + private void findLogicallyDeadOperations(Execution execution) + { + outer: + for (ConstantMethodParameter cmp : parameters) + { + for (Method method : cmp.methods) + { + boolean isDead = isLogicallyDead(execution, method, cmp.paramIndex, cmp.lvtIndex, cmp.value); + if (!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); + } +// for (MethodGroup group : methodGroups) +// //for (Object ogroup : methodGroups.values()) +// { +// System.out.println("Iterating group " + group); +// // MethodGroup group = (MethodGroup) ogroup; +// for (Method m : group.methods) +// { +// System.out.println("Iterating method " + m); +// for (int parameterIndex : group.constantParameters) +// { +// // constants used in this parameter index when calling this method +// List cmps = group.getConstantParametersFor(m, parameterIndex); +// +// // iterate instructions of method and find comparisons to parameter +// // remove if all are logically dead. rely on unused parameter deob to remove +// // the parameter. +// System.out.println(cmps.size() + " calls to " + m.getMethods().getClassFile().getName() + "." + m.getName() + " with index " + parameterIndex); +// } +// } +// } +// for (MethodGroup group : methodGroups) +// //for (Object ogroup : methodGroups.values()) +// { +// System.out.println("Iterating group " + group); +// // MethodGroup group = (MethodGroup) ogroup; +// for (Method m : group.methods) +// { +// System.out.println("Iterating method " + m); +// for (int parameterIndex : group.constantParameters) +// { +// // constants used in this parameter index when calling this method +// List cmps = group.getConstantParametersFor(m, parameterIndex); +// +// // iterate instructions of method and find comparisons to parameter +// // remove if all are logically dead. rely on unused parameter deob to remove +// // the parameter. +// System.out.println(cmps.size() + " calls to " + m.getMethods().getClassFile().getName() + "." + m.getName() + " with index " + parameterIndex); +// } +// } +// } + } + @Override public void run(ClassGroup group) { @@ -164,7 +405,13 @@ public class ConstantParameter implements Deobfuscator execution.run(); findParameters(execution); - findLogicallyDeadOperations(); + findLogicallyDeadOperations(execution); + +// System.out.println(parameters.size() + " params"); +// for (ConstantMethodParameter p : parameters) +// { +// //System.out.println +// } } }