From f809d2a79361e23c8181ec39530462fd771e5110 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 4 Mar 2016 15:37:12 -0500 Subject: [PATCH] Begin inject --- .gitignore | 7 +- .project | 23 -- .settings/org.eclipse.jdt.core.prefs | 12 - .settings/org.eclipse.m2e.core.prefs | 4 - pom.xml | 2 - .../java/net/runelite/deob/ClassFile.java | 5 + .../java/net/runelite/deob/Interfaces.java | 6 + .../runelite/deob/attributes/Annotations.java | 9 + .../attributes/annotation/Annotation.java | 5 + .../deob/attributes/annotation/Element.java | 5 + .../code/instructions/GetField.java | 7 + .../attributes/code/instructions/Return.java | 8 +- .../net/runelite/deob/injection/Inject.java | 240 ++++++++++++++++++ .../runelite/deob/signature/Signature.java | 14 + 14 files changed, 301 insertions(+), 46 deletions(-) delete mode 100644 .project delete mode 100644 .settings/org.eclipse.jdt.core.prefs delete mode 100644 .settings/org.eclipse.m2e.core.prefs create mode 100644 src/main/java/net/runelite/deob/injection/Inject.java diff --git a/.gitignore b/.gitignore index d5be5e446a..c99c2fa7a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ -/target/ -nbactions.xml -nb-configuration.xml +/target/ +nbactions.xml +nb-configuration.xml +/nbproject/ \ No newline at end of file diff --git a/.project b/.project deleted file mode 100644 index efe9011dc3..0000000000 --- a/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - deob - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.jdt.core.javanature - org.eclipse.m2e.core.maven2Nature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index f4217b01dd..0000000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -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 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index 14b697b7bb..0000000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/pom.xml b/pom.xml index 295625ff7f..e9fd898944 100644 --- a/pom.xml +++ b/pom.xml @@ -51,8 +51,6 @@ net.runelite.rs api 1.0-SNAPSHOT - test - jar diff --git a/src/main/java/net/runelite/deob/ClassFile.java b/src/main/java/net/runelite/deob/ClassFile.java index cdfa6fb1ca..29ae344a3d 100644 --- a/src/main/java/net/runelite/deob/ClassFile.java +++ b/src/main/java/net/runelite/deob/ClassFile.java @@ -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; diff --git a/src/main/java/net/runelite/deob/Interfaces.java b/src/main/java/net/runelite/deob/Interfaces.java index 42503916dd..0d63d27d25 100644 --- a/src/main/java/net/runelite/deob/Interfaces.java +++ b/src/main/java/net/runelite/deob/Interfaces.java @@ -30,6 +30,12 @@ public class Interfaces classFile = c; } + public void addInterface(Class clazz) + { + if (!interfaces.contains(clazz)) + interfaces.add(clazz); + } + public List getInterfaces() { return interfaces; diff --git a/src/main/java/net/runelite/deob/attributes/Annotations.java b/src/main/java/net/runelite/deob/attributes/Annotations.java index c95e70fa63..55cc079146 100644 --- a/src/main/java/net/runelite/deob/attributes/Annotations.java +++ b/src/main/java/net/runelite/deob/attributes/Annotations.java @@ -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 { diff --git a/src/main/java/net/runelite/deob/attributes/annotation/Annotation.java b/src/main/java/net/runelite/deob/attributes/annotation/Annotation.java index dcd6b51859..4e3e95536e 100644 --- a/src/main/java/net/runelite/deob/attributes/annotation/Annotation.java +++ b/src/main/java/net/runelite/deob/attributes/annotation/Annotation.java @@ -40,6 +40,11 @@ public class Annotation return elements; } + public Element getElement() + { + return elements.get(0); + } + public void addElement(Element element) { elements.add(element); diff --git a/src/main/java/net/runelite/deob/attributes/annotation/Element.java b/src/main/java/net/runelite/deob/attributes/annotation/Element.java index c644994534..150bcb7515 100644 --- a/src/main/java/net/runelite/deob/attributes/annotation/Element.java +++ b/src/main/java/net/runelite/deob/attributes/annotation/Element.java @@ -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(); diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java b/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java index 6ff7a50d28..c88844e5b5 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/GetField.java @@ -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() { diff --git a/src/main/java/net/runelite/deob/attributes/code/instructions/Return.java b/src/main/java/net/runelite/deob/attributes/code/instructions/Return.java index b2620d001b..1c929146e3 100644 --- a/src/main/java/net/runelite/deob/attributes/code/instructions/Return.java +++ b/src/main/java/net/runelite/deob/attributes/code/instructions/Return.java @@ -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) diff --git a/src/main/java/net/runelite/deob/injection/Inject.java b/src/main/java/net/runelite/deob/injection/Inject.java new file mode 100644 index 0000000000..c21d76420c --- /dev/null +++ b/src/main/java/net/runelite/deob/injection/Inject.java @@ -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 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); + } +} diff --git a/src/main/java/net/runelite/deob/signature/Signature.java b/src/main/java/net/runelite/deob/signature/Signature.java index 8b9ce7fec5..60ed0abeec 100644 --- a/src/main/java/net/runelite/deob/signature/Signature.java +++ b/src/main/java/net/runelite/deob/signature/Signature.java @@ -14,6 +14,10 @@ public class Signature private List 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 getArguments() + { + return arguments; + } }