From d6c7d7dc8ad393d28a7c0b5bf723396ba6f9ed16 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 3 Apr 2016 15:53:13 -0400 Subject: [PATCH] playing with less memory --- .../net/runelite/asm/execution/Execution.java | 23 +- .../asm/execution/ExecutionVisitor.java | 6 + .../net/runelite/asm/execution/Frame.java | 2 + .../asm/execution/InstructionContext.java | 16 + .../asm/execution/WeakInstructionContext.java | 58 +++ .../deob/deobfuscators/ConstantParameter.java | 491 +++++++++++------- 6 files changed, 415 insertions(+), 181 deletions(-) create mode 100644 src/main/java/net/runelite/asm/execution/ExecutionVisitor.java create mode 100644 src/main/java/net/runelite/asm/execution/WeakInstructionContext.java diff --git a/src/main/java/net/runelite/asm/execution/Execution.java b/src/main/java/net/runelite/asm/execution/Execution.java index eecee4dbca..0c1f9008b1 100644 --- a/src/main/java/net/runelite/asm/execution/Execution.java +++ b/src/main/java/net/runelite/asm/execution/Execution.java @@ -17,14 +17,14 @@ import org.apache.commons.collections4.map.MultiValueMap; public class Execution { private ClassGroup group; - public List frames = new LinkedList<>(), - processedFrames = new LinkedList<>(); + public List frames = new LinkedList<>(); public Set methods = new HashSet<>(); // all methods public Set executed = new HashSet<>(); // executed instructions - private MultiValueMap invokes = new MultiValueMap<>(); - public MultiValueMap contexts = new MultiValueMap<>(); + private MultiValueMap invokes = new MultiValueMap<>(); + public MultiValueMap contexts = new MultiValueMap<>(); // XXX this should move to method ctx probably public boolean paused; public boolean step = false; + private List visitors = new ArrayList<>(); public Execution(ClassGroup group) { @@ -76,11 +76,11 @@ public class Execution public boolean hasInvoked(InstructionContext from, Method to) { - Collection methods = invokes.getCollection(from); + Collection methods = invokes.getCollection(from.toWeak()); if (methods != null && methods.contains(to)) return true; - invokes.put(from, to); + invokes.put(from.toWeak(), to); return false; } @@ -128,7 +128,6 @@ public class Execution assert !frame.isExecuting(); frames.remove(frame); - processedFrames.add(frame); } System.out.println("Processed " + fcount + " frames"); @@ -138,4 +137,14 @@ public class Execution { return contexts.getCollection(i); } + + public void addExecutionVisitor(ExecutionVisitor ev) + { + this.visitors.add(ev); + } + + public void accept(InstructionContext ic) + { + visitors.forEach(v -> v.visit(ic)); + } } diff --git a/src/main/java/net/runelite/asm/execution/ExecutionVisitor.java b/src/main/java/net/runelite/asm/execution/ExecutionVisitor.java new file mode 100644 index 0000000000..68d8fabdb0 --- /dev/null +++ b/src/main/java/net/runelite/asm/execution/ExecutionVisitor.java @@ -0,0 +1,6 @@ +package net.runelite.asm.execution; + +public interface ExecutionVisitor +{ + void visit(InstructionContext context); +} diff --git a/src/main/java/net/runelite/asm/execution/Frame.java b/src/main/java/net/runelite/asm/execution/Frame.java index ea55dfe568..212d5ef34c 100644 --- a/src/main/java/net/runelite/asm/execution/Frame.java +++ b/src/main/java/net/runelite/asm/execution/Frame.java @@ -239,6 +239,8 @@ public class Frame execution.contexts.put(oldCur, ictx); execution.executed.add(oldCur); + + execution.accept(ictx); processExceptions(ictx); diff --git a/src/main/java/net/runelite/asm/execution/InstructionContext.java b/src/main/java/net/runelite/asm/execution/InstructionContext.java index ae34911dd0..b6796ed731 100644 --- a/src/main/java/net/runelite/asm/execution/InstructionContext.java +++ b/src/main/java/net/runelite/asm/execution/InstructionContext.java @@ -209,4 +209,20 @@ public class InstructionContext return ctx; } + + public WeakInstructionContext toWeak() + { + WeakInstructionContext wic = new WeakInstructionContext(this.getInstruction()); + + for (StackContext sctx : stack.getStack()) + { + if (sctx == null) + break; + + InstructionContext i = sctx.getPushed(); + wic.addStack(i.getInstruction()); + } + + return wic; + } } diff --git a/src/main/java/net/runelite/asm/execution/WeakInstructionContext.java b/src/main/java/net/runelite/asm/execution/WeakInstructionContext.java new file mode 100644 index 0000000000..713443dc81 --- /dev/null +++ b/src/main/java/net/runelite/asm/execution/WeakInstructionContext.java @@ -0,0 +1,58 @@ +package net.runelite.asm.execution; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import net.runelite.asm.attributes.code.Instruction; + +public class WeakInstructionContext +{ + private Instruction ins; + private List stack = new ArrayList<>(); + + public WeakInstructionContext(Instruction ins) + { + this.ins = ins; + } + + public void addStack(Instruction i) + { + stack.add(i); + } + + @Override + public int hashCode() + { + int hash = 3; + hash = 37 * hash + Objects.hashCode(this.ins); + hash = 37 * hash + Objects.hashCode(this.stack); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final WeakInstructionContext other = (WeakInstructionContext) obj; + if (!Objects.equals(this.ins, other.ins)) + { + return false; + } + if (!Objects.equals(this.stack, other.stack)) + { + return false; + } + return true; + } +} diff --git a/src/main/java/net/runelite/deob/deobfuscators/ConstantParameter.java b/src/main/java/net/runelite/deob/deobfuscators/ConstantParameter.java index 3d46e249b9..eb8928b89a 100644 --- a/src/main/java/net/runelite/deob/deobfuscators/ConstantParameter.java +++ b/src/main/java/net/runelite/deob/deobfuscators/ConstantParameter.java @@ -39,6 +39,8 @@ class ConstantMethodParameter public int paramIndex; public int lvtIndex; public Object value; + List operations = new ArrayList<>(); + Boolean result; @Override public int hashCode() @@ -88,6 +90,8 @@ class MethodGroup public class ConstantParameter implements Deobfuscator { private List parameters = new ArrayList<>(); + private MultiValueMap mparams = new MultiValueMap<>(); + private MultiValueMap nonconst = new MultiValueMap<>(); // methods with non const parameters private List methodGroups = new ArrayList<>(); @@ -105,8 +109,10 @@ public class ConstantParameter implements Deobfuscator } } - private void findConstantParameter(Execution execution, List methods, InstructionContext invokeCtx) + private List findConstantParameter(List methods, InstructionContext invokeCtx) { + List list = new ArrayList<>(); + checkMethodsAreConsistent(methods); Method method = methods.get(0); // all methods must have the same signature etc @@ -149,6 +155,10 @@ public class ConstantParameter implements Deobfuscator continue outer; parameters.add(cmp); + for (Method m : methods) + mparams.put(m, cmp); + + list.add(cmp); } else { @@ -162,28 +172,152 @@ public class ConstantParameter implements Deobfuscator if (c.methods.equals(methods) && c.lvtIndex == lvtOffset) { it.remove(); + list.remove(c); } } } } + + return list; } - private void findParameters(Execution execution) + private void findParameters(InstructionContext ins) { - for (Frame frame : execution.processedFrames) - for (InstructionContext ins : frame.getInstructions()) - { - if (!(ins.getInstruction() instanceof InvokeInstruction)) - continue; - - List methods = ((InvokeInstruction) ins.getInstruction()).getMethods(); - if (methods.isEmpty()) - continue; - - findConstantParameter(execution, methods, ins); - } + if (!(ins.getInstruction() instanceof InvokeInstruction)) + return; + + List methods = ((InvokeInstruction) ins.getInstruction()).getMethods(); + if (methods.isEmpty()) + return; + + findConstantParameter(methods, ins); + //findDeadParameters(ins, c); } + private List findParametersForMethod(Method m) + { + Collection c = mparams.getCollection(m); + if (c == null) return new ArrayList(); + return new ArrayList(c); +// List list = new ArrayList<>(); +// for (ConstantMethodParameter c : parameters) +// if (c.methods.contains(m)) +// list.add(c); +// return list; + } + + private void findDeadParameters(InstructionContext ins) + { + List parameters = this.findParametersForMethod(ins.getFrame().getMethod()); + + for (ConstantMethodParameter parameter : parameters) + { + int lvtIndex = parameter.lvtIndex; + Object value = parameter.value; + + if (ins.getInstruction() instanceof LVTInstruction) + { + LVTInstruction lvt = (LVTInstruction) ins.getInstruction(); + + if (lvt.getVariableIndex() == lvtIndex && lvt.store()) + { + this.parameters.remove(parameter); + continue; // parameter is used + } + } + + if (!(ins.getInstruction() instanceof ComparisonInstruction)) + { + continue; + } + + // assume that this will always be variable index #paramIndex comp with a constant. + ComparisonInstruction comp = (ComparisonInstruction) ins.getInstruction(); + + StackContext one, two = null; + + if (comp instanceof If0) + { + one = ins.getPops().get(0); + } + else if (comp instanceof If) + { + one = ins.getPops().get(0); + two = ins.getPops().get(1); + } + else + { + throw new RuntimeException("Unknown comp ins"); + } + + // find if one is a lvt ins + LVTInstruction lvt = null; + StackContext other = null; + + if (one.getPushed().getInstruction() instanceof LVTInstruction) + { + lvt = (LVTInstruction) one.getPushed().getInstruction(); + other = two; + } + else if (two != null && two.getPushed().getInstruction() instanceof LVTInstruction) + { + lvt = (LVTInstruction) two.getPushed().getInstruction(); + other = one; + } + + assert lvt == null || !lvt.store(); + + if (lvt == null || lvt.getVariableIndex() != lvtIndex) + { + continue; + } + + Object otherValue = null; + + if (two != null) // two is null for if0 + { + if (!(other.getPushed().getInstruction() instanceof PushConstantInstruction)) + { + continue; + } + + PushConstantInstruction pc = (PushConstantInstruction) other.getPushed().getInstruction(); + otherValue = pc.getConstant().getObject(); + } + + // the result of the comparison doesn't matter, only that it always goes the same direction for every invocation + boolean result = doLogicalComparison(value, comp, otherValue); + + if (parameter.result != null && parameter.result != result) + { + this.parameters.remove(parameter); + } + else + { + parameter.operations.add(ins); + parameter.result = result; + } +// +// Boolean b = deadOps.get(ins.getInstruction()); +// if (b != null) +// { +// if (b != result) +// { +// //deadOps.remove(ins.getInstruction()); +// this.parameters.remove(parameter); +// //invalidDeadOps.add(ins.getInstruction()); +// } +// } +// else +// { +// deadOps.put(ins.getInstruction(), result); +// } + } + } + + //private Map deadOps = new HashMap<>(); +// private Set invalidDeadOps = new HashSet<>(); + private boolean doLogicalComparison(Object value, ComparisonInstruction comparison, Object otherValue) { Instruction ins = (Instruction) comparison; @@ -252,93 +386,93 @@ public class ConstantParameter implements Deobfuscator } // find all comparisons of lvtIndex in method and record branch taken - private List isLogicallyDead(Execution execution, Method method, int lvtIndex, Object value) - { - List ops = new ArrayList<>(); - - for (Frame frame : execution.processedFrames) - { - if (frame.getMethod() != method) - continue; - - for (InstructionContext ins : frame.getInstructions()) - { - if (ins.getInstruction() instanceof LVTInstruction) - { - LVTInstruction lvt = (LVTInstruction) ins.getInstruction(); - - if (lvt.getVariableIndex() == lvtIndex && lvt.store()) - { - return null; - } - } - - if (!(ins.getInstruction() instanceof ComparisonInstruction)) - continue; - - // assume that this will always be variable index #paramIndex comp with a constant. - - ComparisonInstruction comp = (ComparisonInstruction) ins.getInstruction(); - - StackContext one, two = null; - - if (comp instanceof If0) - { - one = ins.getPops().get(0); - } - else if (comp instanceof If) - { - one = ins.getPops().get(0); - two = ins.getPops().get(1); - } - else - { - throw new RuntimeException("Unknown comp ins"); - } - - // find if one is a lvt ins - LVTInstruction lvt = null; - StackContext other = null; - - if (one.getPushed().getInstruction() instanceof LVTInstruction) - { - lvt = (LVTInstruction) one.getPushed().getInstruction(); - other = two; - } - else if (two != null && two.getPushed().getInstruction() instanceof LVTInstruction) - { - lvt = (LVTInstruction) two.getPushed().getInstruction(); - other = one; - } - - assert lvt == null || !lvt.store(); - - if (lvt == null || lvt.getVariableIndex() != lvtIndex) - continue; - - Object otherValue = null; - - if (two != null) // two is null for if0 - { - if (!(other.getPushed().getInstruction() instanceof PushConstantInstruction)) - continue; - - PushConstantInstruction pc = (PushConstantInstruction) other.getPushed().getInstruction(); - otherValue = pc.getConstant().getObject(); - } - - // the result of the comparison doesn't matter, only that it always goes the same direction for every invocation - boolean result = doLogicalComparison(value, comp, otherValue); - - LogicallyDeadOp deadOp = new LogicallyDeadOp(); - deadOp.compCtx = ins; - deadOp.branch = result; - ops.add(deadOp); - } - } - - return ops; - } +// private List isLogicallyDead(Execution execution, Method method, int lvtIndex, Object value) +// { +// List ops = new ArrayList<>(); +// +// for (Frame frame : execution.processedFrames) +// { +// if (frame.getMethod() != method) +// continue; +// +// for (InstructionContext ins : frame.getInstructions()) +// { +// if (ins.getInstruction() instanceof LVTInstruction) +// { +// LVTInstruction lvt = (LVTInstruction) ins.getInstruction(); +// +// if (lvt.getVariableIndex() == lvtIndex && lvt.store()) +// { +// return null; +// } +// } +// +// if (!(ins.getInstruction() instanceof ComparisonInstruction)) +// continue; +// +// // assume that this will always be variable index #paramIndex comp with a constant. +// +// ComparisonInstruction comp = (ComparisonInstruction) ins.getInstruction(); +// +// StackContext one, two = null; +// +// if (comp instanceof If0) +// { +// one = ins.getPops().get(0); +// } +// else if (comp instanceof If) +// { +// one = ins.getPops().get(0); +// two = ins.getPops().get(1); +// } +// else +// { +// throw new RuntimeException("Unknown comp ins"); +// } +// +// // find if one is a lvt ins +// LVTInstruction lvt = null; +// StackContext other = null; +// +// if (one.getPushed().getInstruction() instanceof LVTInstruction) +// { +// lvt = (LVTInstruction) one.getPushed().getInstruction(); +// other = two; +// } +// else if (two != null && two.getPushed().getInstruction() instanceof LVTInstruction) +// { +// lvt = (LVTInstruction) two.getPushed().getInstruction(); +// other = one; +// } +// +// assert lvt == null || !lvt.store(); +// +// if (lvt == null || lvt.getVariableIndex() != lvtIndex) +// continue; +// +// Object otherValue = null; +// +// if (two != null) // two is null for if0 +// { +// if (!(other.getPushed().getInstruction() instanceof PushConstantInstruction)) +// continue; +// +// PushConstantInstruction pc = (PushConstantInstruction) other.getPushed().getInstruction(); +// otherValue = pc.getConstant().getObject(); +// } +// +// // the result of the comparison doesn't matter, only that it always goes the same direction for every invocation +// boolean result = doLogicalComparison(value, comp, otherValue); +// +// LogicallyDeadOp deadOp = new LogicallyDeadOp(); +// deadOp.compCtx = ins; +// deadOp.branch = result; +// ops.add(deadOp); +// } +// } +// +// return ops; +// } private static class MethodLvtPair { @@ -368,20 +502,25 @@ public class ConstantParameter implements Deobfuscator @Override public boolean equals(Object obj) { - if (obj == null) { + if (obj == null) + { return false; } - if (getClass() != obj.getClass()) { + if (getClass() != obj.getClass()) + { return false; } final MethodLvtPair other = (MethodLvtPair) obj; - if (!Objects.equals(this.method, other.method)) { + if (!Objects.equals(this.method, other.method)) + { return false; } - if (this.lvtIndex != other.lvtIndex) { + if (this.lvtIndex != other.lvtIndex) + { return false; } - if (this.paramIndex != other.paramIndex) { + if (this.paramIndex != other.paramIndex) + { return false; } return true; @@ -389,71 +528,67 @@ public class ConstantParameter implements Deobfuscator } - - private Map > deadops = new HashMap<>(); - private Set invalidDeadops = new HashSet<>(); +// +// private Map > deadops = new HashMap<>(); +// private Set invalidDeadops = new HashSet<>(); // check every method parameter that we've identified as being passed constants to see if it's logically dead - private void findLogicallyDeadOperations(Execution execution) - { - outer: - for (ConstantMethodParameter cmp : parameters) - { - for (Method method : cmp.methods) - { - MethodLvtPair pair = new MethodLvtPair(method, cmp.lvtIndex, cmp.paramIndex, cmp.value); - - if (invalidDeadops.contains(pair)) - continue; - - // the dead comparisons must be the same and branch the same way for every call to this method. - List deadOps = isLogicallyDead(execution, method, cmp.lvtIndex, cmp.value); - - if (deadOps == null) - { - deadops.remove(pair); - invalidDeadops.add(pair); - continue; // lvt store - } - - if (deadOps.isEmpty()) - continue; // no ops to compare - - // this must be per method,lvtindex - List existing = deadops.get(pair); - if (existing != null) - if (!existing.equals(deadOps)) - { - // one of the branches taken differs because of the value, skip it - deadops.remove(pair); - invalidDeadops.add(pair); - continue; - } - else - { - continue; - } - - deadops.put(pair, deadOps); - } - } - } +// private void findLogicallyDeadOperations(Execution execution) +// { +// for (ConstantMethodParameter cmp : parameters) +// { +// for (Method method : cmp.methods) +// { +// MethodLvtPair pair = new MethodLvtPair(method, cmp.lvtIndex, cmp.paramIndex, cmp.value); +// +// if (invalidDeadops.contains(pair)) +// continue; +// +// // the dead comparisons must be the same and branch the same way for every call to this method. +// List deadOps = isLogicallyDead(execution, method, cmp.lvtIndex, cmp.value); +// +// if (deadOps == null) +// { +// deadops.remove(pair); +// invalidDeadops.add(pair); +// continue; // lvt store +// } +// +// if (deadOps.isEmpty()) +// continue; // no ops to compare +// +// // this must be per method,lvtindex +// List existing = deadops.get(pair); +// if (existing != null) +// if (!existing.equals(deadOps)) +// { +// // one of the branches taken differs because of the value, skip it +// deadops.remove(pair); +// invalidDeadops.add(pair); +// continue; +// } +// else +// { +// continue; +// } +// +// deadops.put(pair, deadOps); +// } +// } +// } // remove logically dead comparisons private int removeDeadOperations() { int count = 0; - for (MethodLvtPair mvp : deadops.keySet()) + for (ConstantMethodParameter cmp : parameters) { - List ops = deadops.get(mvp); + annotateObfuscatedSignature(cmp); - annotateObfuscatedSignature(mvp); - - for (LogicallyDeadOp op : ops) + for (InstructionContext ctx : cmp.operations) // comparisons { - InstructionContext ctx = op.compCtx; // comparison Instruction ins = ctx.getInstruction(); - boolean branch = op.branch; // branch that is always taken + boolean branch = cmp.result; // branch that is always taken if (ins.getInstructions() == null) continue; // ins already removed? @@ -512,23 +647,25 @@ public class ConstantParameter implements Deobfuscator private static final Type OBFUSCATED_SIGNATURE = new Type("Lnet/runelite/mapping/ObfuscatedSignature;"); - private void annotateObfuscatedSignature(MethodLvtPair mvp) + private void annotateObfuscatedSignature(ConstantMethodParameter parameter) { - Method m = mvp.method; - Object value = mvp.value; + for (Method m : parameter.methods) + { + Object value = parameter.value; - Attributes attributes = m.getAttributes(); - Annotations annotations = attributes.getAnnotations(); + Attributes attributes = m.getAttributes(); + Annotations annotations = attributes.getAnnotations(); - if (annotations != null && annotations.find(OBFUSCATED_SIGNATURE) != null) - return; + if (annotations != null && annotations.find(OBFUSCATED_SIGNATURE) != null) + return; - Annotation annotation = attributes.addAnnotation(OBFUSCATED_SIGNATURE, "signature", new net.runelite.asm.pool.UTF8(m.getDescriptor().toString())); + Annotation annotation = attributes.addAnnotation(OBFUSCATED_SIGNATURE, "signature", new net.runelite.asm.pool.UTF8(m.getDescriptor().toString())); - Element element = new Element(annotation); - element.setType(new Type("garbageValue")); - element.setValue(new net.runelite.asm.pool.UTF8(value.toString())); - annotation.addElement(element); + Element element = new Element(annotation); + element.setType(new Type("garbageValue")); + element.setValue(new net.runelite.asm.pool.UTF8(value.toString())); + annotation.addElement(element); + } } @Override @@ -537,11 +674,17 @@ public class ConstantParameter implements Deobfuscator group.buildClassGraph(); // required for getMethods in the invoke stuff by execution... Execution execution = new Execution(group); + execution.addExecutionVisitor((i) -> findParameters(i)); + execution.addExecutionVisitor((i) -> findDeadParameters(i)); execution.populateInitialMethods(); execution.run(); + +// execution = new Execution(group); +// execution.addExecutionVisitor((i) -> findDeadParameters(i)); +// execution.populateInitialMethods(); +// execution.run(); - findParameters(execution); - findLogicallyDeadOperations(execution); + //findLogicallyDeadOperations(execution); int count = removeDeadOperations(); System.out.println("Removed " + count + " logically dead conditional jumps");