RSCanvas stuff, mostly from runeloader. I assume this breaks resizable. I can't tell whether or not this defers all rendering by 1 frame. Fix various injectreplace bugs. I need to make the unused parameter annotate methods with ObfuscatedSignature or I can't export methods that had an unused parameter. This is going to all break if I ever need to export something with an unused parameter that isn't at the end. Should consider only removing unused parameters that I can detect as being garbage.

This commit is contained in:
Adam
2016-04-09 23:03:55 -04:00
parent bbfa8e2ecb
commit 92ddde57b0
10 changed files with 228 additions and 28 deletions

View File

@@ -39,6 +39,11 @@
<artifactId>api</artifactId> <artifactId>api</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>net.runelite</groupId>
<artifactId>client</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>

View File

@@ -10,6 +10,7 @@ import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.runelite.asm.signature.Signature;
public class ClassFile public class ClassFile
{ {
@@ -246,6 +247,11 @@ public class ClassFile
{ {
return methods.findMethod(name); return methods.findMethod(name);
} }
public Method findMethod(String name, Signature signature)
{
return methods.findMethod(new NameAndType(name, signature));
}
public Method findMethodDeep(String name) public Method findMethodDeep(String name)
{ {
@@ -300,4 +306,9 @@ public class ClassFile
{ {
this.access_flags &= ~ACC_FINAL; this.access_flags &= ~ACC_FINAL;
} }
public void clearAbstract()
{
this.access_flags &= ~ACC_ABSTRACT;
}
} }

View File

@@ -143,6 +143,14 @@ public class Method
return (accessFlags & ACC_FINAL) != 0; return (accessFlags & ACC_FINAL) != 0;
} }
public void setFinal(boolean f)
{
if (f)
accessFlags |= ACC_FINAL;
else
accessFlags &= ~ACC_FINAL;
}
public boolean isPrivate() public boolean isPrivate()
{ {
return (accessFlags & ACC_PRIVATE) != 0; return (accessFlags & ACC_PRIVATE) != 0;
@@ -152,6 +160,11 @@ public class Method
{ {
accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PRIVATE); accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PRIVATE);
} }
public void setPublic()
{
accessFlags = (short) ((accessFlags & ~ACCESS_MODIFIERS) | ACC_PUBLIC);
}
public Exceptions getExceptions() public Exceptions getExceptions()
{ {

View File

@@ -16,6 +16,11 @@ public class AConstNull extends Instruction
super(instructions, type, pc); super(instructions, type, pc);
} }
public AConstNull(Instructions instructions)
{
super(instructions, InstructionType.ACONST_NULL, -1);
}
@Override @Override
public InstructionContext execute(Frame frame) public InstructionContext execute(Frame frame)
{ {

View File

@@ -68,16 +68,6 @@ public class InvokeInterface extends Instruction implements InvokeInstruction
{ {
return myMethods != null ? myMethods : Arrays.asList(); return myMethods != null ? myMethods : Arrays.asList();
} }
private void findMethodFromClass(List<net.runelite.asm.Method> 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 @Override
public InstructionContext execute(Frame frame) public InstructionContext execute(Frame frame)

View File

@@ -141,11 +141,13 @@ public class InvokeSpecial extends Instruction implements InvokeInstruction
public void setMethod(Method method) public void setMethod(Method method)
{ {
this.method = method; this.method = method;
lookup();
} }
@Override @Override
public void lookup() public void lookup()
{ {
myMethods = null;
ClassGroup group = this.getInstructions().getCode().getAttributes().getClassFile().getGroup(); ClassGroup group = this.getInstructions().getCode().getAttributes().getClassFile().getGroup();
ClassFile otherClass = group.findClass(method.getClassEntry().getName()); ClassFile otherClass = group.findClass(method.getClassEntry().getName());

View File

@@ -62,6 +62,7 @@ public class New extends Instruction
public void setNewClass(Class clazz) public void setNewClass(Class clazz)
{ {
this.clazz = clazz; this.clazz = clazz;
lookup();
} }
@Override @Override

View File

@@ -11,6 +11,7 @@ import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.Attributes; import net.runelite.asm.attributes.Attributes;
import net.runelite.asm.attributes.Code; import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.annotation.Annotation; 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.Instruction;
import net.runelite.asm.attributes.code.InstructionType; import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions; import net.runelite.asm.attributes.code.Instructions;
@@ -172,7 +173,12 @@ public class Inject
assert !m.isStatic(); assert !m.isStatic();
String exportedName = an.find(EXPORT).getElement().getString(); 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; Method otherm;
@@ -181,8 +187,11 @@ public class Inject
String garbage = null; String garbage = null;
if (obfuscatedSignature != null) if (obfuscatedSignature != null)
{ {
String signatureString = obfuscatedSignature.getElements().get(0).getString(); List<Element> elements = obfuscatedSignature.getElements();
garbage = obfuscatedSignature.getElements().get(1).getString();
String signatureString = elements.get(0).getString();
if (elements.size() == 2)
garbage = obfuscatedSignature.getElements().get(1).getString();
Signature signature = new Signature(signatureString); // parse signature Signature signature = new Signature(signatureString); // parse signature
@@ -349,7 +358,11 @@ public class Inject
// deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature // deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature
// invokeMethod = method to invoke, obfuscated // 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.isStatic();
assert invokeMethod.getMethods().getClassFile() == clazz; assert invokeMethod.getMethods().getClassFile() == clazz;
@@ -423,6 +436,10 @@ public class Inject
{ {
// function requires garbage value // 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()) switch (lastGarbageArgumentType.getType())
{ {
case "Z": case "Z":

View File

@@ -14,24 +14,31 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.code.Instruction; import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType; import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions; 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.ALoad;
import net.runelite.asm.attributes.code.instructions.BiPush;
import net.runelite.asm.attributes.code.instructions.DLoad; import net.runelite.asm.attributes.code.instructions.DLoad;
import net.runelite.asm.attributes.code.instructions.FLoad; import net.runelite.asm.attributes.code.instructions.FLoad;
import net.runelite.asm.attributes.code.instructions.ILoad; import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial; import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual; 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.LLoad;
import net.runelite.asm.attributes.code.instructions.New; import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.Pop; import net.runelite.asm.attributes.code.instructions.Pop;
import net.runelite.asm.attributes.code.instructions.Return; 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.pool.NameAndType;
import net.runelite.asm.signature.Signature;
import net.runelite.asm.signature.Type; import net.runelite.asm.signature.Type;
public class InjectReplace public class InjectReplace
{ {
private static final Type REPLACE = new Type("Lnet/runelite/mapping/Replace;"); 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_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 static final Type EXPORT = new Type("Lnet/runelite/mapping/Export;");
private ClassFile cf, vanilla; private ClassFile cf, vanilla;
@@ -79,6 +86,7 @@ public class InjectReplace
// set parent // set parent
classToInject.setParentClass(vanilla.getPoolClass()); classToInject.setParentClass(vanilla.getPoolClass());
vanilla.clearFinal(); // can't be final anymore now that we inherit from it 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); injectConstructors(classToInject);
@@ -94,7 +102,7 @@ public class InjectReplace
private void injectConstructors(ClassFile classToInject) private void injectConstructors(ClassFile classToInject)
{ {
// Delete compiler generate constructors // Delete compiler generated constructors
Methods methods = classToInject.getMethods(); Methods methods = classToInject.getMethods();
Methods vanillaMethods = vanilla.getMethods(); Methods vanillaMethods = vanilla.getMethods();
@@ -139,6 +147,9 @@ public class InjectReplace
Method constructor = new Method(methods, "<init>", m.getDescriptor()); Method constructor = new Method(methods, "<init>", m.getDescriptor());
constructor.setAccessFlags(Method.ACC_PUBLIC); constructor.setAccessFlags(Method.ACC_PUBLIC);
// ensure vanilla ctor is public too
m.setAccessFlags(Method.ACC_PUBLIC);
Attributes methodAttributes = constructor.getAttributes(); Attributes methodAttributes = constructor.getAttributes();
// create code attribute // create code attribute
@@ -222,41 +233,102 @@ public class InjectReplace
String overridenMethod = annotation.getElement().getString(); // name of @Exported method to override String overridenMethod = annotation.getElement().getString(); // name of @Exported method to override
// Find method with exported name on 'cf' // 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(); NameAndType deobfuscatedNat = m.getNameAndType();
assert obfuscatedMethodToOverride != null; assert obfuscatedMethodToOverride != null;
assert !obfuscatedMethodToOverride.isFinal(); assert vanillaMethodToOverride != null;
assert !obfuscatedMethodToOverride.isPrivate();
vanillaMethodToOverride.setFinal(false);
vanillaMethodToOverride.setPublic();
assert !vanillaMethodToOverride.isFinal();
assert !vanillaMethodToOverride.isPrivate();
// Rename method to override // Rename method to override
m.setName(obfuscatedMethodToOverride.getName()); m.setName(vanillaMethodToOverride.getName());
assert false; String garbageValue = null;
if (!m.getDescriptor().equals(obfuscatedMethodToOverride.getDescriptor())) Signature originalSignature = null;
if (!m.getDescriptor().equals(vanillaMethodToOverride.getDescriptor()))
{ {
// Obfuscation can add garbage parameter. // 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, originalSignature = m.getDescriptor();
// or maybe can inject overloaded function into superclass if it doesn't cause a signature collision
assert false; 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 // 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, // of the parents overriden method, and it will still override the method however the signatures won't match,
// but we don't do that. // 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 // Now that the function is overriden, when the invoke injector is called, it turns around and invokevirtuals
// the parent method, which hits ours. // the parent method, which hits ours.
// locate super.method() calls and modify... // 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)) if (!(i instanceof InvokeSpecial))
continue; continue;
if (originalSignature != null)
{
assert originalSignature.size() + 1 == m.getDescriptor().size();
Instructions instructions = m.getCode().getInstructions();
List<Instruction> 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; InvokeSpecial is = (InvokeSpecial) i;
net.runelite.asm.pool.Method invokedMethod = (net.runelite.asm.pool.Method) is.getMethod(); net.runelite.asm.pool.Method invokedMethod = (net.runelite.asm.pool.Method) is.getMethod();
@@ -293,6 +365,60 @@ public class InjectReplace
return null; 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) private void replaceSuperclass(ClassFile classToInject)
{ {
for (ClassFile cf : vanilla.getGroup().getClasses()) for (ClassFile cf : vanilla.getGroup().getClasses())
@@ -382,6 +508,9 @@ public class InjectReplace
InvokeSpecial is = (InvokeSpecial) i; InvokeSpecial is = (InvokeSpecial) i;
net.runelite.asm.pool.Method method = (net.runelite.asm.pool.Method) is.getMethod(); net.runelite.asm.pool.Method method = (net.runelite.asm.pool.Method) is.getMethod();
if (!method.getNameAndType().getName().equals("<init>") || !method.getClassEntry().equals(vanilla.getPoolClass()))
continue;
is.setMethod(new net.runelite.asm.pool.Method( is.setMethod(new net.runelite.asm.pool.Method(
classToInject.getPoolClass(), classToInject.getPoolClass(),
method.getNameAndType() method.getNameAndType()

View File

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