Remove jump graph, isn't used except for some integrity checks, but it makes stuff overly complex.
This commit is contained in:
@@ -41,8 +41,6 @@ public class Code extends Attribute
|
||||
|
||||
this.attributes = new Attributes(this);
|
||||
this.attributes.load(is);
|
||||
|
||||
instructions.buildJumpGraph();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
|
||||
|
||||
public abstract class Instruction implements Cloneable
|
||||
{
|
||||
@@ -18,9 +19,6 @@ public abstract class Instruction implements Cloneable
|
||||
private int pc; // offset into method this instructions resides at
|
||||
protected int length = 1; // length of this instruction
|
||||
|
||||
public List<Instruction> jump = new ArrayList<>(), // instructions which this instruction jumps to
|
||||
from = new ArrayList<>(); // instructions which jump to this instruction
|
||||
|
||||
public Instruction(Instructions instructions, InstructionType type, int pc)
|
||||
{
|
||||
this.instructions = instructions;
|
||||
@@ -48,9 +46,6 @@ public abstract class Instruction implements Cloneable
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
i.from = new ArrayList<>();
|
||||
i.jump = new ArrayList<>();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -60,10 +55,6 @@ public abstract class Instruction implements Cloneable
|
||||
|
||||
protected void remove()
|
||||
{
|
||||
for (Instruction i : jump)
|
||||
i.from.remove(this);
|
||||
jump.clear();
|
||||
|
||||
Exceptions exceptions = instructions.getCode().getExceptions();
|
||||
for (Exception e : exceptions.getExceptions())
|
||||
{
|
||||
@@ -71,8 +62,12 @@ public abstract class Instruction implements Cloneable
|
||||
assert this != e.getEnd();
|
||||
assert this != e.getHandler();
|
||||
}
|
||||
|
||||
assert from.isEmpty(); // because this is empty no jumping instructions point here
|
||||
|
||||
// XXX unreached code deob relies on being able to remove instructions that other ins jump to,
|
||||
// if those other ins are also unreached.
|
||||
//for (Instruction i : instructions.getInstructions())
|
||||
// if (i instanceof JumpingInstruction)
|
||||
// assert ((JumpingInstruction) i).getJumps().contains(this) == false;
|
||||
}
|
||||
|
||||
public void replace(Instruction other)
|
||||
@@ -83,35 +78,12 @@ public abstract class Instruction implements Cloneable
|
||||
assert ins.contains(this);
|
||||
assert !ins.contains(other);
|
||||
|
||||
// XXX instructions which hold references to instructions !
|
||||
// is this really the right place for this?
|
||||
for (Instruction i : ins)
|
||||
{
|
||||
i.replace(this, other);
|
||||
}
|
||||
|
||||
// update instructions which jump here to jump to the new instruction
|
||||
for (Instruction i : from)
|
||||
{
|
||||
assert i.jump.contains(this);
|
||||
assert !i.jump.contains(other);
|
||||
|
||||
i.jump.remove(this);
|
||||
i.jump.add(other);
|
||||
}
|
||||
from.clear();
|
||||
|
||||
// move jumps over
|
||||
for (Instruction i : jump)
|
||||
{
|
||||
assert i.from.contains(this);
|
||||
assert !i.from.contains(other);
|
||||
|
||||
i.from.remove(this);
|
||||
i.from.add(other);
|
||||
}
|
||||
other.jump = new ArrayList<>(this.jump);
|
||||
jump.clear();
|
||||
|
||||
Exceptions exceptions = instructions.getCode().getExceptions();
|
||||
for (Exception e : exceptions.getExceptions())
|
||||
{
|
||||
@@ -141,17 +113,6 @@ public abstract class Instruction implements Cloneable
|
||||
i.replace(this, next);
|
||||
}
|
||||
|
||||
for (Instruction i : from)
|
||||
{
|
||||
assert i.jump.contains(this);
|
||||
|
||||
i.jump.remove(this);
|
||||
|
||||
i.jump.add(next);
|
||||
next.from.add(i);
|
||||
}
|
||||
from.clear();
|
||||
|
||||
for (Exception e : instructions.getCode().getExceptions().getExceptions())
|
||||
e.replace(this, next);
|
||||
|
||||
@@ -220,19 +181,6 @@ public abstract class Instruction implements Cloneable
|
||||
return type.getName() + " at pc " + frame.getPc() + " in " + frame.getMethod().getName() + " " + frame.getMethod().getDescriptor() + " class " + frame.getMethod().getCode().getAttributes().getClassFile().getName();
|
||||
}
|
||||
|
||||
protected void addJump(Instruction to)
|
||||
{
|
||||
assert to != null;
|
||||
assert to != this;
|
||||
|
||||
assert this.jump.contains(to) == to.from.contains(this);
|
||||
if (this.jump.contains(to))
|
||||
return; // switch statements can jump to the same place multiple times
|
||||
|
||||
this.jump.add(to);
|
||||
to.from.add(this);
|
||||
}
|
||||
|
||||
public abstract void execute(Frame e);
|
||||
|
||||
/* does this terminate a block? */
|
||||
|
||||
@@ -85,8 +85,6 @@ public class Instructions
|
||||
this.regeneratePool();
|
||||
|
||||
// translate instructions to specific
|
||||
this.buildJumpGraph();
|
||||
|
||||
for (Instruction i : new ArrayList<>(instructions))
|
||||
{
|
||||
Instruction specific = i.makeSpecific();
|
||||
@@ -121,26 +119,6 @@ public class Instructions
|
||||
out.write(ba);
|
||||
}
|
||||
|
||||
public void clearJumpGraph()
|
||||
{
|
||||
for (Instruction i : instructions)
|
||||
{
|
||||
i.jump.clear();
|
||||
i.from.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
clearJumpGraph();
|
||||
|
||||
assert new HashSet<>(instructions).size() == instructions.size();
|
||||
|
||||
for (Instruction i : instructions)
|
||||
if (i instanceof JumpingInstruction)
|
||||
((JumpingInstruction) i).buildJumpGraph();
|
||||
}
|
||||
|
||||
public Code getCode()
|
||||
{
|
||||
return code;
|
||||
@@ -180,21 +158,9 @@ public class Instructions
|
||||
instructions.remove(oldi);
|
||||
oldi.setInstructions(null);
|
||||
instructions.add(i, newi);
|
||||
|
||||
for (Instruction ins : oldi.from)
|
||||
{
|
||||
assert ins.getInstructions() == this;
|
||||
assert this.getInstructions().contains(ins);
|
||||
assert ins.jump.contains(oldi);
|
||||
|
||||
ins.jump.remove(oldi);
|
||||
|
||||
ins.jump.add(newi);
|
||||
newi.from.add(ins);
|
||||
|
||||
|
||||
for (Instruction ins : instructions)
|
||||
ins.replace(oldi, newi);
|
||||
}
|
||||
oldi.from.clear();
|
||||
|
||||
for (net.runelite.asm.attributes.code.Exception e : code.getExceptions().getExceptions())
|
||||
e.replace(oldi, newi);
|
||||
|
||||
@@ -5,7 +5,5 @@ import java.util.List;
|
||||
|
||||
public interface JumpingInstruction
|
||||
{
|
||||
public void buildJumpGraph();
|
||||
|
||||
List<Instruction> getJumps();
|
||||
}
|
||||
|
||||
@@ -64,12 +64,6 @@ public class Goto extends Instruction implements JumpingInstruction
|
||||
|
||||
out.writeShort(offset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
this.addJump(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
|
||||
@@ -42,12 +42,6 @@ public class GotoW extends Instruction implements JumpingInstruction
|
||||
super.write(out);
|
||||
out.writeInt(to.getPc() - this.getPc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
this.addJump(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
|
||||
@@ -23,7 +23,6 @@ import net.runelite.asm.attributes.code.instruction.types.PushConstantInstructio
|
||||
import net.runelite.deob.deobfuscators.rename.MappingExecutorUtil;
|
||||
import net.runelite.deob.deobfuscators.rename.PacketHandler;
|
||||
import net.runelite.deob.deobfuscators.rename.ParallelExecutorMapping;
|
||||
import net.runelite.asm.execution.Execution;
|
||||
|
||||
public abstract class If extends Instruction implements JumpingInstruction, ComparisonInstruction, MappableInstruction
|
||||
{
|
||||
@@ -71,12 +70,6 @@ public abstract class If extends Instruction implements JumpingInstruction, Comp
|
||||
super.write(out);
|
||||
out.writeShort(to.getPc() - this.getPc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
this.addJump(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
|
||||
@@ -63,12 +63,6 @@ public abstract class If0 extends Instruction implements JumpingInstruction, Com
|
||||
out.writeShort(to.getPc() - this.getPc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
this.addJump(to);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -101,14 +100,6 @@ public class LookupSwitch extends Instruction implements JumpingInstruction
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
for (Instruction i : branchi)
|
||||
this.addJump(i);
|
||||
this.addJump(defi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
{
|
||||
|
||||
@@ -97,14 +97,6 @@ public class TableSwitch extends Instruction implements JumpingInstruction
|
||||
out.writeInt(i.getPc() - this.getPc());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildJumpGraph()
|
||||
{
|
||||
for (Instruction i : branchi)
|
||||
this.addJump(i);
|
||||
this.addJump(defi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Frame frame)
|
||||
{
|
||||
|
||||
@@ -6,7 +6,6 @@ import java.io.IOException;
|
||||
import net.runelite.deob.deobfuscators.ConstantParameter;
|
||||
import net.runelite.deob.deobfuscators.FieldInliner;
|
||||
import net.runelite.deob.deobfuscators.IllegalStateExceptions;
|
||||
import net.runelite.deob.deobfuscators.MethodInliner;
|
||||
import net.runelite.deob.deobfuscators.RenameUnique;
|
||||
import net.runelite.deob.deobfuscators.RuntimeExceptions;
|
||||
import net.runelite.deob.deobfuscators.UnreachedCode;
|
||||
@@ -73,25 +72,25 @@ public class Deob
|
||||
|
||||
run(group, new UnusedClass());
|
||||
|
||||
ModArith mod = new ModArith();
|
||||
mod.run(group);
|
||||
|
||||
int last = -1, cur;
|
||||
while ((cur = mod.runOnce()) > 0)
|
||||
{
|
||||
new MultiplicationDeobfuscator().run(group);
|
||||
|
||||
new MultiplyOneDeobfuscator().run(group);
|
||||
|
||||
new MultiplyZeroDeobfuscator().run(group);
|
||||
|
||||
if (last == cur)
|
||||
break;
|
||||
|
||||
last = cur;
|
||||
}
|
||||
|
||||
mod.annotateEncryption();
|
||||
// ModArith mod = new ModArith();
|
||||
// mod.run(group);
|
||||
//
|
||||
// int last = -1, cur;
|
||||
// while ((cur = mod.runOnce()) > 0)
|
||||
// {
|
||||
// new MultiplicationDeobfuscator().run(group);
|
||||
//
|
||||
// new MultiplyOneDeobfuscator().run(group);
|
||||
//
|
||||
// new MultiplyZeroDeobfuscator().run(group);
|
||||
//
|
||||
// if (last == cur)
|
||||
// break;
|
||||
//
|
||||
// last = cur;
|
||||
// }
|
||||
//
|
||||
// mod.annotateEncryption();
|
||||
|
||||
JarUtil.saveJar(group, new File(args[1]));
|
||||
|
||||
|
||||
@@ -459,7 +459,6 @@ public class ConstantParameter implements Deobfuscator
|
||||
continue; // ins already removed?
|
||||
|
||||
Instructions instructions = ins.getInstructions();
|
||||
instructions.buildJumpGraph();
|
||||
|
||||
// remove the if
|
||||
if (ctx.getInstruction() instanceof If)
|
||||
@@ -489,14 +488,8 @@ public class ConstantParameter implements Deobfuscator
|
||||
assert instructions.getInstructions().contains(to);
|
||||
|
||||
// move things that jump here to instead jump to 'to'
|
||||
for (Instruction fromI : ins.from)
|
||||
{
|
||||
assert fromI.jump.contains(ins);
|
||||
|
||||
fromI.jump.remove(ins);
|
||||
fromI.replace(ins, to);
|
||||
}
|
||||
ins.from.clear();
|
||||
for (Instruction i : instructions.getInstructions())
|
||||
i.replace(ins, to);
|
||||
|
||||
instructions.remove(ins);
|
||||
|
||||
@@ -505,8 +498,6 @@ public class ConstantParameter implements Deobfuscator
|
||||
if (branch)
|
||||
{
|
||||
Goto gotoins = new Goto(instructions, to);
|
||||
to.from.add(gotoins);
|
||||
gotoins.jump.add(to);
|
||||
|
||||
// insert goto
|
||||
instructions.getInstructions().add(idx, gotoins);
|
||||
|
||||
@@ -101,22 +101,12 @@ public class FieldInliner implements Deobfuscator
|
||||
|
||||
// nop
|
||||
NOP nop1 = new NOP(instructions), nop2 = new NOP(instructions);
|
||||
|
||||
for (Instruction i : prev.from)
|
||||
{
|
||||
i.jump.remove(prev);
|
||||
i.jump.add(nop1);
|
||||
|
||||
for (Instruction i : instructions.getInstructions())
|
||||
i.replace(prev, nop1);
|
||||
}
|
||||
prev.from.clear();
|
||||
|
||||
for (Instruction i : ins.from)
|
||||
{
|
||||
i.jump.remove(ins);
|
||||
i.jump.add(nop1);
|
||||
|
||||
for (Instruction i : instructions.getInstructions())
|
||||
i.replace(ins, nop1);
|
||||
}
|
||||
ins.from.clear();
|
||||
|
||||
boolean b = instructions.getInstructions().remove(prev);
|
||||
assert b;
|
||||
@@ -144,8 +134,6 @@ public class FieldInliner implements Deobfuscator
|
||||
// remove fin, add push constant
|
||||
Instruction i = (Instruction) fin;
|
||||
|
||||
i.getInstructions().buildJumpGraph();
|
||||
|
||||
Instruction pushIns = new LDC_W(i.getInstructions(), value.getValue());
|
||||
|
||||
List<Instruction> instructions = i.getInstructions().getInstructions();
|
||||
@@ -154,13 +142,8 @@ public class FieldInliner implements Deobfuscator
|
||||
assert idx != -1;
|
||||
|
||||
// move jumps to i to pushIns
|
||||
for (Instruction i2 : i.from)
|
||||
{
|
||||
i2.jump.remove(i);
|
||||
i2.jump.add(pushIns);
|
||||
for (Instruction i2 : instructions)
|
||||
i2.replace(i, pushIns);
|
||||
}
|
||||
i.from.clear();
|
||||
|
||||
i.getInstructions().remove(i);
|
||||
instructions.add(idx, pushIns);
|
||||
|
||||
@@ -1,339 +0,0 @@
|
||||
package net.runelite.deob.deobfuscators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.deob.Deobfuscator;
|
||||
import net.runelite.asm.Field;
|
||||
import net.runelite.asm.Method;
|
||||
import net.runelite.asm.attributes.Code;
|
||||
import net.runelite.asm.attributes.code.Instruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.FieldInstruction;
|
||||
import net.runelite.asm.pool.NameAndType;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import net.runelite.asm.attributes.Attributes;
|
||||
import net.runelite.asm.attributes.code.InstructionType;
|
||||
import net.runelite.asm.attributes.code.Instructions;
|
||||
import net.runelite.asm.attributes.code.instructions.Goto;
|
||||
import net.runelite.asm.attributes.code.instructions.PutStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.VReturn;
|
||||
import net.runelite.asm.execution.Execution;
|
||||
import net.runelite.asm.execution.Frame;
|
||||
import net.runelite.asm.execution.InstructionContext;
|
||||
import net.runelite.asm.execution.StackContext;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import org.apache.commons.collections4.map.MultiValueMap;
|
||||
|
||||
public class FieldMover implements Deobfuscator
|
||||
{
|
||||
private static final String mainClass = "client";
|
||||
|
||||
private Execution execution;
|
||||
private ClassGroup group;
|
||||
private MultiValueMap<Field, ClassFile> fields = new MultiValueMap<>();
|
||||
private Map<Field, PutStatic> clinits = new HashMap<>();
|
||||
|
||||
private void findUses()
|
||||
{
|
||||
fields.clear();
|
||||
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
Code code = m.getCode();
|
||||
|
||||
if (code == null)
|
||||
continue;
|
||||
|
||||
for (Instruction i : code.getInstructions().getInstructions())
|
||||
{
|
||||
if (!(i instanceof FieldInstruction))
|
||||
continue;
|
||||
|
||||
FieldInstruction fi = (FieldInstruction) i;
|
||||
Field field = fi.getMyField();
|
||||
|
||||
if (field == null)
|
||||
continue;
|
||||
|
||||
if (!field.isStatic())
|
||||
continue;
|
||||
|
||||
if (m.getName().equals("<clinit>"))
|
||||
{
|
||||
if (fi instanceof PutStatic)
|
||||
clinits.put(field, (PutStatic) fi);
|
||||
}
|
||||
else if (!m.isStatic()) // I think non static methods are always right?
|
||||
{
|
||||
if (fields.containsKey(field))
|
||||
{
|
||||
Collection<ClassFile> col = fields.getCollection(field);
|
||||
if (!col.contains(cf))
|
||||
fields.put(field, cf);
|
||||
}
|
||||
else
|
||||
fields.put(field, cf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDowncastable(ClassFile from, ClassFile to)
|
||||
{
|
||||
while (from != null && from != to)
|
||||
{
|
||||
from = from.getParent();
|
||||
}
|
||||
|
||||
return from != null;
|
||||
}
|
||||
|
||||
private ClassFile getBase(ClassFile one, ClassFile two)
|
||||
{
|
||||
if (one == two)
|
||||
return one;
|
||||
|
||||
if (isDowncastable(one, two))
|
||||
return two;
|
||||
|
||||
if (isDowncastable(two, one))
|
||||
return one;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private ClassFile findCommonBase(Collection<ClassFile> classes)
|
||||
{
|
||||
List<ClassFile> list = new ArrayList<>(classes);
|
||||
|
||||
if (list.size() == 1)
|
||||
return list.get(0);
|
||||
|
||||
ClassFile cf = getBase(list.get(0), list.get(1));
|
||||
|
||||
for (int i = 2; i < list.size(); ++i)
|
||||
cf = getBase(cf, list.get(i));
|
||||
|
||||
return cf;
|
||||
}
|
||||
|
||||
private int moveFields()
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (Field field : fields.keySet())
|
||||
{
|
||||
Collection<ClassFile> cfs = fields.getCollection(field);
|
||||
|
||||
ClassFile to = findCommonBase(cfs);
|
||||
if (to == null)
|
||||
// no common base, move to entry class
|
||||
to = group.findClass(mainClass);
|
||||
|
||||
assert to != null;
|
||||
|
||||
if (field.getFields().getClassFile() == to)
|
||||
continue;
|
||||
|
||||
moveField(field, to);
|
||||
++count;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private void moveField(Field field, ClassFile to)
|
||||
{
|
||||
assert field.getFields().getClassFile() != to;
|
||||
|
||||
net.runelite.asm.pool.Field newField = new net.runelite.asm.pool.Field(
|
||||
new net.runelite.asm.pool.Class(to.getName()),
|
||||
new NameAndType(field.getName(), field.getType())
|
||||
);
|
||||
|
||||
// move on instructions
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
Code code = m.getCode();
|
||||
|
||||
if (code == null)
|
||||
continue;
|
||||
|
||||
//code.getInstructions().renameField(field, newField);
|
||||
}
|
||||
}
|
||||
|
||||
// move the field
|
||||
field.getFields().getFields().remove(field);
|
||||
to.getFields().getFields().add(field);
|
||||
field.setFields(to.getFields());
|
||||
|
||||
// move initializer
|
||||
PutStatic setField = clinits.get(field);
|
||||
if (setField == null)
|
||||
return; // no initializer
|
||||
|
||||
Method toClinit = to.findMethod("<clinit>");
|
||||
if (toClinit == null)
|
||||
{
|
||||
// make clinit
|
||||
|
||||
Signature sig = new Signature("()V");
|
||||
toClinit = new Method(to.getMethods(), "<clinit>", sig);
|
||||
|
||||
Attributes attributes = toClinit.getAttributes();
|
||||
Code code = new Code(attributes);
|
||||
|
||||
attributes.addAttribute(code);
|
||||
|
||||
// make instructions
|
||||
Instructions instructions = new Instructions(code);
|
||||
code.setInstructions(instructions);
|
||||
|
||||
instructions.getInstructions().add(new VReturn(instructions, InstructionType.RETURN, 0)); // add return
|
||||
|
||||
to.getMethods().getMethods().add(toClinit);
|
||||
}
|
||||
|
||||
moveInitializer(setField, toClinit);
|
||||
}
|
||||
|
||||
private void moveInitializer(PutStatic setInstruction, Method to)
|
||||
{
|
||||
// find instruction in execution and remove it
|
||||
InstructionContext setCtx = null;
|
||||
Frame frame = null;
|
||||
List<StackContext> ctxs = null;
|
||||
for (Frame f : execution.processedFrames)
|
||||
for (InstructionContext ctx : f.getInstructions())
|
||||
{
|
||||
if (ctx.getInstruction() != setInstruction)
|
||||
continue;
|
||||
|
||||
setCtx = ctx;
|
||||
|
||||
// get instructions before recursive stack removal
|
||||
//List<Instruction> oldIns = new ArrayList<>(frame.getMethod().getCode().getInstructions().getInstructions());
|
||||
|
||||
ctxs = ctx.removeStack(0); //remove
|
||||
frame = f;
|
||||
|
||||
//List<Instruction> newIns = new ArrayList<>(frame.getMethod().getCode().getInstructions().getInstructions());
|
||||
|
||||
//changedIns = CollectionUtils.disjunction(oldIns, newIns);
|
||||
break;
|
||||
}
|
||||
|
||||
if (setCtx == null)
|
||||
{
|
||||
System.err.println("Unable to locate context for putstatic when moving field initializer");
|
||||
return;
|
||||
}
|
||||
|
||||
// insert instructions into method
|
||||
|
||||
// convert stack info to instruction ctx
|
||||
List<InstructionContext> ictxs = getContexts(setCtx);
|
||||
|
||||
// order instructions based on the order they execute in the frame
|
||||
Map<Integer, Instruction> orderedIns = new TreeMap<>();
|
||||
for (InstructionContext i : ictxs)
|
||||
{
|
||||
assert frame.getInstructions().indexOf(i) != -1;
|
||||
orderedIns.put(frame.getInstructions().indexOf(i), i.getInstruction());
|
||||
}
|
||||
|
||||
to.getCode().getInstructions().buildJumpGraph();
|
||||
frame.getMethod().getCode().getInstructions().buildJumpGraph();
|
||||
|
||||
for (Instruction i : orderedIns.values())
|
||||
{
|
||||
moveJumps(i);
|
||||
i.getInstructions().remove(i);
|
||||
|
||||
i.setInstructions(to.getCode().getInstructions());
|
||||
}
|
||||
|
||||
// insert instructions into method
|
||||
to.getCode().getInstructions().getInstructions().addAll(0, orderedIns.values());
|
||||
}
|
||||
|
||||
private void moveJumps(Instruction i)
|
||||
{
|
||||
List<Instruction> list = i.getInstructions().getInstructions();
|
||||
|
||||
int idx = list.indexOf(i);
|
||||
|
||||
Instruction next = list.get(idx + 1);
|
||||
|
||||
for (Instruction i2 : i.from)
|
||||
{
|
||||
i2.jump.remove(i);
|
||||
|
||||
i2.replace(i, next);
|
||||
|
||||
next.from.add(i2);
|
||||
i2.jump.add(next);
|
||||
}
|
||||
i.from.clear();
|
||||
}
|
||||
|
||||
private void getContexts(List<InstructionContext> list, InstructionContext ctx)
|
||||
{
|
||||
assert !(ctx.getInstruction() instanceof Goto);
|
||||
|
||||
if (list.contains(ctx))
|
||||
return;
|
||||
|
||||
list.add(ctx);
|
||||
|
||||
for (StackContext s : ctx.getPops())
|
||||
{
|
||||
assert s.getPopped() == ctx;
|
||||
|
||||
getContexts(list, s.getPushed());
|
||||
}
|
||||
|
||||
for (StackContext s : ctx.getPushes())
|
||||
{
|
||||
assert s.getPushed() == ctx;
|
||||
|
||||
for (InstructionContext i : s.getPopped())
|
||||
getContexts(list, i);
|
||||
}
|
||||
}
|
||||
|
||||
// get instruction contexts for stack contexts
|
||||
private List<InstructionContext> getContexts(InstructionContext ctx)
|
||||
{
|
||||
List<InstructionContext> list = new ArrayList<>();
|
||||
getContexts(list, ctx);
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
group.buildClassGraph();
|
||||
|
||||
execution = new Execution(group);
|
||||
execution.populateInitialMethods();
|
||||
execution.run();
|
||||
|
||||
this.group = group;
|
||||
findUses();
|
||||
int count = moveFields();
|
||||
|
||||
System.out.println("Moved " + count + " fields");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,7 +35,6 @@ public class IllegalStateExceptions implements Deobfuscator
|
||||
continue;
|
||||
|
||||
Instructions instructions = c.getInstructions();
|
||||
instructions.buildJumpGraph();
|
||||
|
||||
List<Instruction> ilist = instructions.getInstructions();
|
||||
for (int i = 0; i < ilist.size(); ++i)
|
||||
@@ -78,17 +77,9 @@ public class IllegalStateExceptions implements Deobfuscator
|
||||
while (!(ins instanceof AThrow))
|
||||
{
|
||||
// modify instructions which jump to here to instead jump to 'to'
|
||||
|
||||
for (Instruction from : ins.from)
|
||||
{
|
||||
from.jump.remove(ins);
|
||||
//ins.from.remove(from);
|
||||
|
||||
|
||||
for (Instruction from : ilist)
|
||||
from.replace(ins, to);
|
||||
|
||||
from.jump.add(to);
|
||||
}
|
||||
ins.from.clear();
|
||||
|
||||
instructions.remove(ins);
|
||||
ins = ilist.get(i); // don't need to ++i because
|
||||
@@ -100,8 +91,6 @@ public class IllegalStateExceptions implements Deobfuscator
|
||||
// insert goto
|
||||
assert ilist.contains(to);
|
||||
Goto g = new Goto(instructions, to);
|
||||
g.jump.add(to);
|
||||
to.from.add(g);
|
||||
ilist.add(i, g);
|
||||
|
||||
++count;
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
package net.runelite.deob.deobfuscators;
|
||||
|
||||
import net.runelite.asm.ClassFile;
|
||||
import net.runelite.asm.ClassGroup;
|
||||
import net.runelite.deob.Deobfuscator;
|
||||
import net.runelite.asm.Method;
|
||||
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.instruction.types.InvokeInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
|
||||
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.AStore;
|
||||
import net.runelite.asm.attributes.code.instructions.DStore;
|
||||
import net.runelite.asm.attributes.code.instructions.FStore;
|
||||
import net.runelite.asm.attributes.code.instructions.Goto;
|
||||
import net.runelite.asm.attributes.code.instructions.IStore;
|
||||
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
|
||||
import net.runelite.asm.attributes.code.instructions.LStore;
|
||||
import net.runelite.asm.attributes.code.instructions.NOP;
|
||||
import net.runelite.asm.signature.Signature;
|
||||
import net.runelite.asm.signature.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import net.runelite.asm.attributes.code.Exceptions;
|
||||
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
|
||||
import net.runelite.asm.attributes.code.instructions.If;
|
||||
|
||||
public class MethodInliner implements Deobfuscator
|
||||
{
|
||||
private Map<Method, Integer> calls = new HashMap<>();
|
||||
//private Set<Method> removeMethods = new HashSet<>();
|
||||
|
||||
private void countCalls(Method m)
|
||||
{
|
||||
Code code = m.getCode();
|
||||
if (code == null)
|
||||
return;
|
||||
|
||||
Instructions ins = code.getInstructions();
|
||||
|
||||
for (Instruction i : ins.getInstructions())
|
||||
{
|
||||
// can only inline static method calls
|
||||
if (!(i instanceof InvokeStatic))
|
||||
continue;
|
||||
|
||||
List<Method> invokedMethods = ((InvokeInstruction) i).getMethods();
|
||||
if (invokedMethods.isEmpty())
|
||||
continue; // not our method
|
||||
|
||||
assert invokedMethods.size() == 1;
|
||||
Method invokedMethod = invokedMethods.get(0);
|
||||
|
||||
Integer count = calls.get(invokedMethod);
|
||||
if (count == null)
|
||||
calls.put(invokedMethod, 1);
|
||||
else
|
||||
calls.put(invokedMethod, count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private int processMethod(Method m)
|
||||
{
|
||||
int inlineCount = 0;
|
||||
Code code = m.getCode();
|
||||
if (code == null)
|
||||
return inlineCount;
|
||||
|
||||
Instructions ins = code.getInstructions();
|
||||
|
||||
for (Instruction i : ins.getInstructions())
|
||||
{
|
||||
assert i.getInstructions() == ins;
|
||||
// can only inline static method calls
|
||||
if (!(i instanceof InvokeStatic))
|
||||
continue;
|
||||
|
||||
List<Method> invokedMethods = ((InvokeInstruction) i).getMethods();
|
||||
if (invokedMethods.isEmpty())
|
||||
continue; // not our method
|
||||
|
||||
Method invokedMethod = invokedMethods.get(0);
|
||||
Integer count = calls.get(invokedMethod);
|
||||
|
||||
// this can't inline functions with exception handlers because throwing exceptions clears the stack
|
||||
if (count == null
|
||||
|| !invokedMethod.getCode().getExceptions().getExceptions().isEmpty()
|
||||
|| invokedMethod.getCode().getInstructions().getInstructions().size() > 30) // XXX magic
|
||||
continue;
|
||||
// if (count == null || count != 1)
|
||||
// continue; // only inline methods called once
|
||||
|
||||
if (m == invokedMethod)
|
||||
continue; // recursive method
|
||||
|
||||
int invokeIdx = ins.getInstructions().indexOf(i);
|
||||
assert invokeIdx != -1;
|
||||
assert ins.getInstructions().get(invokeIdx).getInstructions() == ins;
|
||||
|
||||
int lvtIndex = code.getMaxLocals(),
|
||||
theirLocals = invokedMethod.getCode().getMaxLocals();
|
||||
|
||||
if (lvtIndex + theirLocals > 127)
|
||||
continue;
|
||||
|
||||
if (invokedMethod.isSynchronized())
|
||||
continue;
|
||||
|
||||
// assign variables on stack to lvt
|
||||
Signature descriptor = invokedMethod.getDescriptor();
|
||||
|
||||
Map<Integer, Integer> lvtIndexes = new HashMap<>();
|
||||
for (int j = 0, idx = 0; j < descriptor.size(); ++j)
|
||||
{
|
||||
lvtIndexes.put(j, idx);
|
||||
idx += descriptor.getTypeOfArg(j).getSlots();
|
||||
}
|
||||
|
||||
Instruction firstParamStore = null;
|
||||
|
||||
for (int j = descriptor.size() - 1; j >= 0; --j)
|
||||
{
|
||||
Type type = descriptor.getTypeOfArg(j);
|
||||
int paramLvtIndex = lvtIndexes.get(j);
|
||||
|
||||
// insert instruction to store top of stack in lvt
|
||||
|
||||
Instruction storeIns = null;
|
||||
if (type.getArrayDims() == 0)
|
||||
{
|
||||
switch (type.getType())
|
||||
{
|
||||
case "B":
|
||||
case "Z":
|
||||
case "C":
|
||||
case "S":
|
||||
case "I":
|
||||
storeIns = new IStore(ins, lvtIndex + paramLvtIndex);
|
||||
break;
|
||||
case "J":
|
||||
storeIns = new LStore(ins, lvtIndex + paramLvtIndex);
|
||||
break;
|
||||
case "F":
|
||||
storeIns = new FStore(ins, lvtIndex + paramLvtIndex);
|
||||
break;
|
||||
case "D":
|
||||
storeIns = new DStore(ins, lvtIndex + paramLvtIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type.getArrayDims() != 0 || type.getType().startsWith("L"))
|
||||
{
|
||||
assert storeIns == null;
|
||||
storeIns = new AStore(ins, lvtIndex + paramLvtIndex);
|
||||
}
|
||||
assert storeIns != null;
|
||||
|
||||
// insert storeIns before invoke instruction
|
||||
ins.getInstructions().add(invokeIdx++, storeIns);
|
||||
|
||||
if (firstParamStore == null)
|
||||
firstParamStore = storeIns;
|
||||
}
|
||||
|
||||
int maxStack = code.getMaxStack() + invokedMethod.getCode().getMaxStack(); // not really right but ok
|
||||
code.setMaxStack(maxStack);
|
||||
|
||||
inline(m, i, invokedMethod, lvtIndex, firstParamStore);
|
||||
++inlineCount;
|
||||
break;
|
||||
}
|
||||
|
||||
return inlineCount;
|
||||
}
|
||||
|
||||
private void inline(Method method, Instruction invokeIns, Method invokeMethod, int lvtBase, Instruction firstParamStore)
|
||||
{
|
||||
Code methodCode = method.getCode(),
|
||||
invokeMethodCode = invokeMethod.getCode();
|
||||
Instructions methodInstructions = methodCode.getInstructions(),
|
||||
invokeMethodInstructions = invokeMethodCode.getInstructions();
|
||||
|
||||
int idx = methodInstructions.getInstructions().indexOf(invokeIns); // index of invoke ins, before removal
|
||||
assert invokeIns.getInstructions() == methodInstructions;
|
||||
assert idx != -1;
|
||||
|
||||
Instruction nextInstruction = methodInstructions.getInstructions().get(idx + 1);
|
||||
|
||||
// move stuff which jumps to invokeIns to firstParamStore. If there are no arguments that are stored,
|
||||
// firstParamStore is null, and so create a nop instruction.
|
||||
|
||||
if (firstParamStore == null)
|
||||
{
|
||||
Instruction nop = new NOP(methodInstructions);
|
||||
methodInstructions.getInstructions().add(idx + 1, nop);
|
||||
++idx;
|
||||
|
||||
firstParamStore = nop;
|
||||
}
|
||||
|
||||
methodInstructions.buildJumpGraph();
|
||||
invokeMethodInstructions.buildJumpGraph();
|
||||
|
||||
for (Instruction fromI : invokeIns.from)
|
||||
{
|
||||
assert fromI.jump.contains(invokeIns);
|
||||
|
||||
fromI.jump.remove(invokeIns);
|
||||
fromI.replace(invokeIns, firstParamStore);
|
||||
|
||||
fromI.jump.add(firstParamStore);
|
||||
firstParamStore.from.add(fromI);
|
||||
}
|
||||
invokeIns.from.clear();
|
||||
|
||||
for (net.runelite.asm.attributes.code.Exception e : invokeMethodCode.getExceptions().getExceptions())
|
||||
e.replace(invokeIns, firstParamStore);
|
||||
|
||||
methodInstructions.remove(invokeIns);
|
||||
|
||||
Map<Instruction, Instruction> insMap = new HashMap<>();
|
||||
for (Instruction i : invokeMethodInstructions.getInstructions())
|
||||
{
|
||||
Instruction i2 = i.clone();
|
||||
i2.setInstructions(null);
|
||||
insMap.put(i, i2);
|
||||
}
|
||||
|
||||
for (Instruction i : insMap.values())
|
||||
{
|
||||
for (Entry<Instruction, Instruction> e : insMap.entrySet())
|
||||
{
|
||||
i.replace(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (Instruction i : insMap.values())
|
||||
{
|
||||
if (i instanceof JumpingInstruction)
|
||||
{
|
||||
JumpingInstruction j = (JumpingInstruction) i;
|
||||
for (Instruction i2 : j.getJumps())
|
||||
{
|
||||
assert insMap.values().contains(i2);
|
||||
assert i2.getInstructions() == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Exceptions fromExceptions = invokeMethod.getCode().getExceptions();
|
||||
Exceptions toExceptions = method.getCode().getExceptions();
|
||||
|
||||
for (net.runelite.asm.attributes.code.Exception e : fromExceptions.getExceptions())
|
||||
{
|
||||
e = e.clone();
|
||||
e.setExceptions(toExceptions);
|
||||
|
||||
for (Entry<Instruction, Instruction> en : insMap.entrySet())
|
||||
{
|
||||
e.replace(en.getKey(), en.getValue());
|
||||
}
|
||||
|
||||
toExceptions.add(e);
|
||||
}
|
||||
|
||||
for (Instruction i : invokeMethodInstructions.getInstructions())
|
||||
{
|
||||
Instruction orig = i;
|
||||
i = insMap.get(i);
|
||||
// copy instructions over.
|
||||
|
||||
if (i instanceof ReturnInstruction)
|
||||
{
|
||||
// XXX I am assuming that this function leaves the stack in a clean state?
|
||||
|
||||
// instead of return, jump to next instruction after the invoke
|
||||
Instruction oldI = i;
|
||||
assert oldI.getInstructions() == null;
|
||||
|
||||
i = new Goto(methodInstructions, nextInstruction);
|
||||
|
||||
assert nextInstruction.getInstructions() == methodInstructions;
|
||||
assert methodInstructions.getInstructions().contains(nextInstruction);
|
||||
|
||||
for (Instruction i2 : insMap.values())
|
||||
i2.replace(oldI, i);
|
||||
|
||||
for (net.runelite.asm.attributes.code.Exception e : toExceptions.getExceptions())
|
||||
e.replace(oldI, i);
|
||||
|
||||
insMap.put(orig, i);
|
||||
}
|
||||
|
||||
if (i instanceof LVTInstruction)
|
||||
{
|
||||
LVTInstruction lvt = (LVTInstruction) i;
|
||||
// offset lvt index
|
||||
int newIndex = lvtBase + lvt.getVariableIndex();
|
||||
|
||||
Instruction oldI = i;
|
||||
i = lvt.setVariableIndex(newIndex);
|
||||
|
||||
assert oldI == i;
|
||||
// if (oldI != i)
|
||||
// {
|
||||
// for (Instruction i2 : insMap.values())
|
||||
// i2.replace(oldI, i);
|
||||
//
|
||||
// for (net.runelite.deob.attributes.code.Exception e : toExceptions.getExceptions())
|
||||
// e.replace(oldI, i);
|
||||
//
|
||||
// insMap.put(orig, i);
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
i.setInstructions(methodInstructions);
|
||||
assert !methodInstructions.getInstructions().contains(i);
|
||||
methodInstructions.getInstructions().add(idx++, i);
|
||||
}
|
||||
|
||||
this.checkJumpGraph(methodInstructions);
|
||||
}
|
||||
|
||||
private void checkJumpGraph(Instructions ins)
|
||||
{
|
||||
ins.buildJumpGraph();
|
||||
|
||||
assert new HashSet<>(ins.getInstructions()).size() == ins.getInstructions().size();
|
||||
|
||||
for (Instruction i : ins.getInstructions())
|
||||
{
|
||||
for (Instruction i2 : i.jump)
|
||||
{
|
||||
assert i2.getInstructions() == ins;
|
||||
assert ins.getInstructions().contains(i2);
|
||||
|
||||
assert i2.from.contains(i);
|
||||
}
|
||||
|
||||
for (Instruction i2 : i.from)
|
||||
{
|
||||
assert i2.getInstructions() == ins;
|
||||
assert ins.getInstructions().contains(i2);
|
||||
|
||||
assert i2.jump.contains(i);
|
||||
}
|
||||
|
||||
if (i instanceof JumpingInstruction)
|
||||
{
|
||||
JumpingInstruction j = (JumpingInstruction) i;
|
||||
assert j.getJumps().size() == i.jump.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
int total = 0;
|
||||
int i;
|
||||
do
|
||||
{
|
||||
i = pass(group);
|
||||
total += i;
|
||||
}
|
||||
while (i > 0);
|
||||
|
||||
System.out.println("[TOTAL] Inlined " + total + " methods");
|
||||
}
|
||||
|
||||
private int pass(ClassGroup group)
|
||||
{
|
||||
group.buildClassGraph();
|
||||
int count = 0;
|
||||
|
||||
calls.clear();
|
||||
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
countCalls(m);
|
||||
}
|
||||
}
|
||||
|
||||
for (ClassFile cf : group.getClasses())
|
||||
{
|
||||
for (Method m : cf.getMethods().getMethods())
|
||||
{
|
||||
count += processMethod(m);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("Inlined " + count + " methods");
|
||||
return count;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -27,10 +27,6 @@ public class UnreachedCode implements Deobfuscator
|
||||
|
||||
if (!execution.executed.contains(i))
|
||||
{
|
||||
for (Instruction i2 : i.from)
|
||||
i2.jump.remove(i);
|
||||
i.from.clear(); // if this is never executed, anything that jumps here ia also never executed?
|
||||
|
||||
// if this is an exception handler, the exception handler is never used...
|
||||
for (net.runelite.asm.attributes.code.Exception e : new ArrayList<>(m.getCode().getExceptions().getExceptions()))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user