From 0a8d233083bf7fd93b3926462eaa008c76d18a35 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 23 Aug 2015 12:40:12 -0400 Subject: [PATCH] arith v2 --- .../java/net/runelite/deob/ClassFile.java | 5 + src/main/java/net/runelite/deob/Deob.java | 5 +- src/main/java/net/runelite/deob/Fields.java | 8 + .../code/instructions/GetField.java | 14 ++ .../code/instructions/GetStatic.java | 28 ++- .../deob/deobfuscators/arithmetic/DMath.java | 35 ++++ .../deobfuscators/arithmetic/Encryption.java | 25 +++ .../deobfuscators/arithmetic/ModArith.java | 189 ++++++++++++++++++ .../deob/deobfuscators/arithmetic/Pair.java | 9 + .../runelite/deob/execution/Execution.java | 7 + .../runelite/deob/execution/StackContext.java | 1 + 11 files changed, 314 insertions(+), 12 deletions(-) create mode 100644 src/main/java/net/runelite/deob/deobfuscators/arithmetic/DMath.java create mode 100644 src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java create mode 100644 src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java create mode 100644 src/main/java/net/runelite/deob/deobfuscators/arithmetic/Pair.java diff --git a/src/main/java/net/runelite/deob/ClassFile.java b/src/main/java/net/runelite/deob/ClassFile.java index 1ad261847d..0fe86823c7 100644 --- a/src/main/java/net/runelite/deob/ClassFile.java +++ b/src/main/java/net/runelite/deob/ClassFile.java @@ -148,6 +148,11 @@ public class ClassFile { return children; } + + public Field findField(String name) + { + return fields.findField(name); + } public Field findFieldDeep(NameAndType nat) { diff --git a/src/main/java/net/runelite/deob/Deob.java b/src/main/java/net/runelite/deob/Deob.java index 3995fdcab4..cf23be88ad 100644 --- a/src/main/java/net/runelite/deob/Deob.java +++ b/src/main/java/net/runelite/deob/Deob.java @@ -25,6 +25,7 @@ import net.runelite.deob.deobfuscators.UnusedClass; 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; //move static methods //move static fields @@ -102,7 +103,7 @@ public class Deob // System.out.println("unused methods took " + bdur/1000L + " seconds"); - new MethodInliner().run(group); + //new MethodInliner().run(group); // // new MethodMover().run(group); // @@ -115,6 +116,8 @@ public class Deob //new UnusedClass().run(group); // new ModularArithmeticDeobfuscation().run(group); + + new ModArith().run(group); saveJar(group, args[1]); diff --git a/src/main/java/net/runelite/deob/Fields.java b/src/main/java/net/runelite/deob/Fields.java index 293bc7fa1b..49e2ffcefb 100644 --- a/src/main/java/net/runelite/deob/Fields.java +++ b/src/main/java/net/runelite/deob/Fields.java @@ -50,4 +50,12 @@ public class Fields return f; return null; } + + public Field findField(String name) + { + for (Field f : fields) + if (f.getName().equals(name)) + return f; + return null; + } } diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java b/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java index 252f3aa2c4..9449bedc2c 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java @@ -17,6 +17,8 @@ import net.runelite.deob.pool.NameAndType; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import net.runelite.deob.deobfuscators.arithmetic.Encryption; +import net.runelite.deob.deobfuscators.arithmetic.Pair; public class GetField extends Instruction implements GetFieldInstruction { @@ -48,6 +50,18 @@ public class GetField extends Instruction implements GetFieldInstruction ins.pop(object); StackContext ctx = new StackContext(ins, new Type(field.getNameAndType().getDescriptorType()).toStackType()); + + Encryption encryption = frame.getExecution().getEncryption(); + net.runelite.deob.Field f = getMyField(); + if (f != null) + { + Pair pair = encryption.getField(f); + if (pair != null) + { + ctx.encryption = pair.getter; + } + } + stack.push(ctx); ins.push(ctx); diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/GetStatic.java b/src/main/java/net/runelite/deob/attributes/code/instructions/GetStatic.java index f2b69e97c9..5c57774614 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/GetStatic.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/GetStatic.java @@ -17,6 +17,8 @@ import net.runelite.deob.pool.NameAndType; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import net.runelite.deob.deobfuscators.arithmetic.Encryption; +import net.runelite.deob.deobfuscators.arithmetic.Pair; public class GetStatic extends Instruction implements GetFieldInstruction { @@ -45,6 +47,18 @@ public class GetStatic extends Instruction implements GetFieldInstruction Stack stack = frame.getStack(); StackContext ctx = new StackContext(ins, new Type(field.getNameAndType().getDescriptorType()).toStackType()); + + Encryption encryption = frame.getExecution().getEncryption(); + net.runelite.deob.Field f = getMyField(); + if (f != null) + { + Pair pair = encryption.getField(f); + if (pair != null) + { + ctx.encryption = pair.getter; + } + } + stack.push(ctx); ins.push(ctx); @@ -55,17 +69,9 @@ public class GetStatic extends Instruction implements GetFieldInstruction @Override public void buildInstructionGraph() { - Class clazz = field.getClassEntry(); - NameAndType nat = field.getNameAndType(); - - ClassFile cf = this.getInstructions().getCode().getAttributes().getClassFile().getGroup().findClass(clazz.getName()); - if (cf == null) - return; - - net.runelite.deob.Field f = cf.findFieldDeep(nat); - assert f != null; - - f.addReference(this); + net.runelite.deob.Field f = getMyField(); + if (f != null) + f.addReference(this); } @Override diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/DMath.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/DMath.java new file mode 100644 index 0000000000..bb3aa9fbb5 --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/DMath.java @@ -0,0 +1,35 @@ +package net.runelite.deob.deobfuscators.arithmetic; + +import java.math.BigInteger; + +public class DMath +{ + public static BigInteger modInverse(BigInteger val, int bits) + { + BigInteger shift = BigInteger.ONE.shiftLeft(bits); + return val.modInverse(shift); + } + + public static int modInverse(int val) + { + return modInverse(BigInteger.valueOf(val), 32).intValue(); + } + + public static long modInverse(long val) + { + return modInverse(BigInteger.valueOf(val), 64).longValue(); + } + + public static boolean isInversable(int val) + { + try + { + modInverse(val); + return true; + } + catch (ArithmeticException ex) + { + return false; + } + } +} diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java new file mode 100644 index 0000000000..c6f7c8f435 --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Encryption.java @@ -0,0 +1,25 @@ +package net.runelite.deob.deobfuscators.arithmetic; + +import java.util.HashMap; +import java.util.Map; +import net.runelite.deob.Field; + +public class Encryption +{ + private Map fields = new HashMap<>(); + + public Pair getField(Field field) + { + if (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; + } + return null; + //return fields.get(field); + } +} diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java new file mode 100644 index 0000000000..254917cd09 --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/ModArith.java @@ -0,0 +1,189 @@ +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.ClassGroup; +import net.runelite.deob.Deobfuscator; +import net.runelite.deob.Field; +import net.runelite.deob.attributes.code.Instruction; +import net.runelite.deob.attributes.code.instruction.types.FieldInstruction; +import net.runelite.deob.attributes.code.instruction.types.GetFieldInstruction; +import net.runelite.deob.attributes.code.instruction.types.InvokeInstruction; +import net.runelite.deob.attributes.code.instruction.types.PushConstantInstruction; +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; + +/* +store an encryption context on stack context that shows the value the ctx is encrypted with +*/ + +public class ModArith implements Deobfuscator +{ + private ClassGroup group; + private Execution execution; + private MultiValueMap fieldIns = new MultiValueMap<>(); + + private void findGetField(InstructionContext ctx) + { + + } + + private void findUses() + { + //List list = new ArrayList<>(); + + for (Frame f : execution.processedFrames) + for (InstructionContext ctx : f.getInstructions()) + { + Instruction i = ctx.getInstruction(); + + if (!(i instanceof FieldInstruction)) + continue; + + FieldInstruction fi = (FieldInstruction) i; + + Field fifield = fi.getMyField(); + + if (fifield == null) + continue; + + fieldIns.put(fifield, ctx); +// if (i instanceof GetFieldInstruction) +// { +// findGetField(ctx); +// } + } + + //return list; +// for (ClassFile cf : group.getClasses()) +// for (Field f : cf.getFields().getFields()) +// { +// +// } + } + + public void calculate(Field field) + { + Collection c = fieldIns.getCollection(field); + if (c == null) + return; + + List constants = new ArrayList<>(); + for (InstructionContext ctx : c) + { + if (ctx.getInstruction() instanceof GetFieldInstruction) + { + List fields = getFieldsInExpression(ctx, constants); + if (fields.size() == 1) + { + } + } + } + + 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) + { + return check(ctx, new HashSet(), constants); + } + + private List check(InstructionContext context, Set visited, List constants) + { + List fields = new ArrayList<>(); + + if (visited.contains(context)) + return fields; + + visited.add(context); + + if (context.getInstruction() instanceof InvokeInstruction) + { + // field = func(field * constant), the output of the function isn't directly related to the result of field * constant + return fields; + } + + if (context.getInstruction() instanceof FieldInstruction) + { + FieldInstruction fi = (FieldInstruction) context.getInstruction(); + Field myf = fi.getMyField(); + if (myf != null) + fields.add(myf); + } + + if (context.getInstruction() instanceof PushConstantInstruction) + { + PushConstantInstruction pci = (PushConstantInstruction) context.getInstruction(); + int i = (int) pci.getConstant().getObject(); + constants.add(i); + } + + for (StackContext ctx : context.getPops()) + { + InstructionContext i = ctx.getPushed(); + + fields.addAll(check(i, visited, constants)); + } + + for (StackContext ctx : context.getPushes()) + { + InstructionContext i = ctx.getPopped(); + + if (i == null) + continue; + + fields.addAll(check(i, visited, constants)); + } + + return fields; + } + +// private void replace(Pair pair) +// { +// // do replacements with pair +// +// for (Frame frame : execution.processedFrames) +// { +// for (InstructionContext ctx : frame.getInstructions()) +// { +// } +// } +// } + + @Override + public void run(ClassGroup group) + { + this.group = group; + group.buildClassGraph(); + + execution = new Execution(group); + execution.populateInitialMethods(); + execution.run(); + + findUses(); + + Field f = group.findClass("class41").findField("field1170"); + calculate(f); + } + +} diff --git a/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Pair.java b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Pair.java new file mode 100644 index 0000000000..f68d9eea8d --- /dev/null +++ b/src/main/java/net/runelite/deob/deobfuscators/arithmetic/Pair.java @@ -0,0 +1,9 @@ +package net.runelite.deob.deobfuscators.arithmetic; + +import net.runelite.deob.Field; + +public class Pair +{ + public Field field; + public int getter, setter; +} diff --git a/src/main/java/net/runelite/deob/execution/Execution.java b/src/main/java/net/runelite/deob/execution/Execution.java index d216e6c9f6..5e3ba5bdd5 100644 --- a/src/main/java/net/runelite/deob/execution/Execution.java +++ b/src/main/java/net/runelite/deob/execution/Execution.java @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; +import net.runelite.deob.deobfuscators.arithmetic.Encryption; import org.apache.commons.collections4.map.MultiValueMap; public class Execution @@ -21,11 +22,17 @@ public class Execution public Set methods = new HashSet<>(); // all methods public Set executed = new HashSet<>(); // executed instructions private MultiValueMap invokes = new MultiValueMap<>(); + private Encryption encryption; public Execution(ClassGroup group) { this.group = group; } + + public Encryption getEncryption() + { + return encryption; + } public void populateInitialMethods() { diff --git a/src/main/java/net/runelite/deob/execution/StackContext.java b/src/main/java/net/runelite/deob/execution/StackContext.java index 92fc0f3f93..78d4a75f9d 100644 --- a/src/main/java/net/runelite/deob/execution/StackContext.java +++ b/src/main/java/net/runelite/deob/execution/StackContext.java @@ -9,6 +9,7 @@ public class StackContext public InstructionContext popped; // instruction which popped this public Type type; // type of this public boolean removed; + public int encryption; // if this value is encrypted, this is the key to get the real value public StackContext(InstructionContext pushed, Type type) {