diff --git a/pom.xml b/pom.xml
index 8a79b3a249..52d0d038c0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,11 @@
api
1.0.0-SNAPSHOT
+
+ net.runelite
+ client
+ 1.0.0-SNAPSHOT
+
org.slf4j
diff --git a/src/main/java/net/runelite/asm/ClassFile.java b/src/main/java/net/runelite/asm/ClassFile.java
index 1f24533ee4..ddf2670672 100644
--- a/src/main/java/net/runelite/asm/ClassFile.java
+++ b/src/main/java/net/runelite/asm/ClassFile.java
@@ -10,6 +10,7 @@ import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import net.runelite.asm.signature.Signature;
public class ClassFile
{
@@ -246,6 +247,11 @@ public class ClassFile
{
return methods.findMethod(name);
}
+
+ public Method findMethod(String name, Signature signature)
+ {
+ return methods.findMethod(new NameAndType(name, signature));
+ }
public Method findMethodDeep(String name)
{
@@ -300,4 +306,9 @@ public class ClassFile
{
this.access_flags &= ~ACC_FINAL;
}
+
+ public void clearAbstract()
+ {
+ this.access_flags &= ~ACC_ABSTRACT;
+ }
}
diff --git a/src/main/java/net/runelite/asm/Method.java b/src/main/java/net/runelite/asm/Method.java
index f740fd4dcc..72aab017b3 100644
--- a/src/main/java/net/runelite/asm/Method.java
+++ b/src/main/java/net/runelite/asm/Method.java
@@ -143,6 +143,14 @@ public class Method
return (accessFlags & ACC_FINAL) != 0;
}
+ public void setFinal(boolean f)
+ {
+ if (f)
+ accessFlags |= ACC_FINAL;
+ else
+ accessFlags &= ~ACC_FINAL;
+ }
+
public boolean isPrivate()
{
return (accessFlags & ACC_PRIVATE) != 0;
@@ -152,6 +160,11 @@ public class Method
{
accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PRIVATE);
}
+
+ public void setPublic()
+ {
+ accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PUBLIC);
+ }
public Exceptions getExceptions()
{
diff --git a/src/main/java/net/runelite/asm/attributes/code/instructions/AConstNull.java b/src/main/java/net/runelite/asm/attributes/code/instructions/AConstNull.java
index cc9da792f5..1da3d2f384 100644
--- a/src/main/java/net/runelite/asm/attributes/code/instructions/AConstNull.java
+++ b/src/main/java/net/runelite/asm/attributes/code/instructions/AConstNull.java
@@ -16,6 +16,11 @@ public class AConstNull extends Instruction
super(instructions, type, pc);
}
+ public AConstNull(Instructions instructions)
+ {
+ super(instructions, InstructionType.ACONST_NULL, -1);
+ }
+
@Override
public InstructionContext execute(Frame frame)
{
diff --git a/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeInterface.java b/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeInterface.java
index b0fd4240a9..324a7d1c29 100644
--- a/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeInterface.java
+++ b/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeInterface.java
@@ -68,16 +68,6 @@ public class InvokeInterface extends Instruction implements InvokeInstruction
{
return myMethods != null ? myMethods : Arrays.asList();
}
-
- private void findMethodFromClass(List list, ClassFile clazz)
- {
- net.runelite.asm.Method m = clazz.findMethodDeep(method.getNameAndType());
- if (m != null && !list.contains(m))
- list.add(m);
-
- for (ClassFile cf : clazz.getChildren())
- findMethodFromClass(list, cf);
- }
@Override
public InstructionContext execute(Frame frame)
diff --git a/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeSpecial.java b/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeSpecial.java
index 58de97394a..22853d1a76 100644
--- a/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeSpecial.java
+++ b/src/main/java/net/runelite/asm/attributes/code/instructions/InvokeSpecial.java
@@ -141,11 +141,13 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction
public void setMethod(Method method)
{
this.method = method;
+ lookup();
}
@Override
public void lookup()
{
+ myMethods = null;
ClassGroup group = this.getInstructions().getCode().getAttributes().getClassFile().getGroup();
ClassFile otherClass = group.findClass(method.getClassEntry().getName());
diff --git a/src/main/java/net/runelite/asm/attributes/code/instructions/New.java b/src/main/java/net/runelite/asm/attributes/code/instructions/New.java
index 7ff6edbe82..466531da1f 100644
--- a/src/main/java/net/runelite/asm/attributes/code/instructions/New.java
+++ b/src/main/java/net/runelite/asm/attributes/code/instructions/New.java
@@ -62,6 +62,7 @@ public class New extends Instruction
public void setNewClass(Class clazz)
{
this.clazz = clazz;
+ lookup();
}
@Override
diff --git a/src/main/java/net/runelite/deob/injection/Inject.java b/src/main/java/net/runelite/deob/injection/Inject.java
index fbe053cbdc..cfccdec11f 100644
--- a/src/main/java/net/runelite/deob/injection/Inject.java
+++ b/src/main/java/net/runelite/deob/injection/Inject.java
@@ -11,6 +11,7 @@ import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.Attributes;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.annotation.Annotation;
+import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
@@ -172,7 +173,12 @@ public class Inject
assert !m.isStatic();
String exportedName = an.find(EXPORT).getElement().getString();
- obfuscatedName = an.find(OBFUSCATED_NAME).getElement().getString();
+
+ Annotation obAn = an.find(OBFUSCATED_NAME);
+ if (obAn != null)
+ obfuscatedName = obAn.getElement().getString();
+ else
+ obfuscatedName = m.getName();
Method otherm;
@@ -181,8 +187,11 @@ public class Inject
String garbage = null;
if (obfuscatedSignature != null)
{
- String signatureString = obfuscatedSignature.getElements().get(0).getString();
- garbage = obfuscatedSignature.getElements().get(1).getString();
+ List elements = obfuscatedSignature.getElements();
+
+ String signatureString = elements.get(0).getString();
+ if (elements.size() == 2)
+ garbage = obfuscatedSignature.getElements().get(1).getString();
Signature signature = new Signature(signatureString); // parse signature
@@ -349,7 +358,11 @@ public class Inject
// deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature
// invokeMethod = method to invoke, obfuscated
- assert clazz.findMethod(method.getName()) == null;
+ if (clazz.findMethod(method.getName()) != null)
+ {
+ return; // hmm. this might be due to an export/import of a non obfuscated method
+ }
+
assert !invokeMethod.isStatic();
assert invokeMethod.getMethods().getClassFile() == clazz;
@@ -423,6 +436,10 @@ public class Inject
{
// function requires garbage value
+ // if garbage is null here it might just be an unused parameter, not part of the obfuscation
+ if (garbage == null)
+ garbage = "0";
+
switch (lastGarbageArgumentType.getType())
{
case "Z":
diff --git a/src/main/java/net/runelite/deob/injection/InjectReplace.java b/src/main/java/net/runelite/deob/injection/InjectReplace.java
index 5b579623af..90da6e6a75 100644
--- a/src/main/java/net/runelite/deob/injection/InjectReplace.java
+++ b/src/main/java/net/runelite/deob/injection/InjectReplace.java
@@ -14,24 +14,31 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
+import net.runelite.asm.attributes.code.instructions.AConstNull;
import net.runelite.asm.attributes.code.instructions.ALoad;
+import net.runelite.asm.attributes.code.instructions.BiPush;
import net.runelite.asm.attributes.code.instructions.DLoad;
import net.runelite.asm.attributes.code.instructions.FLoad;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
+import net.runelite.asm.attributes.code.instructions.LDC2_W;
+import net.runelite.asm.attributes.code.instructions.LDC_W;
import net.runelite.asm.attributes.code.instructions.LLoad;
import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.Pop;
import net.runelite.asm.attributes.code.instructions.Return;
+import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.pool.NameAndType;
+import net.runelite.asm.signature.Signature;
import net.runelite.asm.signature.Type;
public class InjectReplace
{
private static final Type REPLACE = new Type("Lnet/runelite/mapping/Replace;");
private static final Type OBFUSCATED_OVERRIDE = new Type("Lnet/runelite/mapping/ObfuscatedOverride;");
- //private static final Type OBFUSCATED_NAME = new Type("Lnet/runelite/mapping/ObfuscatedName;");
+ private static final Type OBFUSCATED_NAME = new Type("Lnet/runelite/mapping/ObfuscatedName;");
+ private static final Type OBFUSCATED_SIGNATURE = new Type("Lnet/runelite/mapping/ObfuscatedSignature;");
private static final Type EXPORT = new Type("Lnet/runelite/mapping/Export;");
private ClassFile cf, vanilla;
@@ -79,6 +86,7 @@ public class InjectReplace
// set parent
classToInject.setParentClass(vanilla.getPoolClass());
vanilla.clearFinal(); // can't be final anymore now that we inherit from it
+ classToInject.clearAbstract(); // this is being instantiated now, so is no longer abstract
injectConstructors(classToInject);
@@ -94,7 +102,7 @@ public class InjectReplace
private void injectConstructors(ClassFile classToInject)
{
- // Delete compiler generate constructors
+ // Delete compiler generated constructors
Methods methods = classToInject.getMethods();
Methods vanillaMethods = vanilla.getMethods();
@@ -139,6 +147,9 @@ public class InjectReplace
Method constructor = new Method(methods, "", m.getDescriptor());
constructor.setAccessFlags(Method.ACC_PUBLIC);
+ // ensure vanilla ctor is public too
+ m.setAccessFlags(Method.ACC_PUBLIC);
+
Attributes methodAttributes = constructor.getAttributes();
// create code attribute
@@ -222,41 +233,102 @@ public class InjectReplace
String overridenMethod = annotation.getElement().getString(); // name of @Exported method to override
// Find method with exported name on 'cf'
- Method obfuscatedMethodToOverride = findMethodByExportedName(overridenMethod);
+ Method obfuscatedMethodToOverride = findMethodByExportedName(overridenMethod); // this is from the deobfuscated jar
+ Method vanillaMethodToOverride = findVanillaMethodFromDeobfuscatedMethod(obfuscatedMethodToOverride);
NameAndType deobfuscatedNat = m.getNameAndType();
assert obfuscatedMethodToOverride != null;
- assert !obfuscatedMethodToOverride.isFinal();
- assert !obfuscatedMethodToOverride.isPrivate();
+ assert vanillaMethodToOverride != null;
+
+ vanillaMethodToOverride.setFinal(false);
+ vanillaMethodToOverride.setPublic();
+
+ assert !vanillaMethodToOverride.isFinal();
+ assert !vanillaMethodToOverride.isPrivate();
// Rename method to override
- m.setName(obfuscatedMethodToOverride.getName());
+ m.setName(vanillaMethodToOverride.getName());
- assert false;
- if (!m.getDescriptor().equals(obfuscatedMethodToOverride.getDescriptor()))
+ String garbageValue = null;
+ Signature originalSignature = null;
+ if (!m.getDescriptor().equals(vanillaMethodToOverride.getDescriptor()))
{
// Obfuscation can add garbage parameter.
- assert m.getDescriptor().size() + 1 == obfuscatedMethodToOverride.getDescriptor().size();
+ assert m.getDescriptor().size() + 1 == vanillaMethodToOverride.getDescriptor().size();
- // Either we have to modify the bytecode when it is copied over to include this,
- // or maybe can inject overloaded function into superclass if it doesn't cause a signature collision
- assert false;
+ originalSignature = m.getDescriptor();
+
+ m.arguments = vanillaMethodToOverride.getDescriptor(); // is this right?
+
+ garbageValue = this.getGarbage(obfuscatedMethodToOverride);
}
// This means method is overriden. It is possible that the return value is a child class
// of the parents overriden method, and it will still override the method however the signatures won't match,
// but we don't do that.
- assert m.getDescriptor().equals(obfuscatedMethodToOverride.getDescriptor());
+ assert m.getDescriptor().equals(vanillaMethodToOverride.getDescriptor());
// Now that the function is overriden, when the invoke injector is called, it turns around and invokevirtuals
// the parent method, which hits ours.
// locate super.method() calls and modify...
- for (Instruction i : m.getCode().getInstructions().getInstructions())
+ for (Instruction i : new ArrayList<>(m.getCode().getInstructions().getInstructions()))
{
if (!(i instanceof InvokeSpecial))
continue;
+ if (originalSignature != null)
+ {
+ assert originalSignature.size() + 1 == m.getDescriptor().size();
+
+ Instructions instructions = m.getCode().getInstructions();
+ List ins = instructions.getInstructions();
+ Type type = m.getDescriptor().getTypeOfArg(m.getDescriptor().size() - 1);
+ int offset = ins.indexOf(i);
+
+ assert offset != -1;
+
+ // XXX we could maybe just pull the variable off of the lvt here, instead
+ // if we know we haven't overwritten it?
+ if (type.getArrayDims() > 0 || !type.isPrimitive())
+ {
+ ins.add(offset, new AConstNull(instructions));
+ }
+ else
+ {
+ if (garbageValue == null)
+ {
+ garbageValue = "0";
+ }
+
+ switch (type.getType())
+ {
+ case "Z":
+ case "B":
+ case "C":
+ ins.add(offset, new BiPush(instructions, Byte.parseByte(garbageValue)));
+ break;
+ case "S":
+ ins.add(offset, new SiPush(instructions, Short.parseShort(garbageValue)));
+ break;
+ case "I":
+ ins.add(offset, new LDC_W(instructions, Integer.parseInt(garbageValue)));
+ break;
+ case "D":
+ ins.add(offset, new LDC2_W(instructions, Double.parseDouble(garbageValue)));
+ break;
+ case "F":
+ ins.add(offset, new LDC_W(instructions, Float.parseFloat(garbageValue)));
+ break;
+ case "J":
+ ins.add(offset, new LDC2_W(instructions, Long.parseLong(garbageValue)));
+ break;
+ default:
+ throw new RuntimeException("Unknown type");
+ }
+ }
+ }
+
InvokeSpecial is = (InvokeSpecial) i;
net.runelite.asm.pool.Method invokedMethod = (net.runelite.asm.pool.Method) is.getMethod();
@@ -293,6 +365,60 @@ public class InjectReplace
return null;
}
+ private Method findVanillaMethodFromDeobfuscatedMethod(Method method)
+ {
+ String name = getObfuscatedName(method);
+ Signature sig = getObfuscatedSignature(method);
+ return vanilla.findMethod(name, sig);
+ }
+
+ private String getObfuscatedName(Method method)
+ {
+ Annotations an = method.getAttributes().getAnnotations();
+ if (an == null)
+ return method.getName();
+
+ Annotation a = an.find(OBFUSCATED_NAME);
+ if (a == null)
+ return method.getName();
+
+ return a.getElement().getString();
+ }
+
+ private Signature getObfuscatedSignature(Method method)
+ {
+ Annotations an = method.getAttributes().getAnnotations();
+ if (an == null)
+ {
+ return method.getDescriptor();
+ }
+
+ Annotation obSig = an.find(OBFUSCATED_SIGNATURE);
+ if (obSig == null)
+ {
+ return method.getDescriptor();
+ }
+
+ return new Signature(obSig.getElement().getString());
+ }
+
+ private String getGarbage(Method method)
+ {
+ Annotations an = method.getAttributes().getAnnotations();
+ if (an == null)
+ {
+ return null;
+ }
+
+ Annotation obSig = an.find(OBFUSCATED_SIGNATURE);
+ if (obSig == null || obSig.getElements().size() < 2)
+ {
+ return null;
+ }
+
+ return obSig.getElements().get(1).getString();
+ }
+
private void replaceSuperclass(ClassFile classToInject)
{
for (ClassFile cf : vanilla.getGroup().getClasses())
@@ -382,6 +508,9 @@ public class InjectReplace
InvokeSpecial is = (InvokeSpecial) i;
net.runelite.asm.pool.Method method = (net.runelite.asm.pool.Method) is.getMethod();
+ if (!method.getNameAndType().getName().equals("") || !method.getClassEntry().equals(vanilla.getPoolClass()))
+ continue;
+
is.setMethod(new net.runelite.asm.pool.Method(
classToInject.getPoolClass(),
method.getNameAndType()
diff --git a/src/main/java/net/runelite/inject/RSCanvas.java b/src/main/java/net/runelite/inject/RSCanvas.java
new file mode 100644
index 0000000000..004d140106
--- /dev/null
+++ b/src/main/java/net/runelite/inject/RSCanvas.java
@@ -0,0 +1,27 @@
+package net.runelite.inject;
+
+import java.awt.Canvas;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+
+public abstract class RSCanvas extends Canvas
+{
+ private final BufferedImage clientBuffer = new BufferedImage(756, 503, BufferedImage.TYPE_INT_RGB);
+ private final BufferedImage gameBuffer = new BufferedImage(756, 503, BufferedImage.TYPE_INT_RGB);
+
+ @Override
+ public Graphics getGraphics()
+ {
+ Graphics clientGraphics = clientBuffer.getGraphics();
+ clientGraphics.drawImage(gameBuffer, 0, 0, null);
+ //clientGraphics.dispose();
+
+ //clientGraphics = clientBuffer.getGraphics();
+ clientGraphics.drawString("something, something", 42, 42);
+
+ Graphics superGraphics = super.getGraphics();
+ superGraphics.drawImage(clientBuffer, 0, 0, null);
+
+ return gameBuffer.getGraphics();
+ }
+}