requires some work to move static field initializers

This commit is contained in:
Adam
2015-08-16 21:15:51 -04:00
parent 5059f8cc75
commit 5839452b3b
14 changed files with 250 additions and 110 deletions

View File

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

View File

@@ -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]);

View File

@@ -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;

View File

@@ -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

View File

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

View File

@@ -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()
{

View File

@@ -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()
{

View File

@@ -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);
}
}

View File

@@ -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];

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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;
}
}