Rewrite multi deob, this looks rather nice

This commit is contained in:
Adam
2015-09-26 19:31:07 -04:00
parent 5cc12d0228
commit 9a5d2c801f
5 changed files with 260 additions and 56 deletions

View File

@@ -84,11 +84,11 @@ public class Deob
//new ModArith().run(group);
//new MultiplicationDeobfuscator().run(group); // this causes spinning?
new MultiplicationDeobfuscator().run(group); // this causes spinning?
new MultiplyOneDeobfuscator().run(group);
new MultiplyZeroDeobfuscator().run(group);
// new MultiplyOneDeobfuscator().run(group);
//
// new MultiplyZeroDeobfuscator().run(group);
saveJar(group, args[1]);

View File

@@ -1,15 +1,18 @@
package net.runelite.deob.deobfuscators.arithmetic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import net.runelite.deob.ClassGroup;
import net.runelite.deob.Deobfuscator;
import net.runelite.deob.attributes.code.Instruction;
import net.runelite.deob.attributes.code.Instructions;
import net.runelite.deob.attributes.code.instruction.types.GetFieldInstruction;
import net.runelite.deob.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.deob.attributes.code.instructions.BiPush;
import net.runelite.deob.attributes.code.instructions.IAdd;
import net.runelite.deob.attributes.code.instructions.IMul;
import net.runelite.deob.attributes.code.instructions.ISub;
import net.runelite.deob.attributes.code.instructions.SiPush;
import net.runelite.deob.execution.Execution;
import net.runelite.deob.execution.Frame;
@@ -28,42 +31,141 @@ public class MultiplicationDeobfuscator implements Deobfuscator
this.group = group;
int i;
int count = 0;
while ((i = runOnce()) > 0)
{
System.out.println("Replaced " + i + " constants");
count += i;
}
System.out.println("Total changed " + count);
}
private List<InstructionContext> getConstants(InstructionContext ctx)
private MultiplicationExpression parseExpression(InstructionContext ctx)
//private List<InstructionContext> getConstants(InstructionContext ctx)
{
List<InstructionContext> l = new ArrayList<>();
MultiplicationExpression me = new MultiplicationExpression();
assert ctx.getInstruction() instanceof IMul;
//assert ctx.getInstruction() instanceof IMul;
//
if (ctx.getInstruction() instanceof PushConstantInstruction)
{
if (ctx.getInstruction() instanceof BiPush || ctx.getInstruction() instanceof SiPush)
{
throw new IllegalStateException();
}
// PushConstantInstruction pci = (PushConstantInstruction) ctx.getInstruction();
// int value = (int) pci.getConstant().getObject();
//
// if (value == 1)
// return null
me.instructions.add(ctx);
return me;
}
for (StackContext sctx : ctx.getPops())
{
InstructionContext i = sctx.getPushed();
if (i.getInstruction() instanceof IMul)
// if this instruction is imul, look at pops
if (ctx.getInstruction() instanceof IMul)
{
l.addAll(getConstants(i));
if (i.getInstruction() instanceof PushConstantInstruction)
{
if (i.getInstruction() instanceof BiPush || i.getInstruction() instanceof SiPush)
throw new IllegalStateException();
// a constant of imul
me.instructions.add(i);
}
else if (i.getInstruction() instanceof IMul)
{
// chained imul, append to me
try
{
MultiplicationExpression other = parseExpression(i);
me.instructions.addAll(other.instructions);
me.subexpressions.addAll(other.subexpressions);
}
catch (IllegalStateException ex)
{
// this is ok? just don't include it?
}
}
else if (i.getInstruction() instanceof IAdd || i.getInstruction() instanceof ISub)
{
// imul using result of iadd or isub. evaluate expression
try
{
MultiplicationExpression other = parseExpression(i);
// subexpr
//if (other != null)
me.subexpressions.add(other);
}
catch (IllegalStateException ex)
{
assert me.subexpressions.isEmpty();
// subexpression is too complex. we can still simplify the top level though
}
}
else if (i.getInstruction() instanceof GetFieldInstruction)
{
// non constant, ignore
}
else
{
//throw new IllegalStateException();
//System.out.println("imul pops something I don't know " + i.getInstruction());
}
}
else if (i.getInstruction() instanceof PushConstantInstruction)
// this is an iadd/sub
else if (ctx.getInstruction() instanceof IAdd || ctx.getInstruction() instanceof ISub)
{
PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
int value = (int) pci.getConstant().getObject();
if (value != 1) // already been touched, otherwise we keep multiplying the same ins over and over
l.add(i);
MultiplicationExpression other = parseExpression(i); // parse this side of the add/sub
//if (other != null)
me.subexpressions.add(other);
}
else
{
//throw new IllegalStateException();
//System.out.println(ctx.getInstruction() + " pops something I dont know " + i.getInstruction());
}
// else if (i.getInstruction() instanceof PushConstantInstruction)
// {
// me.instructions.add(i);
// //PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
// //int value = (int) pci.getConstant().getObject();
// //if (value != 1) // already been touched, otherwise we keep multiplying the same ins over and over
// // l.add(i);
// }
// else if (i.getInstruction() instanceof IAdd || i.getInstruction() instanceof ISub)
// {
// MultiplicationExpression other = parseExpression(i);
//
// me.subexpressions.add(other);
// }
}
return l;
if (me.instructions.isEmpty() && me.subexpressions.isEmpty())
throw new IllegalStateException();
//return null;
return me;
}
private boolean isOnlyPath(Execution execution, Frame frame, InstructionContext ctx)
private boolean isOnlyPath(Execution execution, InstructionContext ctx)
{
for (Frame f : execution.processedFrames)
if (f.getMethod() == frame.getMethod())
for (InstructionContext i : f.getInstructions())
if (i.getInstruction() == ctx.getInstruction())
Collection<InstructionContext> ins = execution.getInstructonContexts(ctx.getInstruction());
for (InstructionContext i : ins)
//for (Frame f : execution.processedFrames)
// if (f.getMethod() == frame.getMethod())
// for (InstructionContext i : f.getInstructions())
//if (i.getInstruction() == ctx.getInstruction())
{
if (!i.equals(ctx))
{
@@ -73,6 +175,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
return true;
}
Set<Instruction> done = new HashSet<>();
private int runOnce()
{
group.buildClassGraph();
@@ -81,7 +184,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
e.populateInitialMethods();
e.run();
Set<Instruction> done = new HashSet<>();
int count = 0;
for (Frame frame : e.processedFrames)
@@ -91,52 +194,87 @@ public class MultiplicationDeobfuscator implements Deobfuscator
Instruction instruction = ictx.getInstruction();
Instructions instructions = instruction.getInstructions();
// if (!frame.getMethod().getMethods().getClassFile().getName().equals("class114"))
// continue;
if (!(instruction instanceof IMul))
continue;
List<InstructionContext> ins = getConstants(ictx);
MultiplicationExpression expression;
try
{
expression = parseExpression(ictx);
}
catch (IllegalStateException ex)
{
continue;
}
if (ins.size() == 1)
if (expression == null)
continue;
for (InstructionContext i : ins)
{
if (done.contains(i.getInstruction()))
{
continue outer;
}
}
//if (expression.subexpressions.isEmpty())
// continue;
// there can only be one path to here, or else combinging would change code logic
if (!isOnlyPath(e, frame, ictx))
if (!isOnlyPath(e, ictx))
continue;
int result = 1;
if (done.contains(instruction))
continue;
done.add(instruction);
// calculate result
for (InstructionContext i : ins)
count += expression.simplify(1);
if (MultiplicationExpression.replace)
{
PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
int value = (int) pci.getConstant().getObject();
result *= value;
MultiplicationExpression.replace = false;
return count;
}
// set result on ins
for (InstructionContext i : ins)
{
PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
Instruction newIns = pci.setConstant(new net.runelite.deob.pool.Integer(result));
++count;
if (newIns != pci)
{
instructions.replace((Instruction) pci, newIns);
}
result = 1; // rest of the results go to 1
}
for (InstructionContext i : ins)
done.add(i.getInstruction());
//break;
// List<InstructionContext> ins = getConstants(ictx);
//
// if (ins.size() == 1)
// continue;
//
// for (InstructionContext i : ins)
// {
// if (done.contains(i.getInstruction()))
// {
// continue outer;
// }
// }
//
// // there can only be one path to here, or else combinging would change code logic
// if (!isOnlyPath(e, frame, ictx))
// continue;
//
// int result = 1;
//
// // calculate result
// for (InstructionContext i : ins)
// {
// PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
// int value = (int) pci.getConstant().getObject();
//
// result *= value;
// }
//
// // set result on ins
// for (InstructionContext i : ins)
// {
// PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
// Instruction newIns = pci.setConstant(new net.runelite.deob.pool.Integer(result));
// ++count;
// if (newIns != pci)
// {
// instructions.replace((Instruction) pci, newIns);
// }
// result = 1; // rest of the results go to 1
// }
//
// for (InstructionContext i : ins)
// done.add(i.getInstruction());
}
return count;

View File

@@ -0,0 +1,56 @@
package net.runelite.deob.deobfuscators.arithmetic;
import java.util.ArrayList;
import java.util.List;
import net.runelite.deob.attributes.code.Instruction;
import net.runelite.deob.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.deob.execution.InstructionContext;
public class MultiplicationExpression
{
List<InstructionContext> instructions = new ArrayList<>(); // push constant instructions that are being multiplied
List<MultiplicationExpression> subexpressions = new ArrayList<>(); // for distributing, each subexpr is * by this
static boolean replace;
int simplify(int start)
{
int count = 0;
int result = start;
// calculate result
for (InstructionContext i : instructions)
{
PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
int value = (int) pci.getConstant().getObject();
result *= value;
}
// multiply subexpressions by result
if (!subexpressions.isEmpty())
{
for (MultiplicationExpression me : subexpressions)
{
count += me.simplify(result);
}
result = 1; // constant has been distributed, outer numbers all go to 1
}
// set result on ins
for (InstructionContext i : instructions)
{
PushConstantInstruction pci = (PushConstantInstruction) i.getInstruction();
Instruction newIns = pci.setConstant(new net.runelite.deob.pool.Integer(result));
++count;
if (newIns != pci)
{
newIns.getInstructions().replace((Instruction) pci, newIns);
replace = true;
}
result = 1; // rest of the results go to 1
}
return count;
}
}

View File

@@ -23,6 +23,7 @@ public class Execution
public Set<Instruction> executed = new HashSet<>(); // executed instructions
private MultiValueMap<InstructionContext, Method> invokes = new MultiValueMap<>();
private Encryption encryption;
public MultiValueMap<Instruction, InstructionContext> contexts = new MultiValueMap<>();
public Execution(ClassGroup group)
{
@@ -102,4 +103,9 @@ public class Execution
System.out.println("Processed " + fcount + " frames");
}
public Collection<InstructionContext> getInstructonContexts(Instruction i)
{
return contexts.getCollection(i);
}
}

View File

@@ -174,6 +174,10 @@ public class Frame
throw ex;
}
InstructionContext ictx = this.instructions.get(this.instructions.size() - 1);
assert ictx.getInstruction() == oldCur;
execution.contexts.put(oldCur, ictx);
execution.executed.add(oldCur);
processExceptions(oldCur);