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 = new Attributes(this);
|
||||||
this.attributes.load(is);
|
this.attributes.load(is);
|
||||||
|
|
||||||
instructions.buildJumpGraph();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.io.IOException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import net.runelite.asm.Method;
|
import net.runelite.asm.Method;
|
||||||
|
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
|
||||||
|
|
||||||
public abstract class Instruction implements Cloneable
|
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
|
private int pc; // offset into method this instructions resides at
|
||||||
protected int length = 1; // length of this instruction
|
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)
|
public Instruction(Instructions instructions, InstructionType type, int pc)
|
||||||
{
|
{
|
||||||
this.instructions = instructions;
|
this.instructions = instructions;
|
||||||
@@ -48,9 +46,6 @@ public abstract class Instruction implements Cloneable
|
|||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
}
|
}
|
||||||
|
|
||||||
i.from = new ArrayList<>();
|
|
||||||
i.jump = new ArrayList<>();
|
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,10 +55,6 @@ public abstract class Instruction implements Cloneable
|
|||||||
|
|
||||||
protected void remove()
|
protected void remove()
|
||||||
{
|
{
|
||||||
for (Instruction i : jump)
|
|
||||||
i.from.remove(this);
|
|
||||||
jump.clear();
|
|
||||||
|
|
||||||
Exceptions exceptions = instructions.getCode().getExceptions();
|
Exceptions exceptions = instructions.getCode().getExceptions();
|
||||||
for (Exception e : exceptions.getExceptions())
|
for (Exception e : exceptions.getExceptions())
|
||||||
{
|
{
|
||||||
@@ -72,7 +63,11 @@ public abstract class Instruction implements Cloneable
|
|||||||
assert this != e.getHandler();
|
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)
|
public void replace(Instruction other)
|
||||||
@@ -83,35 +78,12 @@ public abstract class Instruction implements Cloneable
|
|||||||
assert ins.contains(this);
|
assert ins.contains(this);
|
||||||
assert !ins.contains(other);
|
assert !ins.contains(other);
|
||||||
|
|
||||||
// XXX instructions which hold references to instructions !
|
// is this really the right place for this?
|
||||||
for (Instruction i : ins)
|
for (Instruction i : ins)
|
||||||
{
|
{
|
||||||
i.replace(this, other);
|
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();
|
Exceptions exceptions = instructions.getCode().getExceptions();
|
||||||
for (Exception e : exceptions.getExceptions())
|
for (Exception e : exceptions.getExceptions())
|
||||||
{
|
{
|
||||||
@@ -141,17 +113,6 @@ public abstract class Instruction implements Cloneable
|
|||||||
i.replace(this, next);
|
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())
|
for (Exception e : instructions.getCode().getExceptions().getExceptions())
|
||||||
e.replace(this, next);
|
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();
|
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);
|
public abstract void execute(Frame e);
|
||||||
|
|
||||||
/* does this terminate a block? */
|
/* does this terminate a block? */
|
||||||
|
|||||||
@@ -85,8 +85,6 @@ public class Instructions
|
|||||||
this.regeneratePool();
|
this.regeneratePool();
|
||||||
|
|
||||||
// translate instructions to specific
|
// translate instructions to specific
|
||||||
this.buildJumpGraph();
|
|
||||||
|
|
||||||
for (Instruction i : new ArrayList<>(instructions))
|
for (Instruction i : new ArrayList<>(instructions))
|
||||||
{
|
{
|
||||||
Instruction specific = i.makeSpecific();
|
Instruction specific = i.makeSpecific();
|
||||||
@@ -121,26 +119,6 @@ public class Instructions
|
|||||||
out.write(ba);
|
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()
|
public Code getCode()
|
||||||
{
|
{
|
||||||
return code;
|
return code;
|
||||||
@@ -181,20 +159,8 @@ public class Instructions
|
|||||||
oldi.setInstructions(null);
|
oldi.setInstructions(null);
|
||||||
instructions.add(i, newi);
|
instructions.add(i, newi);
|
||||||
|
|
||||||
for (Instruction ins : oldi.from)
|
for (Instruction ins : instructions)
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
ins.replace(oldi, newi);
|
ins.replace(oldi, newi);
|
||||||
}
|
|
||||||
oldi.from.clear();
|
|
||||||
|
|
||||||
for (net.runelite.asm.attributes.code.Exception e : code.getExceptions().getExceptions())
|
for (net.runelite.asm.attributes.code.Exception e : code.getExceptions().getExceptions())
|
||||||
e.replace(oldi, newi);
|
e.replace(oldi, newi);
|
||||||
|
|||||||
@@ -5,7 +5,5 @@ import java.util.List;
|
|||||||
|
|
||||||
public interface JumpingInstruction
|
public interface JumpingInstruction
|
||||||
{
|
{
|
||||||
public void buildJumpGraph();
|
|
||||||
|
|
||||||
List<Instruction> getJumps();
|
List<Instruction> getJumps();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,12 +65,6 @@ public class Goto extends Instruction implements JumpingInstruction
|
|||||||
out.writeShort(offset);
|
out.writeShort(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildJumpGraph()
|
|
||||||
{
|
|
||||||
this.addJump(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame)
|
public void execute(Frame frame)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,12 +43,6 @@ public class GotoW extends Instruction implements JumpingInstruction
|
|||||||
out.writeInt(to.getPc() - this.getPc());
|
out.writeInt(to.getPc() - this.getPc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildJumpGraph()
|
|
||||||
{
|
|
||||||
this.addJump(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame)
|
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.MappingExecutorUtil;
|
||||||
import net.runelite.deob.deobfuscators.rename.PacketHandler;
|
import net.runelite.deob.deobfuscators.rename.PacketHandler;
|
||||||
import net.runelite.deob.deobfuscators.rename.ParallelExecutorMapping;
|
import net.runelite.deob.deobfuscators.rename.ParallelExecutorMapping;
|
||||||
import net.runelite.asm.execution.Execution;
|
|
||||||
|
|
||||||
public abstract class If extends Instruction implements JumpingInstruction, ComparisonInstruction, MappableInstruction
|
public abstract class If extends Instruction implements JumpingInstruction, ComparisonInstruction, MappableInstruction
|
||||||
{
|
{
|
||||||
@@ -72,12 +71,6 @@ public abstract class If extends Instruction implements JumpingInstruction, Comp
|
|||||||
out.writeShort(to.getPc() - this.getPc());
|
out.writeShort(to.getPc() - this.getPc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildJumpGraph()
|
|
||||||
{
|
|
||||||
this.addJump(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame)
|
public void execute(Frame frame)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,12 +63,6 @@ public abstract class If0 extends Instruction implements JumpingInstruction, Com
|
|||||||
out.writeShort(to.getPc() - this.getPc());
|
out.writeShort(to.getPc() - this.getPc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildJumpGraph()
|
|
||||||
{
|
|
||||||
this.addJump(to);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame)
|
public void execute(Frame frame)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import java.io.DataInputStream;
|
|||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
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
|
@Override
|
||||||
public void execute(Frame frame)
|
public void execute(Frame frame)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -97,14 +97,6 @@ public class TableSwitch extends Instruction implements JumpingInstruction
|
|||||||
out.writeInt(i.getPc() - this.getPc());
|
out.writeInt(i.getPc() - this.getPc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void buildJumpGraph()
|
|
||||||
{
|
|
||||||
for (Instruction i : branchi)
|
|
||||||
this.addJump(i);
|
|
||||||
this.addJump(defi);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(Frame frame)
|
public void execute(Frame frame)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.io.IOException;
|
|||||||
import net.runelite.deob.deobfuscators.ConstantParameter;
|
import net.runelite.deob.deobfuscators.ConstantParameter;
|
||||||
import net.runelite.deob.deobfuscators.FieldInliner;
|
import net.runelite.deob.deobfuscators.FieldInliner;
|
||||||
import net.runelite.deob.deobfuscators.IllegalStateExceptions;
|
import net.runelite.deob.deobfuscators.IllegalStateExceptions;
|
||||||
import net.runelite.deob.deobfuscators.MethodInliner;
|
|
||||||
import net.runelite.deob.deobfuscators.RenameUnique;
|
import net.runelite.deob.deobfuscators.RenameUnique;
|
||||||
import net.runelite.deob.deobfuscators.RuntimeExceptions;
|
import net.runelite.deob.deobfuscators.RuntimeExceptions;
|
||||||
import net.runelite.deob.deobfuscators.UnreachedCode;
|
import net.runelite.deob.deobfuscators.UnreachedCode;
|
||||||
@@ -73,25 +72,25 @@ public class Deob
|
|||||||
|
|
||||||
run(group, new UnusedClass());
|
run(group, new UnusedClass());
|
||||||
|
|
||||||
ModArith mod = new ModArith();
|
// ModArith mod = new ModArith();
|
||||||
mod.run(group);
|
// mod.run(group);
|
||||||
|
//
|
||||||
int last = -1, cur;
|
// int last = -1, cur;
|
||||||
while ((cur = mod.runOnce()) > 0)
|
// while ((cur = mod.runOnce()) > 0)
|
||||||
{
|
// {
|
||||||
new MultiplicationDeobfuscator().run(group);
|
// new MultiplicationDeobfuscator().run(group);
|
||||||
|
//
|
||||||
new MultiplyOneDeobfuscator().run(group);
|
// new MultiplyOneDeobfuscator().run(group);
|
||||||
|
//
|
||||||
new MultiplyZeroDeobfuscator().run(group);
|
// new MultiplyZeroDeobfuscator().run(group);
|
||||||
|
//
|
||||||
if (last == cur)
|
// if (last == cur)
|
||||||
break;
|
// break;
|
||||||
|
//
|
||||||
last = cur;
|
// last = cur;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
mod.annotateEncryption();
|
// mod.annotateEncryption();
|
||||||
|
|
||||||
JarUtil.saveJar(group, new File(args[1]));
|
JarUtil.saveJar(group, new File(args[1]));
|
||||||
|
|
||||||
|
|||||||
@@ -459,7 +459,6 @@ public class ConstantParameter implements Deobfuscator
|
|||||||
continue; // ins already removed?
|
continue; // ins already removed?
|
||||||
|
|
||||||
Instructions instructions = ins.getInstructions();
|
Instructions instructions = ins.getInstructions();
|
||||||
instructions.buildJumpGraph();
|
|
||||||
|
|
||||||
// remove the if
|
// remove the if
|
||||||
if (ctx.getInstruction() instanceof If)
|
if (ctx.getInstruction() instanceof If)
|
||||||
@@ -489,14 +488,8 @@ public class ConstantParameter implements Deobfuscator
|
|||||||
assert instructions.getInstructions().contains(to);
|
assert instructions.getInstructions().contains(to);
|
||||||
|
|
||||||
// move things that jump here to instead jump to 'to'
|
// move things that jump here to instead jump to 'to'
|
||||||
for (Instruction fromI : ins.from)
|
for (Instruction i : instructions.getInstructions())
|
||||||
{
|
i.replace(ins, to);
|
||||||
assert fromI.jump.contains(ins);
|
|
||||||
|
|
||||||
fromI.jump.remove(ins);
|
|
||||||
fromI.replace(ins, to);
|
|
||||||
}
|
|
||||||
ins.from.clear();
|
|
||||||
|
|
||||||
instructions.remove(ins);
|
instructions.remove(ins);
|
||||||
|
|
||||||
@@ -505,8 +498,6 @@ public class ConstantParameter implements Deobfuscator
|
|||||||
if (branch)
|
if (branch)
|
||||||
{
|
{
|
||||||
Goto gotoins = new Goto(instructions, to);
|
Goto gotoins = new Goto(instructions, to);
|
||||||
to.from.add(gotoins);
|
|
||||||
gotoins.jump.add(to);
|
|
||||||
|
|
||||||
// insert goto
|
// insert goto
|
||||||
instructions.getInstructions().add(idx, gotoins);
|
instructions.getInstructions().add(idx, gotoins);
|
||||||
|
|||||||
@@ -102,21 +102,11 @@ public class FieldInliner implements Deobfuscator
|
|||||||
// nop
|
// nop
|
||||||
NOP nop1 = new NOP(instructions), nop2 = new NOP(instructions);
|
NOP nop1 = new NOP(instructions), nop2 = new NOP(instructions);
|
||||||
|
|
||||||
for (Instruction i : prev.from)
|
for (Instruction i : instructions.getInstructions())
|
||||||
{
|
|
||||||
i.jump.remove(prev);
|
|
||||||
i.jump.add(nop1);
|
|
||||||
i.replace(prev, nop1);
|
i.replace(prev, nop1);
|
||||||
}
|
|
||||||
prev.from.clear();
|
|
||||||
|
|
||||||
for (Instruction i : ins.from)
|
for (Instruction i : instructions.getInstructions())
|
||||||
{
|
|
||||||
i.jump.remove(ins);
|
|
||||||
i.jump.add(nop1);
|
|
||||||
i.replace(ins, nop1);
|
i.replace(ins, nop1);
|
||||||
}
|
|
||||||
ins.from.clear();
|
|
||||||
|
|
||||||
boolean b = instructions.getInstructions().remove(prev);
|
boolean b = instructions.getInstructions().remove(prev);
|
||||||
assert b;
|
assert b;
|
||||||
@@ -144,8 +134,6 @@ public class FieldInliner implements Deobfuscator
|
|||||||
// remove fin, add push constant
|
// remove fin, add push constant
|
||||||
Instruction i = (Instruction) fin;
|
Instruction i = (Instruction) fin;
|
||||||
|
|
||||||
i.getInstructions().buildJumpGraph();
|
|
||||||
|
|
||||||
Instruction pushIns = new LDC_W(i.getInstructions(), value.getValue());
|
Instruction pushIns = new LDC_W(i.getInstructions(), value.getValue());
|
||||||
|
|
||||||
List<Instruction> instructions = i.getInstructions().getInstructions();
|
List<Instruction> instructions = i.getInstructions().getInstructions();
|
||||||
@@ -154,13 +142,8 @@ public class FieldInliner implements Deobfuscator
|
|||||||
assert idx != -1;
|
assert idx != -1;
|
||||||
|
|
||||||
// move jumps to i to pushIns
|
// move jumps to i to pushIns
|
||||||
for (Instruction i2 : i.from)
|
for (Instruction i2 : instructions)
|
||||||
{
|
|
||||||
i2.jump.remove(i);
|
|
||||||
i2.jump.add(pushIns);
|
|
||||||
i2.replace(i, pushIns);
|
i2.replace(i, pushIns);
|
||||||
}
|
|
||||||
i.from.clear();
|
|
||||||
|
|
||||||
i.getInstructions().remove(i);
|
i.getInstructions().remove(i);
|
||||||
instructions.add(idx, pushIns);
|
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;
|
continue;
|
||||||
|
|
||||||
Instructions instructions = c.getInstructions();
|
Instructions instructions = c.getInstructions();
|
||||||
instructions.buildJumpGraph();
|
|
||||||
|
|
||||||
List<Instruction> ilist = instructions.getInstructions();
|
List<Instruction> ilist = instructions.getInstructions();
|
||||||
for (int i = 0; i < ilist.size(); ++i)
|
for (int i = 0; i < ilist.size(); ++i)
|
||||||
@@ -79,17 +78,9 @@ public class IllegalStateExceptions implements Deobfuscator
|
|||||||
{
|
{
|
||||||
// modify instructions which jump to here to instead jump to 'to'
|
// modify instructions which jump to here to instead jump to 'to'
|
||||||
|
|
||||||
for (Instruction from : ins.from)
|
for (Instruction from : ilist)
|
||||||
{
|
|
||||||
from.jump.remove(ins);
|
|
||||||
//ins.from.remove(from);
|
|
||||||
|
|
||||||
from.replace(ins, to);
|
from.replace(ins, to);
|
||||||
|
|
||||||
from.jump.add(to);
|
|
||||||
}
|
|
||||||
ins.from.clear();
|
|
||||||
|
|
||||||
instructions.remove(ins);
|
instructions.remove(ins);
|
||||||
ins = ilist.get(i); // don't need to ++i because
|
ins = ilist.get(i); // don't need to ++i because
|
||||||
}
|
}
|
||||||
@@ -100,8 +91,6 @@ public class IllegalStateExceptions implements Deobfuscator
|
|||||||
// insert goto
|
// insert goto
|
||||||
assert ilist.contains(to);
|
assert ilist.contains(to);
|
||||||
Goto g = new Goto(instructions, to);
|
Goto g = new Goto(instructions, to);
|
||||||
g.jump.add(to);
|
|
||||||
to.from.add(g);
|
|
||||||
ilist.add(i, g);
|
ilist.add(i, g);
|
||||||
|
|
||||||
++count;
|
++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))
|
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...
|
// 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()))
|
for (net.runelite.asm.attributes.code.Exception e : new ArrayList<>(m.getCode().getExceptions().getExceptions()))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user