Make unused method stuff simpler don't worry about collisions, I'll
rename everything later so it wont be a problem
This commit is contained in:
7
pom.xml
7
pom.xml
@@ -3,4 +3,11 @@
|
|||||||
<groupId>info.sigterm</groupId>
|
<groupId>info.sigterm</groupId>
|
||||||
<artifactId>deob</artifactId>
|
<artifactId>deob</artifactId>
|
||||||
<version>0.0.1-SNAPSHOT</version>
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
<version>4.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
@@ -62,7 +62,7 @@ public class Deob
|
|||||||
|
|
||||||
new UnusedFields().run(group);
|
new UnusedFields().run(group);
|
||||||
|
|
||||||
new ModularArithmeticDeobfuscation().run(group);
|
//new ModularArithmeticDeobfuscation().run(group);
|
||||||
|
|
||||||
saveJar(group, args[1]);
|
saveJar(group, args[1]);
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class Method
|
|||||||
|
|
||||||
private short accessFlags;
|
private short accessFlags;
|
||||||
private String name;
|
private String name;
|
||||||
private Signature arguments;
|
public Signature arguments;
|
||||||
private Attributes attributes;
|
private Attributes attributes;
|
||||||
|
|
||||||
Method(Methods methods) throws IOException
|
Method(Methods methods) throws IOException
|
||||||
@@ -60,89 +60,6 @@ public class Method
|
|||||||
{
|
{
|
||||||
//assert callsFrom.isEmpty();
|
//assert callsFrom.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeParameter(Execution execution, int paramIndex, int lvtIndex)
|
|
||||||
{
|
|
||||||
Set<Instruction> 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()
|
public Methods getMethods()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,16 +3,25 @@ package info.sigterm.deob.deobfuscators;
|
|||||||
import info.sigterm.deob.ClassFile;
|
import info.sigterm.deob.ClassFile;
|
||||||
import info.sigterm.deob.ClassGroup;
|
import info.sigterm.deob.ClassGroup;
|
||||||
import info.sigterm.deob.Method;
|
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;
|
||||||
|
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.Execution;
|
||||||
|
import info.sigterm.deob.execution.Frame;
|
||||||
|
import info.sigterm.deob.execution.InstructionContext;
|
||||||
import info.sigterm.deob.pool.NameAndType;
|
import info.sigterm.deob.pool.NameAndType;
|
||||||
|
import info.sigterm.deob.pool.PoolEntry;
|
||||||
import info.sigterm.deob.signature.Signature;
|
import info.sigterm.deob.signature.Signature;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
|
||||||
public class UnusedParameters
|
public class UnusedParameters
|
||||||
{
|
{
|
||||||
private static List<Method> findDependentMethods(NameAndType nat, Set<ClassFile> visited, ClassGroup group, ClassFile cf)
|
private static List<Method> findDependentMethods(NameAndType nat, Set<ClassFile> visited, ClassGroup group, ClassFile cf)
|
||||||
@@ -24,8 +33,8 @@ public class UnusedParameters
|
|||||||
|
|
||||||
visited.add(cf);
|
visited.add(cf);
|
||||||
|
|
||||||
Method method = cf.findMethod(nat);
|
Method method = cf.findMethod(nat); // XXX this searches down
|
||||||
if (method != null)
|
if (method != null && !method.isStatic())
|
||||||
list.add(method);
|
list.add(method);
|
||||||
|
|
||||||
// search parent
|
// search parent
|
||||||
@@ -45,7 +54,147 @@ public class UnusedParameters
|
|||||||
private static List<Method> findDependentMethods(Method m)
|
private static List<Method> findDependentMethods(Method m)
|
||||||
{
|
{
|
||||||
ClassFile cf = m.getMethods().getClassFile();
|
ClassFile cf = m.getMethods().getClassFile();
|
||||||
return findDependentMethods(m.getNameAndType(), new HashSet<ClassFile>(), cf.getGroup(), cf);
|
List<Method> methods = findDependentMethods(m.getNameAndType(), new HashSet<ClassFile>(), cf.getGroup(), cf);
|
||||||
|
|
||||||
|
Set s = new HashSet<>(methods); // XXX
|
||||||
|
return new ArrayList<>(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Integer> findUnusedParameters(Method method)
|
||||||
|
{
|
||||||
|
int offset = method.isStatic() ? 0 : 1;
|
||||||
|
Signature signature = method.getNameAndType().getDescriptor();
|
||||||
|
List<Integer> 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<Integer> findUnusedParameters(Collection<Method> methods)
|
||||||
|
{
|
||||||
|
Collection<Integer> list = null;
|
||||||
|
|
||||||
|
for (Method m : methods)
|
||||||
|
{
|
||||||
|
List<Integer> p = findUnusedParameters(m);
|
||||||
|
|
||||||
|
if (list == null)
|
||||||
|
list = p;
|
||||||
|
else
|
||||||
|
list = CollectionUtils.intersection(list, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeParameter(List<Method> methods, Signature signature, Execution execution, int paramIndex, int lvtIndex)
|
||||||
|
{
|
||||||
|
Set<Instruction> 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)
|
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
|
// if more than one is unused, we'll just remove the one
|
||||||
// and do the others on another pass
|
// and do the others on another pass
|
||||||
|
|
||||||
|
Set<Method> done = new HashSet<>();
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int collide = 0;
|
|
||||||
int overrides = 0;
|
|
||||||
|
|
||||||
//group.buildCallGraph(); // method.removeParameter uses the callgraph
|
|
||||||
|
|
||||||
for (ClassFile cf : group.getClasses())
|
for (ClassFile cf : group.getClasses())
|
||||||
{
|
{
|
||||||
for (Method m : cf.getMethods().getMethods())
|
for (Method m : cf.getMethods().getMethods())
|
||||||
{
|
{
|
||||||
|
if (done.contains(m))
|
||||||
|
continue;
|
||||||
|
|
||||||
int offset = m.isStatic() ? 0 : 1;
|
int offset = m.isStatic() ? 0 : 1;
|
||||||
Signature signature = m.getNameAndType().getDescriptor();
|
Signature signature = m.getNameAndType().getDescriptor();
|
||||||
|
|
||||||
// for a parameter to be unused it must be unused on all methods that override it
|
// for a parameter to be unused it must be unused on all methods that override it
|
||||||
|
|
||||||
List<Method> methods = findDependentMethods(m); // these are all of the methods the param must be unused in
|
List<Method> methods;
|
||||||
assert methods.contains(m);
|
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)
|
Collection<Integer> unusedParameters = findUnusedParameters(methods);
|
||||||
continue; // don't mess with this now
|
|
||||||
|
|
||||||
if (m.getCode() == null)
|
if (unusedParameters.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
outer:
|
int unusedParameter = (int) unusedParameters.toArray()[0];
|
||||||
for (int variableIndex = 0, lvtIndex = offset;
|
int[] lvtIndexes = getLvtIndexes(signature, offset);
|
||||||
variableIndex < signature.size();
|
|
||||||
lvtIndex += signature.getTypeOfArg(variableIndex++).getSlots())
|
for (Method m2 : methods)
|
||||||
{
|
done.add(m2);
|
||||||
for (Method method : methods)
|
|
||||||
{
|
/* removing the parameter can cause collision of overriden methods,
|
||||||
// XXX instead of checking if the lvt index is never accessed,
|
* we should first rename all methods to be unique?
|
||||||
// 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
|
System.out.println("Removing " + m.getName() + " on " + m.getMethods().getClassFile().getName());
|
||||||
// the lvt table.
|
removeParameter(methods, signature, execution, unusedParameter, lvtIndexes[unusedParameter]);
|
||||||
List<? extends Instruction> lv = method.findLVTInstructionsForVariable(lvtIndex);
|
|
||||||
if (lv != null && !lv.isEmpty())
|
++count;
|
||||||
continue outer; // used, try next parameter
|
|
||||||
}
|
break;
|
||||||
|
|
||||||
/*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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new int[] { count, collide, overrides };
|
return new int[] { count };
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(ClassGroup group)
|
public void run(ClassGroup group)
|
||||||
@@ -138,19 +261,15 @@ public class UnusedParameters
|
|||||||
execution.run();
|
execution.run();
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int collide = 0;
|
|
||||||
int override = 0;
|
|
||||||
int[] i;
|
int[] i;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
i = checkParametersOnce(execution, group);
|
i = checkParametersOnce(execution, group);
|
||||||
|
|
||||||
count += i[0];
|
count += i[0];
|
||||||
collide = i[1]; // the next pass may be able to reduce the collisions
|
|
||||||
override = i[2];
|
|
||||||
}
|
}
|
||||||
while (i[0] > 0);
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user