requires some work to move static field initializers
This commit is contained in:
@@ -193,6 +193,11 @@ public class ClassFile
|
||||
return methods.findMethod(nat);
|
||||
}
|
||||
|
||||
public Method findMethod(String name)
|
||||
{
|
||||
return methods.findMethod(name);
|
||||
}
|
||||
|
||||
public Method findMethodDeep(String name)
|
||||
{
|
||||
Method m = methods.findMethod(name);
|
||||
|
||||
@@ -46,72 +46,72 @@ public class Deob
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("rename unique took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove except RuntimeException
|
||||
bstart = System.currentTimeMillis();
|
||||
new RuntimeExceptions().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("runtime exception took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove unused methods
|
||||
bstart = System.currentTimeMillis();
|
||||
new UnusedMethods().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("unused methods took " + bdur/1000L + " seconds");
|
||||
|
||||
new UnreachedCode().run(group);
|
||||
|
||||
// remove illegal state exceptions, frees up some parameters
|
||||
bstart = System.currentTimeMillis();
|
||||
new IllegalStateExceptions().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("illegal state exception took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove constant logically dead parameters
|
||||
bstart = System.currentTimeMillis();
|
||||
new ConstantParameter().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("constant param took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove unhit blocks
|
||||
bstart = System.currentTimeMillis();
|
||||
new UnreachedCode().run(group);
|
||||
//new UnusedBlocks().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("unused blocks took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove unused parameters
|
||||
bstart = System.currentTimeMillis();
|
||||
new UnusedParameters().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("unused params took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove jump obfuscation
|
||||
//new Jumps().run(group);
|
||||
|
||||
// remove unused fields
|
||||
bstart = System.currentTimeMillis();
|
||||
new UnusedFields().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("unused fields took " + bdur/1000L + " seconds");
|
||||
|
||||
// remove unused methods, again?
|
||||
bstart = System.currentTimeMillis();
|
||||
new UnusedMethods().run(group);
|
||||
bdur = System.currentTimeMillis() - bstart;
|
||||
System.out.println("unused methods took " + bdur/1000L + " seconds");
|
||||
|
||||
|
||||
new MethodInliner().run(group);
|
||||
// // remove except RuntimeException
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new RuntimeExceptions().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("runtime exception took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove unused methods
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new UnusedMethods().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("unused methods took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// new UnreachedCode().run(group);
|
||||
//
|
||||
// // remove illegal state exceptions, frees up some parameters
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new IllegalStateExceptions().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("illegal state exception took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove constant logically dead parameters
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new ConstantParameter().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("constant param took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove unhit blocks
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new UnreachedCode().run(group);
|
||||
// //new UnusedBlocks().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("unused blocks took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove unused parameters
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new UnusedParameters().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("unused params took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove jump obfuscation
|
||||
// //new Jumps().run(group);
|
||||
//
|
||||
// // remove unused fields
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new UnusedFields().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("unused fields took " + bdur/1000L + " seconds");
|
||||
//
|
||||
// // remove unused methods, again?
|
||||
// bstart = System.currentTimeMillis();
|
||||
// new UnusedMethods().run(group);
|
||||
// bdur = System.currentTimeMillis() - bstart;
|
||||
// System.out.println("unused methods took " + bdur/1000L + " seconds");
|
||||
//
|
||||
//
|
||||
// new MethodInliner().run(group);
|
||||
|
||||
// new ModularArithmeticDeobfuscation().run(group);
|
||||
|
||||
new MethodMover().run(group);
|
||||
//
|
||||
// new MethodMover().run(group);
|
||||
|
||||
new FieldMover().run(group);
|
||||
//
|
||||
// new FieldInliner().run(group);
|
||||
|
||||
new FieldInliner().run(group);
|
||||
|
||||
new UnusedClass().run(group);
|
||||
//new UnusedClass().run(group);
|
||||
|
||||
saveJar(group, args[1]);
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ public class Method
|
||||
name = pool.getUTF8(is.readUnsignedShort());
|
||||
arguments = new Signature(pool.getUTF8(is.readUnsignedShort()));
|
||||
attributes = new Attributes(this);
|
||||
attributes.load();
|
||||
}
|
||||
|
||||
public Method(Methods methods, String name, Signature signature)
|
||||
{
|
||||
this.methods = methods;
|
||||
this.name = name;
|
||||
this.arguments = signature;
|
||||
attributes = new Attributes(this);
|
||||
}
|
||||
|
||||
public void write(DataOutputStream out) throws IOException
|
||||
@@ -62,6 +71,11 @@ public class Method
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
public Attributes getAttributes()
|
||||
{
|
||||
return attributes;
|
||||
}
|
||||
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
|
||||
@@ -11,20 +11,16 @@ public abstract class Attribute
|
||||
private AttributeType type;
|
||||
private int length;
|
||||
|
||||
Attribute(Attributes attr, AttributeType type) throws IOException
|
||||
Attribute(Attributes attr, AttributeType type)
|
||||
{
|
||||
this.attributes = attr;
|
||||
this.type = type;
|
||||
|
||||
DataInputStream is = attr.getStream();
|
||||
this.length = is.readInt();
|
||||
}
|
||||
|
||||
Attribute(Attributes attr, AttributeType type, int length)
|
||||
public void load() throws IOException
|
||||
{
|
||||
this.attributes = attr;
|
||||
this.type = type;
|
||||
this.length = length;
|
||||
DataInputStream is = attributes.getStream();
|
||||
this.length = is.readInt();
|
||||
}
|
||||
|
||||
public final void write(DataOutputStream out) throws IOException
|
||||
|
||||
@@ -33,11 +33,9 @@ public class Attributes
|
||||
load();
|
||||
}
|
||||
|
||||
public Attributes(Method m) throws IOException
|
||||
public Attributes(Method m)
|
||||
{
|
||||
method = m;
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
public Attributes(Code c) throws IOException
|
||||
@@ -82,7 +80,7 @@ public class Attributes
|
||||
return getClassFile().getStream();
|
||||
}
|
||||
|
||||
private void load() throws IOException
|
||||
public void load() throws IOException
|
||||
{
|
||||
DataInputStream is = getStream();
|
||||
|
||||
@@ -97,6 +95,7 @@ public class Attributes
|
||||
{
|
||||
Constructor<? extends Attribute> con = type.getAttributeClass().getConstructor(new Class[] { Attributes.class });
|
||||
Attribute attr = con.newInstance(this);
|
||||
attr.load();
|
||||
|
||||
if (type != AttributeType.UNKNOWN)
|
||||
attributes.add(attr);
|
||||
|
||||
@@ -17,16 +17,23 @@ public class Code extends Attribute
|
||||
private Exceptions exceptions;
|
||||
private Attributes attributes;
|
||||
|
||||
public Code(Attributes attributes) throws IOException
|
||||
public Code(Attributes attributes)
|
||||
{
|
||||
super(attributes, AttributeType.CODE);
|
||||
|
||||
DataInputStream is = attributes.getStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws IOException
|
||||
{
|
||||
super.load();
|
||||
|
||||
DataInputStream is = this.getAttributes().getStream();
|
||||
|
||||
maxStack = is.readUnsignedShort();
|
||||
is.skip(2); // max locals
|
||||
|
||||
instructions = new Instructions(this);
|
||||
instructions.load();
|
||||
|
||||
exceptions = new Exceptions(this);
|
||||
this.attributes = new Attributes(this);
|
||||
@@ -95,6 +102,11 @@ public class Code extends Attribute
|
||||
{
|
||||
return instructions;
|
||||
}
|
||||
|
||||
public void setInstructions(Instructions instructions)
|
||||
{
|
||||
this.instructions = instructions;
|
||||
}
|
||||
|
||||
public void buildInstructionGraph()
|
||||
{
|
||||
|
||||
@@ -10,20 +10,26 @@ public class ConstantValue extends Attribute
|
||||
{
|
||||
private PoolEntry value;
|
||||
|
||||
public ConstantValue(Attributes attributes) throws IOException
|
||||
public ConstantValue(Attributes attributes)
|
||||
{
|
||||
super(attributes, AttributeType.CONSTANT_VALUE);
|
||||
|
||||
DataInputStream is = attributes.getStream();
|
||||
value = this.getAttributes().getClassFile().getPool().getEntry(is.readUnsignedShort());
|
||||
}
|
||||
|
||||
public ConstantValue(Attributes attributes, PoolEntry value)
|
||||
{
|
||||
super(attributes, AttributeType.CONSTANT_VALUE, -1);
|
||||
super(attributes, AttributeType.CONSTANT_VALUE);
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws IOException
|
||||
{
|
||||
super.load();
|
||||
|
||||
DataInputStream is = this.getAttributes().getStream();
|
||||
value = this.getAttributes().getClassFile().getPool().getEntry(is.readUnsignedShort());
|
||||
}
|
||||
|
||||
public PoolEntry getValue()
|
||||
{
|
||||
|
||||
@@ -13,16 +13,22 @@ public class Exceptions extends Attribute
|
||||
{
|
||||
private List<Class> classes = new ArrayList<>();
|
||||
|
||||
public Exceptions(Attributes attributes) throws IOException
|
||||
public Exceptions(Attributes attributes)
|
||||
{
|
||||
super(attributes, AttributeType.EXCEPTIONS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws IOException
|
||||
{
|
||||
super.load();
|
||||
|
||||
DataInputStream is = attributes.getStream();
|
||||
DataInputStream is = this.getAttributes().getStream();
|
||||
|
||||
int count = is.readUnsignedShort();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
Class clazz = attributes.getClassFile().getPool().getClass(is.readUnsignedShort());
|
||||
Class clazz = this.getAttributes().getClassFile().getPool().getClass(is.readUnsignedShort());
|
||||
classes.add(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,12 +8,18 @@ public class Unknown extends Attribute
|
||||
{
|
||||
private byte[] data;
|
||||
|
||||
public Unknown(Attributes attributes) throws IOException
|
||||
public Unknown(Attributes attributes)
|
||||
{
|
||||
super(attributes, AttributeType.UNKNOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() throws IOException
|
||||
{
|
||||
super.load();
|
||||
|
||||
int len = this.getLength();
|
||||
DataInputStream is = attributes.getStream();
|
||||
DataInputStream is = this.getAttributes().getStream();
|
||||
|
||||
data = new byte[len];
|
||||
|
||||
|
||||
@@ -21,9 +21,13 @@ public class Instructions
|
||||
private List<Instruction> instructions = new ArrayList<>();
|
||||
private List<Block> blocks = new ArrayList<>();
|
||||
|
||||
public Instructions(Code code) throws IOException
|
||||
public Instructions(Code code)
|
||||
{
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public void load() throws IOException
|
||||
{
|
||||
DataInputStream is = code.getAttributes().getStream();
|
||||
|
||||
int length = is.readInt();
|
||||
|
||||
@@ -7,11 +7,10 @@ import net.runelite.deob.attributes.code.instruction.types.ReturnInstruction;
|
||||
import net.runelite.deob.execution.Frame;
|
||||
import net.runelite.deob.execution.InstructionContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class VReturn extends Instruction implements ReturnInstruction
|
||||
{
|
||||
public VReturn(Instructions instructions, InstructionType type, int pc) throws IOException
|
||||
public VReturn(Instructions instructions, InstructionType type, int pc)
|
||||
{
|
||||
super(instructions, type, pc);
|
||||
}
|
||||
|
||||
@@ -11,15 +11,29 @@ import net.runelite.deob.attributes.code.Instruction;
|
||||
import net.runelite.deob.attributes.code.instruction.types.FieldInstruction;
|
||||
import net.runelite.deob.pool.NameAndType;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.runelite.deob.attributes.Attributes;
|
||||
import net.runelite.deob.attributes.code.InstructionType;
|
||||
import net.runelite.deob.attributes.code.Instructions;
|
||||
import net.runelite.deob.attributes.code.instructions.PutStatic;
|
||||
import net.runelite.deob.attributes.code.instructions.VReturn;
|
||||
import net.runelite.deob.execution.Execution;
|
||||
import net.runelite.deob.execution.Frame;
|
||||
import net.runelite.deob.execution.InstructionContext;
|
||||
import net.runelite.deob.execution.StackContext;
|
||||
import net.runelite.deob.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()
|
||||
{
|
||||
@@ -48,14 +62,22 @@ public class FieldMover implements Deobfuscator
|
||||
if (!field.isStatic())
|
||||
continue;
|
||||
|
||||
if (fields.containsKey(field))
|
||||
if (m.getName().equals("<clinit>"))
|
||||
{
|
||||
Collection<ClassFile> col = fields.getCollection(field);
|
||||
if (!col.contains(cf))
|
||||
fields.put(field, cf);
|
||||
if (fi instanceof PutStatic)
|
||||
clinits.put(field, (PutStatic) fi);
|
||||
}
|
||||
else
|
||||
fields.put(field, cf);
|
||||
{
|
||||
if (fields.containsKey(field))
|
||||
{
|
||||
Collection<ClassFile> col = fields.getCollection(field);
|
||||
if (!col.contains(cf))
|
||||
fields.put(field, cf);
|
||||
}
|
||||
else
|
||||
fields.put(field, cf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -92,13 +114,12 @@ public class FieldMover implements Deobfuscator
|
||||
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;
|
||||
return null; // to do this, would have to move static initializer
|
||||
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()
|
||||
@@ -111,9 +132,8 @@ public class FieldMover implements Deobfuscator
|
||||
|
||||
ClassFile to = findCommonBase(cfs);
|
||||
if (to == null)
|
||||
continue;
|
||||
// no common base, move to entry class
|
||||
//to = group.findClass(mainClass);
|
||||
to = group.findClass(mainClass);
|
||||
|
||||
assert to != null;
|
||||
|
||||
@@ -154,12 +174,77 @@ public class FieldMover implements Deobfuscator
|
||||
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
|
||||
}
|
||||
|
||||
moveInitializer(setField, toClinit);
|
||||
}
|
||||
|
||||
private void moveInitializer(PutStatic setInstruction, Method to)
|
||||
{
|
||||
// find instruction in execution and remove it
|
||||
InstructionContext setCtx = null;
|
||||
List<StackContext> ctxs = null;
|
||||
for (Frame frame : execution.processedFrames)
|
||||
for (InstructionContext ctx : frame.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
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(ClassGroup group)
|
||||
{
|
||||
group.buildClassGraph();
|
||||
|
||||
execution = new Execution(group);
|
||||
execution.populateInitialMethods();
|
||||
execution.run();
|
||||
|
||||
this.group = group;
|
||||
findUses();
|
||||
int count = moveFields();
|
||||
|
||||
@@ -75,15 +75,14 @@ public class InstructionContext
|
||||
return invokes;
|
||||
}
|
||||
|
||||
public void removeStack(int idx)
|
||||
public List<StackContext> removeStack(int idx)
|
||||
{
|
||||
// idx 0 is top of the stack, 1 is one under
|
||||
// stack contexts are added to 'pops' in the order that they are popped from the stack,
|
||||
// so just remove at index idx
|
||||
StackContext ctx = pops.remove(idx);
|
||||
StackContext ctx = pops.get(idx);
|
||||
|
||||
// start recursively removing
|
||||
ctx.removeStack();
|
||||
return ctx.removeStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package net.runelite.deob.execution;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class StackContext
|
||||
{
|
||||
public InstructionContext pushed; // instruction which pushed this
|
||||
@@ -45,16 +48,22 @@ public class StackContext
|
||||
}
|
||||
|
||||
// remove this object from the stack
|
||||
public void removeStack()
|
||||
public List<StackContext> removeStack()
|
||||
{
|
||||
List<StackContext> list = new ArrayList<>();
|
||||
|
||||
list.add(this);
|
||||
|
||||
// remove the instruction which pushed this
|
||||
if (!pushed.getInstruction().removeStack())
|
||||
// dup will return false as the other objects on the stack below this are necessary
|
||||
// for the other branch.
|
||||
return;
|
||||
return list;
|
||||
|
||||
// remove from the stack things this instruction read
|
||||
for (StackContext ctx : pushed.getPops())
|
||||
ctx.removeStack();
|
||||
list.addAll(ctx.removeStack());
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user