diff --git a/src/main/java/net/runelite/deob/Deob.java b/src/main/java/net/runelite/deob/Deob.java index f39768c3d4..9008a1ae50 100644 --- a/src/main/java/net/runelite/deob/Deob.java +++ b/src/main/java/net/runelite/deob/Deob.java @@ -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]); diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationDeobfuscator.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationDeobfuscator.java index 15a865fee6..adad743964 100644 --- a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationDeobfuscator.java +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationDeobfuscator.java @@ -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 getConstants(InstructionContext ctx) + private MultiplicationExpression parseExpression(InstructionContext ctx) + //private List getConstants(InstructionContext ctx) { - List 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 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 done = new HashSet<>(); private int runOnce() { group.buildClassGraph(); @@ -81,7 +184,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator e.populateInitialMethods(); e.run(); - Set 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 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 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; diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationExpression.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationExpression.java new file mode 100644 index 0000000000..31587ea53e --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/MultiplicationExpression.java @@ -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 instructions = new ArrayList<>(); // push constant instructions that are being multiplied + List 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; + } +} diff --git a/src/main/java/net/runelite/deob/execution/Execution.java b/src/main/java/net/runelite/deob/execution/Execution.java index 716649bf2d..c920535e83 100644 --- a/src/main/java/net/runelite/deob/execution/Execution.java +++ b/src/main/java/net/runelite/deob/execution/Execution.java @@ -23,6 +23,7 @@ public class Execution public Set executed = new HashSet<>(); // executed instructions private MultiValueMap invokes = new MultiValueMap<>(); private Encryption encryption; + public MultiValueMap contexts = new MultiValueMap<>(); public Execution(ClassGroup group) { @@ -102,4 +103,9 @@ public class Execution System.out.println("Processed " + fcount + " frames"); } + + public Collection getInstructonContexts(Instruction i) + { + return contexts.getCollection(i); + } } diff --git a/src/main/java/net/runelite/deob/execution/Frame.java b/src/main/java/net/runelite/deob/execution/Frame.java index 9761555f08..ade776f508 100644 --- a/src/main/java/net/runelite/deob/execution/Frame.java +++ b/src/main/java/net/runelite/deob/execution/Frame.java @@ -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);