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 extends Instruction> 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 extends Instruction> 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");
}
}