From fcc8fddcd737661eadaee4d49a2bd31288c3d031 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 5 Jul 2015 21:05:34 -0400 Subject: [PATCH] Make unused method stuff simpler don't worry about collisions, I'll rename everything later so it wont be a problem --- pom.xml | 7 + src/main/java/info/sigterm/deob/Deob.java | 2 +- src/main/java/info/sigterm/deob/Method.java | 85 +----- .../deob/deobfuscators/UnusedParameters.java | 251 +++++++++++++----- 4 files changed, 194 insertions(+), 151 deletions(-) diff --git a/pom.xml b/pom.xml index 3b908961f2..62fda7e646 100644 --- a/pom.xml +++ b/pom.xml @@ -3,4 +3,11 @@ info.sigterm deob 0.0.1-SNAPSHOT + + + org.apache.commons + commons-collections4 + 4.0 + + \ No newline at end of file diff --git a/src/main/java/info/sigterm/deob/Deob.java b/src/main/java/info/sigterm/deob/Deob.java index 6882258ee5..fe45732a20 100644 --- a/src/main/java/info/sigterm/deob/Deob.java +++ b/src/main/java/info/sigterm/deob/Deob.java @@ -62,7 +62,7 @@ public class Deob new UnusedFields().run(group); - new ModularArithmeticDeobfuscation().run(group); + //new ModularArithmeticDeobfuscation().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 abfddc5171..f6282dd0bf 100644 --- a/src/main/java/info/sigterm/deob/Method.java +++ b/src/main/java/info/sigterm/deob/Method.java @@ -30,7 +30,7 @@ public class Method private short accessFlags; private String name; - private Signature arguments; + public Signature arguments; private Attributes attributes; Method(Methods methods) throws IOException @@ -60,89 +60,6 @@ public class Method { //assert callsFrom.isEmpty(); } - - public void removeParameter(Execution execution, int paramIndex, int lvtIndex) - { - Set done = new HashSet<>(); - for (Frame f : execution.processedFrames) - for (InstructionContext ins : f.getInstructions()) - if (ins.getInvokes().contains(this)) - { - int pops = arguments.size() - paramIndex - 1; // index from top of stack of parameter - ins.removeStack(pops); // remove parameter from stack - - if (done.contains(ins.getInstruction())) - continue; - - InvokeInstruction iins = (InvokeInstruction) ins.getInstruction(); - iins.removeParameter(paramIndex); // remove parameter from instruction - - done.add(ins.getInstruction()); - } - - // this double checks that all calls to this have been located - for (ClassFile cf : methods.getClassFile().getGroup().getClasses()) - for (Method m : cf.getMethods().getMethods()) - { - Code c = m.getCode(); - if (c == null) - continue; - - for (Instruction i : c.getInstructions().getInstructions()) - { - if (i instanceof InvokeInstruction) - { - InvokeInstruction ii = (InvokeInstruction) i; - PoolEntry pool = ii.getMethod(); - - if (pool instanceof info.sigterm.deob.pool.Method) - { - info.sigterm.deob.pool.Method pm = (info.sigterm.deob.pool.Method) pool; - - if (pm.getClassEntry().getName().equals(this.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(this.getNameAndType()) && !done.contains(i)) - { - // for some reason this wasn't removed above? - System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); - //assert false; - } - } - else if (pool instanceof info.sigterm.deob.pool.InterfaceMethod) - { - info.sigterm.deob.pool.InterfaceMethod pm = (info.sigterm.deob.pool.InterfaceMethod) pool; - - if (pm.getClassEntry().getName().equals(this.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(this.getNameAndType()) && !done.contains(i)) - { - // for some reason this wasn't removed above? - System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); - //assert false; - } - } - } - } - } - - // adjust lvt indexes to get rid of idx in the method - for (Instruction ins : new ArrayList<>(getCode().getInstructions().getInstructions())) - { - if (ins instanceof LVTInstruction) - { - LVTInstruction lins = (LVTInstruction) ins; - - int i = lins.getVariableIndex(); - assert i != lvtIndex; // current unused variable detection just looks for no accesses - - // reassign - if (i > lvtIndex) - { - Instruction newIns = lins.setVariableIndex(--i); - if (newIns != ins) - ins.replace(newIns); - } - } - } - - arguments.remove(paramIndex); - } public Methods getMethods() { diff --git a/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java b/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java index 3bd4d45c93..3037962189 100644 --- a/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java +++ b/src/main/java/info/sigterm/deob/deobfuscators/UnusedParameters.java @@ -3,16 +3,25 @@ package info.sigterm.deob.deobfuscators; import info.sigterm.deob.ClassFile; import info.sigterm.deob.ClassGroup; import info.sigterm.deob.Method; +import info.sigterm.deob.attributes.Code; import info.sigterm.deob.attributes.code.Instruction; +import info.sigterm.deob.attributes.code.instruction.types.InvokeInstruction; +import info.sigterm.deob.attributes.code.instruction.types.LVTInstruction; import info.sigterm.deob.execution.Execution; +import info.sigterm.deob.execution.Frame; +import info.sigterm.deob.execution.InstructionContext; import info.sigterm.deob.pool.NameAndType; +import info.sigterm.deob.pool.PoolEntry; import info.sigterm.deob.signature.Signature; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.collections4.CollectionUtils; + public class UnusedParameters { private static List findDependentMethods(NameAndType nat, Set visited, ClassGroup group, ClassFile cf) @@ -24,8 +33,8 @@ public class UnusedParameters visited.add(cf); - Method method = cf.findMethod(nat); - if (method != null) + Method method = cf.findMethod(nat); // XXX this searches down + if (method != null && !method.isStatic()) list.add(method); // search parent @@ -45,7 +54,147 @@ public class UnusedParameters private static List findDependentMethods(Method m) { ClassFile cf = m.getMethods().getClassFile(); - return findDependentMethods(m.getNameAndType(), new HashSet(), cf.getGroup(), cf); + List methods = findDependentMethods(m.getNameAndType(), new HashSet(), cf.getGroup(), cf); + + Set s = new HashSet<>(methods); // XXX + return new ArrayList<>(s); + } + + private List findUnusedParameters(Method method) + { + int offset = method.isStatic() ? 0 : 1; + Signature signature = method.getNameAndType().getDescriptor(); + List unusedParams = new ArrayList<>(); + + for (int variableIndex = 0, lvtIndex = offset; + variableIndex < signature.size(); + lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots()) + { + List lv = method.findLVTInstructionsForVariable(lvtIndex); + if (lv == null || lv.isEmpty()) + unusedParams.add(variableIndex); + } + + return unusedParams; + } + + private int[] getLvtIndexes(Signature signature, int offset) + { + int[] i = new int[signature.size()]; + for (int variableIndex = 0, lvtIndex = offset; + variableIndex < signature.size(); + lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots()) + { + i[variableIndex] = lvtIndex; + } + return i; + } + + private Collection findUnusedParameters(Collection methods) + { + Collection list = null; + + for (Method m : methods) + { + List p = findUnusedParameters(m); + + if (list == null) + list = p; + else + list = CollectionUtils.intersection(list, p); + } + + return list; + } + + public void removeParameter(List methods, Signature signature, Execution execution, int paramIndex, int lvtIndex) + { + Set done = new HashSet<>(); + + for (Frame f : execution.processedFrames) + for (InstructionContext ins : f.getInstructions()) + if (!ins.getInvokes().isEmpty() && methods.containsAll(ins.getInvokes())) + { + System.out.println("Removing from " + ins); + int pops = signature.size() - paramIndex - 1; // index from top of stack of parameter + ins.removeStack(pops); // remove parameter from stack + + if (done.contains(ins.getInstruction())) + continue; + + InvokeInstruction iins = (InvokeInstruction) ins.getInstruction(); + iins.removeParameter(paramIndex); // remove parameter from instruction + + done.add(ins.getInstruction()); + } + + /* + // this double checks that all calls to this have been located + for (ClassFile cf : method.getMethods().getClassFile().getGroup().getClasses()) + for (Method m : cf.getMethods().getMethods()) + { + Code c = m.getCode(); + if (c == null) + continue; + + for (Instruction i : c.getInstructions().getInstructions()) + { + if (i instanceof InvokeInstruction) + { + InvokeInstruction ii = (InvokeInstruction) i; + PoolEntry pool = ii.getMethod(); + + if (pool instanceof info.sigterm.deob.pool.Method) + { + info.sigterm.deob.pool.Method pm = (info.sigterm.deob.pool.Method) pool; + + if (pm.getClassEntry().getName().equals(method.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(method.getNameAndType()) && !done.contains(i)) + { + // for some reason this wasn't removed above? + System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); + //assert false; + } + } + else if (pool instanceof info.sigterm.deob.pool.InterfaceMethod) + { + info.sigterm.deob.pool.InterfaceMethod pm = (info.sigterm.deob.pool.InterfaceMethod) pool; + + if (pm.getClassEntry().getName().equals(method.getMethods().getClassFile().getName()) && pm.getNameAndType().equals(method.getNameAndType()) && !done.contains(i)) + { + // for some reason this wasn't removed above? + System.err.println("Method " + m.getName() + " in " + cf.getName() + " calls " + pm.getNameAndType().getName() + " in " + pm.getClassEntry().getName() + " at " + i.getPc() + ", but this instruction was not found during execution"); + //assert false; + } + } + } + } + } + */ + + for (Method method : methods) + if (method.getCode() != null) + // adjust lvt indexes to get rid of idx in the method + for (Instruction ins : new ArrayList<>(method.getCode().getInstructions().getInstructions())) + { + if (ins instanceof LVTInstruction) + { + LVTInstruction lins = (LVTInstruction) ins; + + int i = lins.getVariableIndex(); + assert i != lvtIndex; // current unused variable detection just looks for no accesses + + // reassign + if (i > lvtIndex) + { + Instruction newIns = lins.setVariableIndex(--i); + if (newIns != ins) + ins.replace(newIns); + } + } + } + + for (Method method : methods) + method.arguments.remove(paramIndex); } private int[] checkParametersOnce(Execution execution, ClassGroup group) @@ -54,81 +203,55 @@ public class UnusedParameters // if more than one is unused, we'll just remove the one // and do the others on another pass + Set done = new HashSet<>(); int count = 0; - int collide = 0; - int overrides = 0; - - //group.buildCallGraph(); // method.removeParameter uses the callgraph for (ClassFile cf : group.getClasses()) { for (Method m : cf.getMethods().getMethods()) { + if (done.contains(m)) + continue; + int offset = m.isStatic() ? 0 : 1; Signature signature = m.getNameAndType().getDescriptor(); // for a parameter to be unused it must be unused on all methods that override it - List methods = findDependentMethods(m); // these are all of the methods the param must be unused in - assert methods.contains(m); + List methods; + if (!m.isStatic()) + { + methods = findDependentMethods(m); // these are all of the methods the param must be unused in + } + else + { + methods = new ArrayList<>(); + methods.add(m); + } - if (methods.size() > 1) - continue; // don't mess with this now + Collection unusedParameters = findUnusedParameters(methods); - if (m.getCode() == null) + if (unusedParameters.isEmpty()) continue; - outer: - for (int variableIndex = 0, lvtIndex = offset; - variableIndex < signature.size(); - lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots()) - { - for (Method method : methods) - { - // XXX instead of checking if the lvt index is never accessed, - // check execution frames and see if it is never read prior to being - // written to, and if so, then remove the parameter, but don't re index - // the lvt table. - List lv = method.findLVTInstructionsForVariable(lvtIndex); - if (lv != null && !lv.isEmpty()) - continue outer; // used, try next parameter - } - - /*if (lv == null) - continue; - - // XXX instead of checking if the lvt index is never accessed, - // check execution frames and see if it is never read prior to being - // written to, and if so, then remove the parameter, but don't re index - // the lvt table. - if (!lv.isEmpty()) - continue; - - if (!m.getOverriddenMethods().isEmpty()) - { - ++overrides; - continue; - }*/ - - Signature newSig = new Signature(m.getDescriptor()); - newSig.remove(variableIndex); - - Method otherMethod = cf.getMethods().findMethod(new NameAndType(m.getName(), newSig)); - if (otherMethod != null) - { - // sometimes removing an unused parameter will cause a signature collision with another function, - // just ignore it atm (there seems to be very few) - ++collide; - continue; - } - - m.removeParameter(execution, variableIndex, lvtIndex); - ++count; - break; - } + int unusedParameter = (int) unusedParameters.toArray()[0]; + int[] lvtIndexes = getLvtIndexes(signature, offset); + + for (Method m2 : methods) + done.add(m2); + + /* removing the parameter can cause collision of overriden methods, + * we should first rename all methods to be unique? + */ + System.out.println("Removing " + m.getName() + " on " + m.getMethods().getClassFile().getName()); + removeParameter(methods, signature, execution, unusedParameter, lvtIndexes[unusedParameter]); + + ++count; + + break; } } - return new int[] { count, collide, overrides }; + return new int[] { count }; } public void run(ClassGroup group) @@ -138,19 +261,15 @@ public class UnusedParameters execution.run(); int count = 0; - int collide = 0; - int override = 0; int[] i; do { i = checkParametersOnce(execution, group); count += i[0]; - collide = i[1]; // the next pass may be able to reduce the collisions - override = i[2]; } while (i[0] > 0); - System.out.println("Removed " + count + " unused parameters, unable to remove " + collide + " because of signature collisions and " + override + " due to overriding"); + System.out.println("Removed " + count + " unused parameters"); } }