From 88bd6490de1353367bca9bde37abb3628f07ad85 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 5 Sep 2015 23:19:38 -0400 Subject: [PATCH] Work. --- src/main/java/net/runelite/deob/Deob.java | 84 ++++---- .../attributes/code/instructions/IMul.java | 4 - .../code/instructions/PutField.java | 85 +++++++- .../code/instructions/PutStatic.java | 173 +++++++++-------- .../deobfuscators/arithmetic/Encryption.java | 29 +-- .../deobfuscators/arithmetic/ModArith.java | 182 +++++++++++------- 6 files changed, 332 insertions(+), 225 deletions(-) diff --git a/src/main/java/net/runelite/deob/Deob.java b/src/main/java/net/runelite/deob/Deob.java index cf23be88ad..f892934373 100644 --- a/src/main/java/net/runelite/deob/Deob.java +++ b/src/main/java/net/runelite/deob/Deob.java @@ -26,6 +26,7 @@ import net.runelite.deob.deobfuscators.UnusedFields; import net.runelite.deob.deobfuscators.UnusedMethods; import net.runelite.deob.deobfuscators.UnusedParameters; import net.runelite.deob.deobfuscators.arithmetic.ModArith; +import net.runelite.deob.execution.Execution; //move static methods //move static fields @@ -41,7 +42,6 @@ public class Deob long start = System.currentTimeMillis(); ClassGroup group = loadJar(args[0]); - long bstart, bdur; // bstart = System.currentTimeMillis(); // new RenameUnique().run(group); @@ -49,71 +49,45 @@ public class Deob // System.out.println("rename unique took " + bdur/1000L + " seconds"); // // remove except RuntimeException -// bstart = System.currentTimeMillis(); -// new RuntimeExceptions().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("runtime exception took " + bdur/1000L + " seconds"); +// run(group, new RuntimeExceptions()); // // // remove unused methods -// bstart = System.currentTimeMillis(); -// new UnusedMethods().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused methods took " + bdur/1000L + " seconds"); +// run(group, new UnusedMethods()); // -// new UnreachedCode().run(group); +// run(group, new UnreachedCode()); // // // remove illegal state exceptions, frees up some parameters -// bstart = System.currentTimeMillis(); -// new IllegalStateExceptions().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("illegal state exception took " + bdur/1000L + " seconds"); +// run(group, new IllegalStateExceptions()); // // // remove constant logically dead parameters -// bstart = System.currentTimeMillis(); -// new ConstantParameter().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("constant param took " + bdur/1000L + " seconds"); +// run(group, new ConstantParameter()); // // // remove unhit blocks -// bstart = System.currentTimeMillis(); -// new UnreachedCode().run(group); -// //new UnusedBlocks().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused blocks took " + bdur/1000L + " seconds"); +// run(group, new UnreachedCode()); // // // remove unused parameters -// bstart = System.currentTimeMillis(); -// new UnusedParameters().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused params took " + bdur/1000L + " seconds"); +// run(group, new UnusedParameters()); // // // remove jump obfuscation // //new Jumps().run(group); // // // remove unused fields -// bstart = System.currentTimeMillis(); -// new UnusedFields().run(group); -// bdur = System.currentTimeMillis() - bstart; -// System.out.println("unused fields took " + bdur/1000L + " seconds"); +// run(group, new UnusedFields()); // // // 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 MethodInliner().run(group); -// -// new MethodMover().run(group); +// run(group, new UnusedMethods()); // -// new FieldInliner().run(group); - - // XXX this is broken because when moving clinit around, some fields can depend on other fields - // (like multianewarray) - //new FieldMover().run(group); - - //new UnusedClass().run(group); +// run(group, new MethodInliner()); +// +// run(group, new MethodMover()); +// +// run(group, new FieldInliner()); +// +// // XXX this is broken because when moving clinit around, some fields can depend on other fields +// // (like multianewarray) +// //new FieldMover().run(group); +// +// run(group, new UnusedClass()); // new ModularArithmeticDeobfuscation().run(group); @@ -168,4 +142,20 @@ public class Deob jout.close(); } + + private static void run(ClassGroup group, Deobfuscator deob) + { + long bstart, bdur; + + bstart = System.currentTimeMillis(); + deob.run(group); + bdur = System.currentTimeMillis() - bstart; + + System.out.println(deob.getClass().getName() + " took " + (bdur / 1000L) + " seconds"); + + // check code is still correct + Execution execution = new Execution(group); + execution.populateInitialMethods(); + execution.run(); + } } \ No newline at end of file diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/IMul.java b/src/main/java/net/runelite/deob/attributes/code/instructions/IMul.java index a8911d329a..61955fb267 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/IMul.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/IMul.java @@ -45,8 +45,6 @@ public class IMul extends Instruction { int o = other * DMath.modInverse(one.encryption); - System.out.println(other + " -> " + o); - encryption.change(pci, o); } @@ -64,8 +62,6 @@ public class IMul extends Instruction { int o = other * DMath.modInverse(two.encryption); - System.out.println(other + " -> " + o); - encryption.change(pci, o); } } diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/PutField.java b/src/main/java/net/runelite/deob/attributes/code/instructions/PutField.java index 400cc99524..af6c42cd8c 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/PutField.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/PutField.java @@ -16,6 +16,10 @@ import net.runelite.deob.pool.NameAndType; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.List; +import net.runelite.deob.attributes.code.instruction.types.PushConstantInstruction; +import net.runelite.deob.deobfuscators.arithmetic.Encryption; +import net.runelite.deob.deobfuscators.arithmetic.Pair; public class PutField extends Instruction implements SetFieldInstruction { @@ -43,10 +47,89 @@ public class PutField extends Instruction implements SetFieldInstruction InstructionContext ins = new InstructionContext(this, frame); Stack stack = frame.getStack(); - StackContext object = stack.pop(); StackContext value = stack.pop(); + StackContext object = stack.pop(); ins.pop(object, value); + Encryption encryption = frame.getExecution().getEncryption(); + net.runelite.deob.Field myField = getMyField(); + if (encryption != null && myField != null) + { + Pair pair = encryption.getField(myField); + InstructionContext ctx = value.getPushed(); + if (ctx.getInstruction() instanceof IAdd && pair != null) + { + // field += constant * crap; + // in bytecode is really + // field = field + constant * crap + + List pops = ctx.getPops(); + + if (pops.get(0).getPushed().getInstruction() instanceof IMul) + { + ctx = pops.get(0).getPushed(); + } + else if (pops.get(1).getPushed().getInstruction() instanceof IMul) + { + ctx = pops.get(1).getPushed(); + } + } + if (ctx.getInstruction() instanceof PushConstantInstruction && pair != null) + { + // field = encryptedvalue + // decrypt value by * getter + + PushConstantInstruction pci = (PushConstantInstruction) ctx.getInstruction(); + int v = (int) pci.getConstant().getObject(); + + if (v != 0 && v != 1) + { + v = v * pair.getter; + + encryption.change(pci, v); + } + } + if (ctx.getInstruction() instanceof ISub) + { + List stackCtx = ctx.getPops(); + + StackContext one = stackCtx.get(0), two = stackCtx.get(1); + + if (one.getPushed().getInstruction() instanceof IMul) + { + ctx = one.getPushed(); + } + else if (two.getPushed().getInstruction() instanceof IMul) + { + ctx = two.getPushed(); + } + } + if (ctx.getInstruction() instanceof IMul && pair != null) + { + List stackCtx = ctx.getPops(); + + StackContext one = stackCtx.get(0), two = stackCtx.get(1); + + StackContext magicStack = PutStatic.findMagic(one, two); + + if (magicStack != null) + { + PushConstantInstruction pci = (PushConstantInstruction) magicStack.getPushed().getInstruction(); + int v = (int) pci.getConstant().getObject(); + + // field is encrypted with pair + // divide value by setter + + if (v != 0 && v != 1) + { + v = v * pair.getter; + + encryption.change(pci, v); + } + } + } + } + frame.addInstructionContext(ins); } diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/PutStatic.java b/src/main/java/net/runelite/deob/attributes/code/instructions/PutStatic.java index a51c1402d9..34e6a346bf 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/PutStatic.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/PutStatic.java @@ -42,7 +42,7 @@ public class PutStatic extends Instruction implements SetFieldInstruction out.writeShort(this.getPool().make(field)); } - private static StackContext findMagic(StackContext one, StackContext two) + protected static StackContext findMagic(StackContext one, StackContext two) { if (one.getPushed().getInstruction() instanceof PushConstantInstruction) { @@ -68,6 +68,44 @@ public class PutStatic extends Instruction implements SetFieldInstruction return null; } + + private static boolean translate(Encryption encryption, Pair pair, InstructionContext ctx) + { + if (ctx.getInstruction() instanceof LDC_W) + { + LDC_W pci = (LDC_W) ctx.getInstruction(); + int value = (int) pci.getConstant().getObject(); + + if (encryption.hasChange(pci)) + return true; + + if (value != 0 && value != 1) + { + value = value * pair.getter; + + encryption.change(pci, value); + } + + return true; + } + + boolean multipleBranches = ctx.getInstruction() instanceof IAdd || ctx.getInstruction() instanceof ISub; + boolean retVal = false; + + for (StackContext sctx : ctx.getPops()) + { + InstructionContext i = sctx.getPushed(); + + if (translate(encryption, pair, i)) + { + retVal = true; + if (!multipleBranches) + break; + } + } + + return retVal; + } @Override public void execute(Frame frame) @@ -83,80 +121,67 @@ public class PutStatic extends Instruction implements SetFieldInstruction if (encryption != null && myField != null) { Pair pair = encryption.getField(myField); - InstructionContext ctx = object.getPushed(); - if (ctx.getInstruction() instanceof PushConstantInstruction && pair != null) - { - // field = encryptedvalue - // decrypt value by * getter - - PushConstantInstruction pci = (PushConstantInstruction) ctx.getInstruction(); - int value = (int) pci.getConstant().getObject(); - - if (value != 0 && value != 1) - { - value = value * pair.getter; - - encryption.change(pci, value); - } - } - if (ctx.getInstruction() instanceof ISub) - { - List stackCtx = ctx.getPops(); - - StackContext one = stackCtx.get(0), two = stackCtx.get(1); - - if (one.getPushed().getInstruction() instanceof IMul) - { - ctx = one.getPushed(); - } - else if (two.getPushed().getInstruction() instanceof IMul) - { - ctx = two.getPushed(); - } - } - if (ctx.getInstruction() instanceof IMul && pair != null) - { - List stackCtx = ctx.getPops(); - - StackContext one = stackCtx.get(0), two = stackCtx.get(1); - - StackContext magicStack = findMagic(one, two); - - if (magicStack != null) - { - PushConstantInstruction pci = (PushConstantInstruction) magicStack.getPushed().getInstruction(); - int value = (int) pci.getConstant().getObject(); - - // field is encrypted with pair - // divide value by setter - - if (value != 0 && value != 1) - { - value = value * pair.getter; - - encryption.change(pci, value); - } - } - -// if (one.getPushed().getInstruction() instanceof PushConstantInstruction) + if (pair != null) + translate(encryption, pair, ins); +// InstructionContext ctx = object.getPushed(); +// if (ctx.getInstruction() instanceof IAdd && pair != null) +// { +// // field += constant * crap; +// // in bytecode is really +// // field = field + constant * crap +// +// List pops = ctx.getPops(); +// +// if (pops.get(0).getPushed().getInstruction() instanceof IMul) // { -// PushConstantInstruction pci = (PushConstantInstruction) one.getPushed().getInstruction(); -// int value = (int) pci.getConstant().getObject(); -// -// // field is encrypted with pair -// // divide value by setter -// -// if (value != 0 && value != 1) -// { -// value = value * pair.getter; -// -// encryption.change(pci, value); -// } -// +// ctx = pops.get(0).getPushed(); // } -// else if (two.getPushed().getInstruction() instanceof PushConstantInstruction) +// else if (pops.get(1).getPushed().getInstruction() instanceof IMul) // { -// PushConstantInstruction pci = (PushConstantInstruction) two.getPushed().getInstruction(); +// ctx = pops.get(1).getPushed(); +// } +// } +// if (ctx.getInstruction() instanceof PushConstantInstruction && pair != null) +// { +// // field = encryptedvalue +// // decrypt value by * getter +// +// PushConstantInstruction pci = (PushConstantInstruction) ctx.getInstruction(); +// int value = (int) pci.getConstant().getObject(); +// +// if (value != 0 && value != 1) +// { +// value = value * pair.getter; +// +// encryption.change(pci, value); +// } +// } +// if (ctx.getInstruction() instanceof ISub) +// { +// List stackCtx = ctx.getPops(); +// +// StackContext one = stackCtx.get(0), two = stackCtx.get(1); +// +// if (one.getPushed().getInstruction() instanceof IMul) +// { +// ctx = one.getPushed(); +// } +// else if (two.getPushed().getInstruction() instanceof IMul) +// { +// ctx = two.getPushed(); +// } +// } +// if (ctx.getInstruction() instanceof IMul && pair != null) +// { +// List stackCtx = ctx.getPops(); +// +// StackContext one = stackCtx.get(0), two = stackCtx.get(1); +// +// StackContext magicStack = findMagic(one, two); +// +// if (magicStack != null) +// { +// PushConstantInstruction pci = (PushConstantInstruction) magicStack.getPushed().getInstruction(); // int value = (int) pci.getConstant().getObject(); // // // field is encrypted with pair @@ -169,9 +194,7 @@ public class PutStatic extends Instruction implements SetFieldInstruction // encryption.change(pci, value); // } // } -// else -// assert false; - } +// } } frame.addInstructionContext(ins); diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java index ec055c10cc..cf66fa7653 100644 --- a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java @@ -5,7 +5,6 @@ import java.util.Map; import java.util.Map.Entry; import net.runelite.deob.Field; import net.runelite.deob.attributes.code.instruction.types.PushConstantInstruction; -import net.runelite.deob.attributes.code.instructions.SiPush; public class Encryption { @@ -19,34 +18,16 @@ public class Encryption public Pair getField(Field field) { -// if (i == 0 && field.getName().equals("field1170")) -// { -// Pair p = new Pair(); -// p.field = field; -// p.getter = -1570098313; -// p.setter = DMath.modInverse(p.getter); -// assert p.setter == 1237096007; -// return p; -// } -// if (i == 1 && field.getName().equals("field700")) -// { -// Pair p = new Pair(); -// p.field = field; -// p.getter = -478315765; -// p.setter = DMath.modInverse(p.getter); -// //assert p.setter == -// return p; -// } -// return null; return fields.get(field); } + public boolean hasChange(PushConstantInstruction pci) + { + return changes.containsKey(pci); + } + public void change(PushConstantInstruction pci, int value) { - if (pci instanceof SiPush) - { - int i =5; - } assert !changes.containsKey(pci) || changes.get(pci) == value; changes.put(pci, value); } diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java index 9ac0613472..6310cc50e6 100644 --- a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java @@ -2,11 +2,10 @@ package net.runelite.deob.deobfuscators.arithmetic; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; +import net.runelite.deob.ClassFile; import net.runelite.deob.ClassGroup; import net.runelite.deob.Deobfuscator; import net.runelite.deob.Field; @@ -21,7 +20,6 @@ import net.runelite.deob.execution.Execution; import net.runelite.deob.execution.Frame; import net.runelite.deob.execution.InstructionContext; import net.runelite.deob.execution.StackContext; -import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.map.MultiValueMap; /* @@ -32,20 +30,12 @@ public class ModArith implements Deobfuscator { private ClassGroup group; private Execution execution; - private MultiValueMap constants = new MultiValueMap<>(); - //private MultiValueMap fieldIns = new MultiValueMap<>(); - + private MultiValueMap constantGetters = new MultiValueMap<>(), + constantSetters = new MultiValueMap<>(); + private List pairs = new ArrayList<>(); - -// private void findGetField(InstructionContext ctx) -// { -// -// } - private void findUses() - { - //List list = new ArrayList<>(); - + { for (Frame f : execution.processedFrames) for (InstructionContext ctx : f.getInstructions()) { @@ -71,9 +61,12 @@ public class ModArith implements Deobfuscator continue; Field field = gf.getMyField(); + if (field == null) + continue; + int value = (int) pc.getConstant().getObject(); - constants.put(field, value); + constantGetters.put(field, value); } else if (ctx.getInstruction() instanceof SetFieldInstruction) { @@ -103,67 +96,113 @@ public class ModArith implements Deobfuscator continue; Field field = sf.getMyField(); + if (field == null) + continue; + int value2 = (int) pc.getConstant().getObject(); - constants.put(field, value2); + constantSetters.put(field, value2); } } } + private Pair reduce(Collection getters, Collection setters) + { + Pair p = null; + + for (Integer i : getters) + { + Integer inverse; + try + { + inverse = DMath.modInverse(i); + } + catch (ArithmeticException ex) + { + continue; + } + + if (setters.contains(inverse)) + { + if (p != null && p.getter != i) + return null; + + if (p == null) + { + p = new Pair(); + p.getter = i; + p.setter = inverse; + } + } + } + + for (Integer i : setters) + { + Integer inverse; + try + { + inverse = DMath.modInverse(i); + } + catch (ArithmeticException ex) + { + continue; + } + + if (getters.contains(inverse)) + { + if (p != null && p.setter != i) + return null; + + if (p == null) + { + p = new Pair(); + p.setter = i; + p.getter = inverse; + } + } + } + + return p; + } + private void reduce() { - MultiValueMap values = constants; - constants = new MultiValueMap<>(); - - for (Field field : values.keySet()) - { - Collection col = values.getCollection(field); - - Map map = CollectionUtils.getCardinalityMap(col); - int max = Collections.max(map.values()); - - for (final Map.Entry entry : map.entrySet()) { - if (max == entry.getValue()) { - int constant = entry.getKey(); - - constants.put(field, constant); - break; - } + for (ClassFile cf : group.getClasses()) + for (Field f : cf.getFields().getFields()) + { + Collection getters = constantGetters.getCollection(f), + setters = constantSetters.getCollection(f); + + if (getters == null || setters == null) + continue; + + Pair answer = reduce(getters, setters); + if (answer == null) + continue; + + answer.field = f; + pairs.add(answer); } - } - } - -// public void calculate(Field field) -// { -// Collection c = fieldIns.getCollection(field); -// if (c == null) -// return; +// MultiValueMap values = constants; +// constants = new MultiValueMap<>(); // -// List constants = new ArrayList<>(); -// for (InstructionContext ctx : c) +// for (Field field : values.keySet()) // { -// if (ctx.getInstruction() instanceof GetFieldInstruction) -// { -// List fields = getFieldsInExpression(ctx, constants); -// if (fields.size() == 1) -// { +// Collection col = values.getCollection(field); +// +// Map map = CollectionUtils.getCardinalityMap(col); +// int max = Collections.max(map.values()); +// +// for (final Map.Entry entry : map.entrySet()) { +// if (max == entry.getValue()) { +// int constant = entry.getKey(); +// +// constants.put(field, constant); +// break; // } // } // } -// -// Map map = CollectionUtils.getCardinalityMap(constants); -// int max = Collections.max(map.values()); -// -// for (final Map.Entry entry : map.entrySet()) { -// if (max == entry.getValue()) { -// int constant = entry.getKey(); -// -// System.out.println(constant); -// assert DMath.isInversable(constant); -// break; -// } -// } -// } + } private List getFieldsInExpression(InstructionContext ctx, List constants) { @@ -234,19 +273,14 @@ public class ModArith implements Deobfuscator reduce(); int i = 0; - for (Field field : constants.keySet()) + for (Pair pair : pairs) { - System.out.println("Processing " + field.getName()); - int getter = constants.getCollection(field).iterator().next(); + Field field = pair.field; + System.out.println("Processing " + field.getName() + " getter " + pair.getter + " setter " + pair.setter); - if (i > 50) + if (i > 10) // 25 break; - Pair pair = new Pair(); - pair.field = field; - pair.getter = getter; - pair.setter = DMath.modInverse(getter); - Encryption encr = new Encryption(); encr.addPair(pair); @@ -256,11 +290,11 @@ public class ModArith implements Deobfuscator execution.run(); encr.doChange(); - System.out.println("Changed" + ++i); + System.out.println("Changed " + ++i); } Encryption encr = new Encryption(); - System.out.println(constants); + System.out.println(pairs); // execution = new Execution(group); // execution.populateInitialMethods();