Try and replace unused blocks with unreached code, seeing some problems somewhere

This commit is contained in:
Adam
2015-08-08 20:45:16 -04:00
parent 12318efcaf
commit da0b7403b4
9 changed files with 203 additions and 67 deletions

View File

@@ -1,7 +1,6 @@
package info.sigterm.deob; package info.sigterm.deob;
import info.sigterm.deob.deobfuscators.IllegalStateExceptions; import info.sigterm.deob.deobfuscators.IllegalStateExceptions;
import info.sigterm.deob.deobfuscators.RenameUnique;
import info.sigterm.deob.deobfuscators.RuntimeExceptions; import info.sigterm.deob.deobfuscators.RuntimeExceptions;
import info.sigterm.deob.deobfuscators.UnusedBlocks; import info.sigterm.deob.deobfuscators.UnusedBlocks;
import info.sigterm.deob.deobfuscators.UnusedFields; import info.sigterm.deob.deobfuscators.UnusedFields;
@@ -9,6 +8,7 @@ import info.sigterm.deob.deobfuscators.UnusedMethods;
import info.sigterm.deob.deobfuscators.UnusedParameters; import info.sigterm.deob.deobfuscators.UnusedParameters;
import info.sigterm.deob.deobfuscators.ConstantParameter; import info.sigterm.deob.deobfuscators.ConstantParameter;
import info.sigterm.deob.deobfuscators.MethodInliner; import info.sigterm.deob.deobfuscators.MethodInliner;
import info.sigterm.deob.deobfuscators.UnreachedCode;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataInputStream; import java.io.DataInputStream;
@@ -36,57 +36,66 @@ public class Deob
// bdur = System.currentTimeMillis() - bstart; // bdur = System.currentTimeMillis() - bstart;
// System.out.println("rename unique took " + bdur/1000L + " seconds"); // System.out.println("rename unique took " + bdur/1000L + " seconds");
// // remove except RuntimeException // remove except RuntimeException
// bstart = System.currentTimeMillis(); bstart = System.currentTimeMillis();
// new RuntimeExceptions().run(group); new RuntimeExceptions().run(group);
// // the blocks of runtime exceptions may contain interesting things like other obfuscations we identify later, but now that // the blocks of runtime exceptions may contain interesting things like other obfuscations we identify later, but now that
// // it can't be reached by the execution phase, those things become confused. so remove blocks here. // it can't be reached by the execution phase, those things become confused. so remove blocks here.
// new UnusedBlocks().run(group); //new UnusedBlocks().run(group);
// bdur = System.currentTimeMillis() - bstart; bdur = System.currentTimeMillis() - bstart;
// System.out.println("runtime exception took " + bdur/1000L + " seconds"); System.out.println("runtime exception took " + bdur/1000L + " seconds");
//
// // remove unused methods // remove unused methods
// bstart = System.currentTimeMillis(); bstart = System.currentTimeMillis();
// new UnusedMethods().run(group); new UnusedMethods().run(group);
// bdur = System.currentTimeMillis() - bstart; bdur = System.currentTimeMillis() - bstart;
// System.out.println("unused methods took " + bdur/1000L + " seconds"); System.out.println("unused methods took " + bdur/1000L + " seconds");
//
// // remove illegal state exceptions, frees up some parameters new UnreachedCode().run(group);
// bstart = System.currentTimeMillis();
// new IllegalStateExceptions().run(group); // remove illegal state exceptions, frees up some parameters
// bdur = System.currentTimeMillis() - bstart; bstart = System.currentTimeMillis();
// System.out.println("illegal state exception took " + bdur/1000L + " seconds"); new IllegalStateExceptions().run(group);
// bdur = System.currentTimeMillis() - bstart;
// // remove constant logically dead parameters System.out.println("illegal state exception took " + bdur/1000L + " seconds");
// bstart = System.currentTimeMillis();
// new ConstantParameter().run(group); // remove constant logically dead parameters
// bdur = System.currentTimeMillis() - bstart; bstart = System.currentTimeMillis();
// System.out.println("constant param took " + bdur/1000L + " seconds"); new ConstantParameter().run(group);
// bdur = System.currentTimeMillis() - bstart;
// // remove unhit blocks System.out.println("constant param took " + bdur/1000L + " seconds");
// bstart = System.currentTimeMillis();
// new UnusedBlocks().run(group); // remove unhit blocks
// bdur = System.currentTimeMillis() - bstart; bstart = System.currentTimeMillis();
// System.out.println("unused blocks took " + bdur/1000L + " seconds"); new UnreachedCode().run(group);
// //new UnusedBlocks().run(group);
// // remove unused parameters bdur = System.currentTimeMillis() - bstart;
System.out.println("unused blocks took " + bdur/1000L + " seconds");
// remove unused parameters
// bstart = System.currentTimeMillis(); // bstart = System.currentTimeMillis();
// new UnusedParameters().run(group); // new UnusedParameters().run(group);
// bdur = System.currentTimeMillis() - bstart; // bdur = System.currentTimeMillis() - bstart;
// System.out.println("unused params took " + bdur/1000L + " seconds"); // System.out.println("unused params took " + bdur/1000L + " seconds");
//
// // remove jump obfuscation // remove jump obfuscation
// //new Jumps().run(group); //new Jumps().run(group);
//
// // remove unused fields // remove unused fields
// bstart = System.currentTimeMillis(); bstart = System.currentTimeMillis();
// new UnusedFields().run(group); new UnusedFields().run(group);
// bdur = System.currentTimeMillis() - bstart; bdur = System.currentTimeMillis() - bstart;
// System.out.println("unused fields took " + bdur/1000L + " seconds"); System.out.println("unused fields took " + bdur/1000L + " seconds");
// remove unused methods, again?
bstart = System.currentTimeMillis();
new UnusedMethods().run(group);
bdur = System.currentTimeMillis() - bstart;
System.out.println("unused methods took " + bdur/1000L + " seconds");
//new ModularArithmeticDeobfuscation().run(group); //new ModularArithmeticDeobfuscation().run(group);
new MethodInliner().run(group); //new MethodInliner().run(group);
saveJar(group, args[1]); saveJar(group, args[1]);

View File

@@ -18,6 +18,7 @@ import java.util.List;
public class Method public class Method
{ {
public static final int ACC_STATIC = 0x8; public static final int ACC_STATIC = 0x8;
public static final int ACC_SYNCHRONIZED = 0x20;
private Methods methods; private Methods methods;
@@ -79,6 +80,11 @@ public class Method
return (accessFlags & ACC_STATIC) != 0; return (accessFlags & ACC_STATIC) != 0;
} }
public boolean isSynchronized()
{
return (accessFlags & ACC_SYNCHRONIZED) != 0;
}
public Exceptions getExceptions() public Exceptions getExceptions()
{ {
return (Exceptions) attributes.findType(AttributeType.EXCEPTIONS); return (Exceptions) attributes.findType(AttributeType.EXCEPTIONS);

View File

@@ -57,6 +57,11 @@ public class Exception
return start; return start;
} }
public void setStart(Instruction ins)
{
start = ins;
}
public Instruction getEnd() public Instruction getEnd()
{ {
return end; return end;

View File

@@ -78,7 +78,7 @@ public class IllegalStateExceptions implements Deobfuscator
} }
if (!found) if (!found)
{ {
System.out.println("Unable to locate instruction ctx to remove stack for illegalstateexception " + ins + " in " + m); System.out.println("Unable to locate instruction ctx to remove stack for illegalstateexception " + ins.getType().getName() + " in method " + m.getName() + " class " + m.getMethods().getClassFile().getName());
continue; continue;
} }

View File

@@ -87,22 +87,36 @@ public class MethodInliner implements Deobfuscator
assert m != invokedMethod; assert m != invokedMethod;
// XXX do this later
// if (!invokedMethod.getDescriptor().getReturnValue().getType().equals("V")
// || invokedMethod.getDescriptor().size() != 0)
// {
// System.out.println(invokedMethod.getName());
// continue;
// }
int invokeIdx = ins.getInstructions().indexOf(i); int invokeIdx = ins.getInstructions().indexOf(i);
assert invokeIdx != -1; assert invokeIdx != -1;
int lvtIndex = code.getMaxLocals(), startLvtIndex = lvtIndex; int lvtIndex = code.getMaxLocals(),
//startLvtIndex = lvtIndex,
theirLocals = invokedMethod.getCode().getMaxLocals();
if (lvtIndex + theirLocals > 127)
continue;
if (invokedMethod.isSynchronized())
continue;
if (!invokedMethod.getCode().getExceptions().getExceptions().isEmpty())
continue;
// assign variables on stack to lvt // assign variables on stack to lvt
Signature descriptor = invokedMethod.getDescriptor(); Signature descriptor = invokedMethod.getDescriptor();
for (int j = 0; j < descriptor.size(); ++j)
Map<Integer, Integer> lvtIndexes = new HashMap<>();
for (int j = 0, idx = 0; j < descriptor.size(); ++j)
{
lvtIndexes.put(j, idx);
idx += descriptor.getTypeOfArg(j).getSlots();
}
for (int j = descriptor.size() - 1; j >= 0; --j)
{ {
Type type = descriptor.getTypeOfArg(j); Type type = descriptor.getTypeOfArg(j);
int paramLvtIndex = lvtIndexes.get(j);
// insert instruction to store top of stack in lvt // insert instruction to store top of stack in lvt
@@ -111,24 +125,25 @@ public class MethodInliner implements Deobfuscator
{ {
switch (type.getType()) switch (type.getType())
{ {
case "B":
case "Z": case "Z":
case "C": case "C":
case "S": case "S":
case "I": case "I":
storeIns = new IStore(ins, lvtIndex); storeIns = new IStore(ins, lvtIndex + paramLvtIndex);
lvtIndex += type.getSlots(); //lvtIndex += type.getSlots();
break; break;
case "J": case "J":
storeIns = new LStore(ins, lvtIndex); storeIns = new LStore(ins, lvtIndex + paramLvtIndex);
lvtIndex += type.getSlots(); //lvtIndex += type.getSlots();
break; break;
case "F": case "F":
storeIns = new FStore(ins, lvtIndex); storeIns = new FStore(ins, lvtIndex + paramLvtIndex);
lvtIndex += type.getSlots(); //lvtIndex += type.getSlots();
break; break;
case "D": case "D":
storeIns = new DStore(ins, lvtIndex); storeIns = new DStore(ins, lvtIndex + paramLvtIndex);
lvtIndex += type.getSlots(); //lvtIndex += type.getSlots();
break; break;
} }
} }
@@ -136,8 +151,8 @@ public class MethodInliner implements Deobfuscator
if (type.getArrayDims() != 0 || type.getType().startsWith("L")) if (type.getArrayDims() != 0 || type.getType().startsWith("L"))
{ {
assert storeIns == null; assert storeIns == null;
storeIns = new AStore(ins, lvtIndex); storeIns = new AStore(ins, lvtIndex + paramLvtIndex);
lvtIndex += type.getSlots(); //lvtIndex += type.getSlots();
} }
assert storeIns != null; assert storeIns != null;
@@ -145,7 +160,7 @@ public class MethodInliner implements Deobfuscator
ins.getInstructions().add(invokeIdx++, storeIns); ins.getInstructions().add(invokeIdx++, storeIns);
} }
inline(m, i, invokedMethod, startLvtIndex); inline(m, i, invokedMethod, /*start*/lvtIndex);
++inlineCount; ++inlineCount;
break; break;
} }
@@ -236,7 +251,16 @@ public class MethodInliner implements Deobfuscator
@Override @Override
public void run(ClassGroup group) public void run(ClassGroup group)
{ {
while (pass(group) > 0); int total = 0;
int i;
do
{
i = pass(group);
total += i;
}
while (i > 0);
System.out.println("[TOTAL] Inlined " + total + " methods");
} }
private int pass(ClassGroup group) private int pass(ClassGroup group)

View File

@@ -0,0 +1,85 @@
package info.sigterm.deob.deobfuscators;
import info.sigterm.deob.ClassFile;
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.Instructions;
import info.sigterm.deob.execution.Execution;
import java.util.ArrayList;
import java.util.List;
public class UnreachedCode implements Deobfuscator
{
private Execution execution;
private int removeUnused(Method m)
{
Instructions ins = m.getCode().getInstructions();
int count = 0;
List<Instruction> insCopy = new ArrayList<>(ins.getInstructions());
for (int j = 0; j < insCopy.size(); ++j)
//for (Instruction i : new ArrayList<>(ins.getInstructions()))
{
Instruction i = insCopy.get(j);
if (!execution.executed.contains(i))
{
for (Instruction i2 : i.from)
i2.jump.remove(i);
i.from.clear(); // if this is never executed, anything that jumps here ia also never executed?
// if this is an exception handler, the exception handler is never used...
for (info.sigterm.deob.attributes.code.Exception e : new ArrayList<>(m.getCode().getExceptions().getExceptions()))
{
if (e.getStart() == i)
{
e.setStart(insCopy.get(j + 1));
if (e.getStart() == e.getEnd())
{
m.getCode().getExceptions().remove(e);
continue;
}
}
if (e.getHandler() == i)
{
m.getCode().getExceptions().remove(e);
}
}
ins.remove(i);
++count;
}
}
return count;
}
@Override
public void run(ClassGroup group)
{
group.buildClassGraph();
execution = new Execution(group);
execution.populateInitialMethods();
execution.run();
int count = 0;
for (ClassFile cf : group.getClasses())
{
for (Method m : cf.getMethods().getMethods())
{
if (m.getCode() == null)
continue;
count += removeUnused(m);
}
}
System.out.println("Removed " + count + " unused instructions");
}
}

View File

@@ -21,7 +21,10 @@ public class UnusedBlocks implements Deobfuscator
for (Method m : new ArrayList<>(methods)) for (Method m : new ArrayList<>(methods))
{ {
if (m.getCode() == null) if (m.getCode() == null)
{
methods.remove(m);
continue; continue;
}
Instructions ins = m.getCode().getInstructions(); Instructions ins = m.getCode().getInstructions();
ins.buildBlocks(); ins.buildBlocks();

View File

@@ -4,6 +4,7 @@ import info.sigterm.deob.ClassFile;
import info.sigterm.deob.ClassGroup; import info.sigterm.deob.ClassGroup;
import info.sigterm.deob.Deob; import info.sigterm.deob.Deob;
import info.sigterm.deob.Method; import info.sigterm.deob.Method;
import info.sigterm.deob.attributes.code.Instruction;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
@@ -17,6 +18,7 @@ public class Execution
processedFrames = new ArrayList<>(); processedFrames = new ArrayList<>();
private List<Method> pendingMethods = new ArrayList<>(); // pending methods private List<Method> pendingMethods = new ArrayList<>(); // pending methods
public Set<Method> methods = new HashSet<>(); // all methods public Set<Method> methods = new HashSet<>(); // all methods
public Set<Instruction> executed = new HashSet<>(); // executed instructions
public Execution(ClassGroup group) public Execution(ClassGroup group)
{ {

View File

@@ -139,6 +139,8 @@ public class Frame
throw ex; throw ex;
} }
execution.executed.add(oldCur);
if (!executing) if (!executing)
break; break;