Begin inject

This commit is contained in:
Adam
2016-03-04 15:37:12 -05:00
parent a16a8a5a38
commit f809d2a793
14 changed files with 301 additions and 46 deletions

7
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/target/
nbactions.xml
nb-configuration.xml
/target/
nbactions.xml
nb-configuration.xml
/nbproject/

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>deob</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@@ -1,12 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.7
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.7

View File

@@ -1,4 +0,0 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -51,8 +51,6 @@
<groupId>net.runelite.rs</groupId>
<artifactId>api</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>test</scope>
<type>jar</type>
</dependency>
</dependencies>
<build>

View File

@@ -185,6 +185,11 @@ public class ClassFile
return fields.findField(name);
}
public Field findField(NameAndType nat)
{
return fields.findField(nat);
}
public Class getPoolClass()
{
return name;

View File

@@ -30,6 +30,12 @@ public class Interfaces
classFile = c;
}
public void addInterface(Class clazz)
{
if (!interfaces.contains(clazz))
interfaces.add(clazz);
}
public List<Class> getInterfaces()
{
return interfaces;

View File

@@ -6,6 +6,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.runelite.deob.attributes.annotation.Annotation;
import net.runelite.deob.signature.Type;
public class Annotations extends Attribute
{
@@ -26,6 +27,14 @@ public class Annotations extends Attribute
annotations.add(annotation);
}
public Annotation find(Type type)
{
for (Annotation a : annotations)
if (a.getType().equals(type))
return a;
return null;
}
@Override
public void loadAttribute(DataInputStream is) throws IOException
{

View File

@@ -40,6 +40,11 @@ public class Annotation
return elements;
}
public Element getElement()
{
return elements.get(0);
}
public void addElement(Element element)
{
elements.add(element);

View File

@@ -43,6 +43,11 @@ public class Element
this.value = value;
}
public String getString()
{
return (String) value.getObject();
}
public void load(DataInputStream is) throws IOException
{
ConstantPool pool = annotation.getAnnotations().getAttributes().getClassFile().getPool();

View File

@@ -30,6 +30,13 @@ public class GetField extends Instruction implements GetFieldInstruction
super(instructions, type, pc);
}
public GetField(Instructions instructions, Field field)
{
super(instructions, InstructionType.GETFIELD, -1);
this.field = field;
}
@Override
public String toString()
{

View File

@@ -9,14 +9,18 @@ import net.runelite.deob.execution.InstructionContext;
import net.runelite.deob.execution.Stack;
import net.runelite.deob.execution.StackContext;
import java.io.IOException;
public class Return extends Instruction implements ReturnInstruction
{
public Return(Instructions instructions, InstructionType type, int pc) throws IOException
public Return(Instructions instructions, InstructionType type, int pc)
{
super(instructions, type, pc);
}
public Return(Instructions instructions)
{
super(instructions, InstructionType.RETURN, -1);
}
@Override
public void execute(Frame frame)

View File

@@ -0,0 +1,240 @@
package net.runelite.deob.injection;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.runelite.deob.ClassFile;
import net.runelite.deob.ClassGroup;
import net.runelite.deob.Field;
import net.runelite.deob.Interfaces;
import net.runelite.deob.Method;
import net.runelite.deob.attributes.Annotations;
import net.runelite.deob.attributes.Attributes;
import net.runelite.deob.attributes.Code;
import net.runelite.deob.attributes.annotation.Annotation;
import net.runelite.deob.attributes.code.Instruction;
import net.runelite.deob.attributes.code.Instructions;
import net.runelite.deob.attributes.code.instructions.ALoad;
import net.runelite.deob.attributes.code.instructions.GetField;
import net.runelite.deob.attributes.code.instructions.GetStatic;
import net.runelite.deob.attributes.code.instructions.IMul;
import net.runelite.deob.attributes.code.instructions.LDC2_W;
import net.runelite.deob.attributes.code.instructions.LDC_W;
import net.runelite.deob.attributes.code.instructions.LMul;
import net.runelite.deob.attributes.code.instructions.Return;
import net.runelite.deob.signature.Type;
import net.runelite.deob.pool.Class;
import net.runelite.deob.pool.NameAndType;
import net.runelite.deob.signature.Signature;
import net.runelite.mapping.Import;
public class Inject
{
private static final Type OBFUSCATED_NAME = new Type("Lnet/runelite/mapping/ObfuscatedName;");
private static final Type EXPORT = new Type("Lnet/runelite/mapping/Export;");
private static final Type IMPLEMENTS = new Type("Lnet/runelite/mapping/Implements;");
private static final Type OBFUSCATED_GETTER = new Type("Lnet/runelite/mapping/ObfuscatedGetter;");
private static java.lang.Class<?> clientClass;
static
{
try
{
clientClass = java.lang.Class.forName("net.runelite.rs.api.Client");
}
catch (ClassNotFoundException ex)
{
ex.printStackTrace();
}
}
// deobfuscated contains exports etc to apply to vanilla
private final ClassGroup deobfuscated, vanilla;
public Inject(ClassGroup deobfuscated, ClassGroup vanilla)
{
this.deobfuscated = deobfuscated;
this.vanilla = vanilla;
}
private Type toObType(Type t)
{
String className = t.getType();
ClassFile cf = deobfuscated.findClass(className);
Annotations an = cf.getAttributes().getAnnotations();
String obfuscatedName = an.find(OBFUSCATED_NAME).getElement().toString();
return new Type(obfuscatedName, t.getArrayDims());
}
private Signature toObSignature(Signature s)
{
Signature sig = new Signature();
sig.setTypeOfReturnValue(toObType(s.getReturnValue()));
for (Type t : s.getArguments())
sig.addArg(toObType(t));
return sig;
}
public void run()
{
for (ClassFile cf : deobfuscated.getClasses())
{
Annotations an = cf.getAttributes().getAnnotations();
String obfuscatedName = an.find(OBFUSCATED_NAME).getElement().toString();
ClassFile other = vanilla.findClass(obfuscatedName);
assert other != null;
java.lang.Class implementingClass = injectInterface(cf, other);
if (implementingClass == null)
continue;
for (Field f : cf.getFields().getFields())
{
an = f.getAttributes().getAnnotations();
if (an.find(EXPORT) == null)
continue; // not an exported field
String exportedName = an.find(EXPORT).getElement().toString();
obfuscatedName = an.find(OBFUSCATED_NAME).getElement().toString();
Annotation getterAnnotation = an.find(OBFUSCATED_GETTER);
Number getter = null;
if (getterAnnotation != null)
getter = (Number) getterAnnotation.getElement().getValue().getObject();
// the ob jar is the same as the vanilla so this field must exist in this class.
Field otherf = other.findField(new NameAndType(obfuscatedName, toObType(f.getType())));
assert otherf != null;
assert f.isStatic() == otherf.isStatic();
//
ClassFile targetClass = f.isStatic() ? vanilla.findClass("client") : other; // target class for getter
java.lang.Class targetApiClass = f.isStatic() ? clientClass : implementingClass; // target api class for getter
java.lang.reflect.Method apiMethod = findImportMethodOnApi(targetApiClass, exportedName);
injectGetter(targetClass, apiMethod, otherf, getter);
}
for (Method m : cf.getMethods().getMethods())
{
an = m.getAttributes().getAnnotations();
if (an.find(EXPORT) == null)
continue; // not an exported method
String exportedName = an.find(EXPORT).getElement().toString();
obfuscatedName = an.find(OBFUSCATED_NAME).getElement().toString();
// XXX static methods don't have to be in the same class, so we should have @ObfuscatedClass or something
Method otherm = other.findMethod(new NameAndType(obfuscatedName, toObSignature(m.getDescriptor())));
assert otherm != null;
}
}
}
private java.lang.Class injectInterface(ClassFile cf, ClassFile other)
{
Annotations an = cf.getAttributes().getAnnotations();
if (an == null)
return null;
Annotation a = an.find(IMPLEMENTS);
if (a == null)
return null;
String ifaceName = a.getElement().toString();
Class clazz = new Class(ifaceName);
Interfaces interfaces = other.getInterfaces();
interfaces.addInterface(clazz);
try
{
return java.lang.Class.forName(ifaceName);
}
catch (ClassNotFoundException ex)
{
ex.addSuppressed(ex);
return null;
}
}
private java.lang.reflect.Method findImportMethodOnApi(java.lang.Class<?> clazz, String name)
{
for (java.lang.reflect.Method method : clazz.getMethods())
{
Import i = method.getAnnotation(Import.class);
if (i == null || !name.equals(i.value()))
continue;
return method;
}
return null;
}
private void injectGetter(ClassFile clazz, java.lang.reflect.Method method, Field field, Number getter)
{
// clazz = class file we're injecting the method into.
// method = api method (java reflect) that we're overriding
// field = field we're getting. might not be in this class if static.
// getter = encryption getter
assert clazz.findMethod(method.getName()) == null;
assert field.isStatic() || field.getFields().getClassFile() == clazz;
Signature sig = new Signature();
sig.setTypeOfReturnValue(field.getType());
Method getterMethod = new Method(clazz.getMethods(), method.getName(), sig);
Attributes methodAttributes = getterMethod.getAttributes();
// create code attribute
Code code = new Code(methodAttributes);
methodAttributes.addAttribute(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = new ArrayList<>();
if (field.isStatic())
{
ins.add(new GetStatic(instructions, field.getPoolField()));
}
else
{
ins.add(new ALoad(instructions, 0));
ins.add(new GetField(instructions, field.getPoolField()));
}
if (getter != null)
{
assert getter instanceof Integer || getter instanceof Long;
if (getter instanceof Integer)
{
ins.add(new LDC_W(instructions, (int) getter));
ins.add(new IMul(instructions));
}
else
{
ins.add(new LDC2_W(instructions, (long) getter));
ins.add(new LMul(instructions));
}
}
ins.add(new Return(instructions));
clazz.getMethods().addMethod(getterMethod);
}
}

View File

@@ -14,6 +14,10 @@ public class Signature
private List<Type> arguments = new ArrayList<>();
private Type rv;
public Signature()
{
}
public Signature(String str)
{
Matcher m = paramRetPattern.matcher(str);
@@ -90,6 +94,11 @@ public class Signature
arguments.set(i, type);
}
public void addArg(Type type)
{
arguments.add(type);
}
public Type getReturnValue()
{
return rv;
@@ -99,4 +108,9 @@ public class Signature
{
rv = type;
}
public List<Type> getArguments()
{
return arguments;
}
}