this doesn't work at all but looks bette

This commit is contained in:
Adam
2015-07-31 23:48:35 -04:00
parent 7b4de7d8db
commit 7ce6cca104
2 changed files with 304 additions and 57 deletions

View File

@@ -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<Method> 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<Integer> constantParameters; // parameters which are always constant for all invocations
public List<ConstantMethodParameter> cmps = new ArrayList<>(); // cmps for all methods in the group, which hold the values.
public List<ConstantMethodParameter> getConstantParametersFor(Method m, int parameter)
{
List<ConstantMethodParameter> out = new ArrayList<>();
for (ConstantMethodParameter c : cmps)
if (c.method == m && c.paramNum == parameter)
out.add(c);
return out;
}
// public List<ConstantMethodParameter> getConstantParametersFor(Method m, int parameter)
// {
// List<ConstantMethodParameter> 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<Method, ConstantMethodParameter> parameters = new MultiValueMap<>();
private List<ConstantMethodParameter> parameters = new ArrayList<>();
//private MultiValueMap<Method, ConstantMethodParameter> parameters = new MultiValueMap<>();
// methods can be in more than one group because of multiple inheritance with interfaces
//private MultiValueMap<Method, MethodGroup> methodGroups = new MultiValueMap<>();
private List<MethodGroup> methodGroups = new ArrayList<>();
private void findConstantParameter(Execution execution, Method method, InstructionContext invokeCtx)
private void checkMethodsAreConsistent(List<Method> methods)
{
List<StackContext> 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<Method> 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<StackContext> 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<Integer> getParametersFor(Method method)
{
Collection<ConstantMethodParameter> params = parameters.getCollection(method);
Collection<Integer> out = new ArrayList<>();
if (params != null)
for (ConstantMethodParameter p : params)
if (!out.contains(p.paramNum))
out.add(p.paramNum);
return out;
}
// private Collection<Integer> getParametersFor(Method method)
// {
// Collection<ConstantMethodParameter> params = parameters.getCollection(method);
// Collection<Integer> 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<Method> 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<Integer> 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<ConstantMethodParameter> 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<ConstantMethodParameter> 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<ConstantMethodParameter> 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
// }
}
}