Is this right? math tests pass

This commit is contained in:
Adam
2016-04-04 18:21:25 -04:00
parent b1af823585
commit 0b8e56d385
9 changed files with 353 additions and 177 deletions

View File

@@ -1,31 +1,31 @@
package net.runelite.asm.execution;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deob;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.code.Instruction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deob;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.code.Instruction;
import org.apache.commons.collections4.map.MultiValueMap;
public class Execution
{
private ClassGroup group;
public List<Frame> frames = new LinkedList<>();
public List<Frame> frames = new ArrayList<>(), framesOther = new ArrayList<>();
public Set<Method> methods = new HashSet<>(); // all methods
public Set<Instruction> executed = new HashSet<>(); // executed instructions
private MultiValueMap<WeakInstructionContext, Method> invokes = new MultiValueMap<>();
public MultiValueMap<Instruction, InstructionContext> contexts = new MultiValueMap<>(); // XXX this should move to method ctx probably
public boolean paused;
public boolean step = false;
public boolean processInvokes = true;
private List<ExecutionVisitor> visitors = new ArrayList<>();
private List<FrameVisitor> frameVisitors = new ArrayList<>();
private List<MethodContextVisitor> methodContextVisitors = new ArrayList<>();
public Execution(ClassGroup group)
{
@@ -85,9 +85,12 @@ public class Execution
return false;
}
private void addFrame(Frame frame)
public void addFrame(Frame frame)
{
frames.add(frame);
if (frames.isEmpty() || frames.get(0).getMethod() == frame.getMethod())
frames.add(frame);
else
framesOther.add(frame);
}
public Frame invoke(InstructionContext from, Method to)
@@ -95,6 +98,9 @@ public class Execution
if (step) // step executor
return null;
if (!processInvokes)
return null;
if (hasInvoked(from, to))
return null;
@@ -131,15 +137,17 @@ public class Execution
accept(frame);
frames.remove(frame);
if (frames.isEmpty())
{
accept(frame.getMethodCtx());
frames.addAll(framesOther);
framesOther.clear();
}
}
System.out.println("Processed " + fcount + " frames");
}
// public Collection<InstructionContext> getInstructonContexts(Instruction i)
// {
// return contexts.getCollection(i);
// }
public void addExecutionVisitor(ExecutionVisitor ev)
{
@@ -160,4 +168,14 @@ public class Execution
{
frameVisitors.forEach(v -> v.visit(f));
}
public void addMethodContextVisitor(MethodContextVisitor mcv)
{
methodContextVisitors.add(mcv);
}
public void accept(MethodContext m)
{
methodContextVisitors.forEach(mc -> mc.visit(m));
}
}

View File

@@ -149,7 +149,7 @@ public class Frame
public Frame dup()
{
Frame other = new Frame(this);
execution.frames.add(other);
execution.addFrame(other);
return other;
}
@@ -236,7 +236,7 @@ public class Frame
}
assert ictx.getInstruction() == oldCur || oldCur instanceof Wide;
execution.contexts.put(oldCur, ictx);
ctx.contexts.put(oldCur, ictx);
execution.executed.add(oldCur);

View File

@@ -8,6 +8,7 @@ public class MethodContext
{
private Execution execution;
private MultiValueMap<InstructionContext, Instruction> visited = new MultiValueMap<>();
public MultiValueMap<Instruction, InstructionContext> contexts = new MultiValueMap<>(); // XXX this should move to method ctx probably
public MethodContext(Execution execution)
{
@@ -23,4 +24,14 @@ public class MethodContext
visited.put(from, to);
return false;
}
public Collection<InstructionContext> getInstructonContexts(Instruction i)
{
return contexts.getCollection(i);
}
public Collection<InstructionContext> getInstructionContexts()
{
return (Collection) contexts.values();
}
}

View File

@@ -0,0 +1,6 @@
package net.runelite.asm.execution;
public interface MethodContextVisitor
{
void visit(MethodContext context);
}

View File

@@ -4,6 +4,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Method;
import net.runelite.deob.Deobfuscator;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
@@ -26,6 +27,7 @@ import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.Frame;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.MethodContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.asm.execution.VariableContext;
import net.runelite.asm.execution.Variables;
@@ -49,12 +51,10 @@ public class MultiplicationDeobfuscator implements Deobfuscator
System.out.println("Total changed " + count);
}
public static MultiplicationExpression parseExpression(Execution e, InstructionContext ctx, Class want)
public static MultiplicationExpression parseExpression(InstructionContext ctx, Class want)
{
MultiplicationExpression me = new MultiplicationExpression();
// assert !(ctx.getInstruction() instanceof DupInstruction);
if (ctx.getInstruction() instanceof LVTInstruction)
{
LVTInstruction lvt = (LVTInstruction) ctx.getInstruction();
@@ -82,7 +82,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
assert storelvt.store();
InstructionContext pushed = storeCtx.getPops().get(0).getPushed();
return parseExpression(e, pushed, want);
return parseExpression(pushed, want);
}
}
}
@@ -104,7 +104,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
{
if (ctx.getInstruction().getClass() == want)
{
if (!isOnlyPath(e, ctx, sctx))
if (!isOnlyPath(ctx, sctx))
continue;
}
@@ -126,7 +126,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
// chained imul, append to me
try
{
MultiplicationExpression other = parseExpression(e, i, want);
MultiplicationExpression other = parseExpression(i, want);
me.instructions.addAll(other.instructions);
me.subexpressions.addAll(other.subexpressions);
@@ -142,7 +142,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
// imul using result of iadd or isub. evaluate expression
try
{
MultiplicationExpression other = parseExpression(e, i, want);
MultiplicationExpression other = parseExpression(i, want);
// subexpr
me.subexpressions.add(other);
@@ -174,7 +174,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
StackContext orig = dup.getOriginal(sctx); // original
try
{
MultiplicationExpression other = parseExpression(e, orig.getPushed(), want);
MultiplicationExpression other = parseExpression(orig.getPushed(), want);
// this expression is used elsewhere like 'pushConstant' so any changes
// done to it affect that, too. so multiply it by existing values?
if (orig.getPushed().getInstruction() instanceof IAdd || orig.getPushed().getInstruction() instanceof ISub
@@ -209,7 +209,7 @@ public class MultiplicationDeobfuscator implements Deobfuscator
else if (ctx.getInstruction() instanceof IAdd || ctx.getInstruction() instanceof ISub
|| ctx.getInstruction() instanceof LAdd || ctx.getInstruction() instanceof LSub)
{
MultiplicationExpression other = parseExpression(e, i, want); // parse this side of the add/sub
MultiplicationExpression other = parseExpression(i, want); // parse this side of the add/sub
me.subexpressions.add(other);
}
@@ -225,25 +225,6 @@ public class MultiplicationDeobfuscator implements Deobfuscator
return me;
}
public static boolean isOnlyPath(Execution execution, InstructionContext ctx)
{
assert ctx.getInstruction() instanceof IMul || ctx.getInstruction() instanceof LMul;
Collection<InstructionContext> ins = execution.getInstructonContexts(ctx.getInstruction());
for (InstructionContext i : ins)
{
if (!i.equals(ctx))
{
return false;
}
for (StackContext sctx : i.getPushes())
if (sctx.getPopped().size() > 1)
return false;
}
return true;
}
private static boolean ictxEqualsDir(InstructionContext one, InstructionContext two, StackContext sctx)
{
if (one.getInstruction() != two.getInstruction())
@@ -260,77 +241,116 @@ public class MultiplicationDeobfuscator implements Deobfuscator
return true;
}
public static boolean isOnlyPath(Execution execution, InstructionContext ctx, StackContext sctx)
public static boolean isOnlyPath(InstructionContext ctx, StackContext sctx)
{
assert ctx.getInstruction() instanceof IMul || ctx.getInstruction() instanceof LMul;
Collection<InstructionContext> ins = execution.getInstructonContexts(ctx.getInstruction());
// XXX this needs to be all in all frames
Collection<InstructionContext> ins = ctx.getFrame().getMethodCtx().getInstructonContexts(ctx.getInstruction());
for (InstructionContext i : ins)
{
if (!ictxEqualsDir(ctx, i, sctx))
{
if (sctx == null)
{
return false;
}
Instruction poppedIns = null;
for (StackContext s : i.getPushes())
for (InstructionContext i2 : s.getPopped())
if (!i.equals(ctx))
{
if (poppedIns == null)
poppedIns = i2.getInstruction();
else if (poppedIns != i2.getInstruction())
return false;
return false;
}
for (StackContext sctx2 : i.getPushes())
{
if (sctx2.getPopped().size() > 1)
{
return false;
}
}
}
else
{
// everything which pushed the parameters must be the same
if (!ictxEqualsDir(ctx, i, sctx))
{
return false;
}
// everything which pops the result must be the same
Instruction poppedIns = null;
for (StackContext s : i.getPushes())
for (InstructionContext i2 : s.getPopped())
{
if (poppedIns == null)
poppedIns = i2.getInstruction();
else if (poppedIns != i2.getInstruction())
return false;
}
}
}
return true;
}
private Set<Instruction> done = new HashSet<>();
private void visit(MethodContext ctx)
{
for (InstructionContext ictx : ctx.getInstructionContexts())
{
Instruction instruction = ictx.getInstruction();
if (!(instruction instanceof IMul) && !(instruction instanceof LMul))
{
continue;
}
MultiplicationExpression expression;
try
{
expression = parseExpression(ictx, instruction.getClass());
}
catch (IllegalStateException ex)
{
continue;
}
if (expression == null)
{
continue;
}
if (done.contains(instruction))
{
continue;
}
done.add(instruction);
assert instruction instanceof IMul || instruction instanceof LMul;
if (instruction instanceof IMul)
{
count += expression.simplify(1);
}
else if (instruction instanceof LMul)
{
count += expression.simplify(1L);
}
else
{
throw new IllegalStateException();
}
}
}
int count;
private int runOnce()
{
group.buildClassGraph();
count = 0;
Execution e = new Execution(group);
//e.addFrameVisitor(f -> visit(f));
e.addMethodContextVisitor(m -> visit(m));
e.populateInitialMethods();
e.run();
int count = 0;
for (Frame frame : e.processedFrames)
for (InstructionContext ictx : frame.getInstructions())
{
Instruction instruction = ictx.getInstruction();
if (!(instruction instanceof IMul) && !(instruction instanceof LMul))
continue;
MultiplicationExpression expression;
try
{
expression = parseExpression(e, ictx, instruction.getClass());
}
catch (IllegalStateException ex)
{
continue;
}
if (expression == null)
continue;
if (done.contains(instruction))
continue;
done.add(instruction);
assert instruction instanceof IMul || instruction instanceof LMul;
if (instruction instanceof IMul)
count += expression.simplify(1);
else if (instruction instanceof LMul)
count += expression.simplify(1L);
else
throw new IllegalStateException();
}
return count;
}

View File

@@ -1,5 +1,6 @@
package net.runelite.deob.deobfuscators.arithmetic;
import java.util.ArrayList;
import java.util.List;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deobfuscator;
@@ -10,12 +11,26 @@ import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.LMul;
import net.runelite.asm.attributes.code.instructions.NOP;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.Frame;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.StackContext;
class MPair
{
int removeIdx;
InstructionContext ctx;
public MPair(int removeIdx, InstructionContext ctx)
{
this.removeIdx = removeIdx;
this.ctx = ctx;
}
}
public class MultiplyOneDeobfuscator implements Deobfuscator
{
private int count;
private List<MPair> pairs = new ArrayList<>();
private void visit(InstructionContext ictx)
{
@@ -58,22 +73,35 @@ public class MultiplyOneDeobfuscator implements Deobfuscator
return;
}
if (!MultiplicationDeobfuscator.isOnlyPath(e, ictx, removeIdx == 0 ? one : two))
{
return;
}
ictx.removeStack(removeIdx);
ins.replace(ictx.getInstruction(), new NOP(ins));
pairs.add(new MPair(removeIdx, ictx));
++count;
}
private void visit(Frame f)
{
for (MPair p : pairs)
{
StackContext one = p.ctx.getPops().get(0);
StackContext two = p.ctx.getPops().get(1);
if (!MultiplicationDeobfuscator.isOnlyPath(p.ctx, p.removeIdx == 0 ? one : two))
{
continue;
}
p.ctx.removeStack(p.removeIdx);
p.ctx.getInstruction().getInstructions().replace(p.ctx.getInstruction(), new NOP(p.ctx.getInstruction().getInstructions()));
}
pairs.clear();
}
@Override
public void run(ClassGroup group)
{
Execution e = new Execution(group);
e.addExecutionVisitor(i -> visit(i));
e.addFrameVisitor(v -> visit(v));
e.populateInitialMethods();
e.run();

View File

@@ -1,5 +1,6 @@
package net.runelite.deob.deobfuscators.arithmetic;
import java.util.ArrayList;
import java.util.List;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.Deobfuscator;
@@ -17,80 +18,109 @@ import net.runelite.asm.execution.StackContext;
public class MultiplyZeroDeobfuscator implements Deobfuscator
{
private int count;
private List<InstructionContext> pending = new ArrayList<>();
private void visit(InstructionContext ictx)
{
Instruction instruction = ictx.getInstruction();
Instructions ins = instruction.getInstructions();
if (ins == null)
{
return;
}
if (!(instruction instanceof IMul) && !(instruction instanceof LMul))
{
return;
}
List<Instruction> ilist = ins.getInstructions();
StackContext one = ictx.getPops().get(0);
StackContext two = ictx.getPops().get(1);
Instruction ione = one.getPushed().getInstruction(),
itwo = two.getPushed().getInstruction();
boolean remove = false;
if (ione instanceof PushConstantInstruction)
{
PushConstantInstruction pci = (PushConstantInstruction) ione;
Number value = (Number) pci.getConstant().getObject();
if (DMath.equals(value, 0))
{
remove = true;
}
}
if (itwo instanceof PushConstantInstruction)
{
PushConstantInstruction pci = (PushConstantInstruction) itwo;
Number value = (Number) pci.getConstant().getObject();
if (DMath.equals(value, 0))
{
remove = true;
}
}
if (remove == false)
{
return;
}
if (!ilist.contains(instruction))
{
return; // already done
}
pending.add(ictx);
++count;
}
private void visit(Frame frame)
{
for (InstructionContext ictx : pending)
{
Instruction instruction = ictx.getInstruction();
Instructions ins = instruction.getInstructions();
if (!MultiplicationDeobfuscator.isOnlyPath(ictx, null))
{
continue;
}
// remove both, remove imul, push 0
ictx.removeStack(1);
ictx.removeStack(0);
if (instruction instanceof IMul)
{
ins.replace(instruction, new LDC_W(ins, new net.runelite.asm.pool.Integer(0)));
}
else if (instruction instanceof LMul)
{
ins.replace(instruction, new LDC2_W(ins, 0L));
}
else
{
throw new IllegalStateException();
}
}
pending.clear();
}
@Override
public void run(ClassGroup group)
{
group.buildClassGraph();
Execution e = new Execution(group);
e.addExecutionVisitor(i -> visit(i));
e.addFrameVisitor(v -> visit(v));
e.populateInitialMethods();
e.run();
int count = 0;
for (Frame frame : e.processedFrames)
for (InstructionContext ictx : frame.getInstructions())
{
Instruction instruction = ictx.getInstruction();
Instructions ins = instruction.getInstructions();
if (ins == null)
continue;
if (!(instruction instanceof IMul) && !(instruction instanceof LMul))
continue;
List<Instruction> ilist = ins.getInstructions();
StackContext one = ictx.getPops().get(0);
StackContext two = ictx.getPops().get(1);
Instruction ione = one.getPushed().getInstruction(),
itwo = two.getPushed().getInstruction();
boolean remove = false;
if (ione instanceof PushConstantInstruction)
{
PushConstantInstruction pci = (PushConstantInstruction) ione;
Number value = (Number) pci.getConstant().getObject();
if (DMath.equals(value, 0))
remove = true;
}
if (itwo instanceof PushConstantInstruction)
{
PushConstantInstruction pci = (PushConstantInstruction) itwo;
Number value = (Number) pci.getConstant().getObject();
if (DMath.equals(value, 0))
remove = true;
}
if (remove == false)
{
continue;
}
if (!ilist.contains(instruction))
continue; // already done
if (!MultiplicationDeobfuscator.isOnlyPath(e, ictx))
continue;
// remove both, remove imul, push 0
ictx.removeStack(1);
ictx.removeStack(0);
if (instruction instanceof IMul)
ins.replace(instruction, new LDC_W(ins, new net.runelite.asm.pool.Integer(0)));
else if (instruction instanceof LMul)
ins.replace(instruction, new LDC2_W(ins, 0L));
else
throw new IllegalStateException();
++count;
}
System.out.println("Removed " + count + " 0 multiplications");
}
}

View File

@@ -231,14 +231,14 @@ public class MultiplicationDeobfuscatorTest
assert constant4.getConstantAsInt() * constant5.getConstantAsInt() == 1;
{
Collection<InstructionContext> ctxs = e.getInstructonContexts(body[3]);
assert ctxs.size() == 1;
InstructionContext ictx = ctxs.iterator().next();
boolean onlyPath = MultiplicationDeobfuscator.isOnlyPath(e, ictx);
Assert.assertFalse(onlyPath);
}
// {
// Collection<InstructionContext> ctxs = e.getInstructonContexts(body[3]);
// assert ctxs.size() == 1;
//
// InstructionContext ictx = ctxs.iterator().next();
// boolean onlyPath = MultiplicationDeobfuscator.isOnlyPath(e, ictx);
// Assert.assertFalse(onlyPath);
// }
Deobfuscator d = new MultiplicationDeobfuscator();
d.run(group);

View File

@@ -0,0 +1,63 @@
package net.runelite.deob.deobfuscators.arithmetic;
import java.io.File;
import java.io.IOException;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.ClassGroupFactory;
import net.runelite.deob.Deobfuscator;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.Goto;
import net.runelite.asm.attributes.code.instructions.IConst_1;
import net.runelite.asm.attributes.code.instructions.IConst_2;
import net.runelite.asm.attributes.code.instructions.IConst_3;
import net.runelite.asm.attributes.code.instructions.IConst_M1;
import net.runelite.asm.attributes.code.instructions.IDiv;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.IStore_0;
import net.runelite.asm.attributes.code.instructions.IStore_1;
import net.runelite.asm.attributes.code.instructions.IfEq;
import net.runelite.asm.attributes.code.instructions.IfICmpEq;
import net.runelite.asm.attributes.code.instructions.LDC_W;
import net.runelite.asm.attributes.code.instructions.NOP;
import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.attributes.code.instructions.VReturn;
import net.runelite.asm.execution.Execution;
import net.runelite.deob.util.JarUtil;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class MultiplyZeroDeobfuscatorTest
{
private static final File GAMEPACK = new File("c:/rs/gamepack_v19.jar");
@Rule
public TemporaryFolder folder = new TemporaryFolder();
private ClassGroup group;
@Before
public void before() throws IOException
{
group = JarUtil.loadJar(GAMEPACK);
}
@After
public void after() throws IOException
{
JarUtil.saveJar(group, folder.newFile());
}
@Test
public void testRun()
{
MultiplyZeroDeobfuscator m = new MultiplyZeroDeobfuscator();
m.run(group);
}
}