Try and replace unused blocks with unreached code, seeing some problems somewhere
This commit is contained in:
@@ -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]);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ public class Frame
|
|||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
execution.executed.add(oldCur);
|
||||||
|
|
||||||
if (!executing)
|
if (!executing)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user