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");