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>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.runelite</groupId>
<artifactId>client</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

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

View File

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

View File

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

View File

@@ -68,16 +68,6 @@ public class InvokeInterface extends Instruction implements InvokeInstruction
{
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
public InstructionContext execute(Frame frame)

View File

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

View File

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

View File

@@ -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<Element> 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":

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.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, "<init>", 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<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;
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("<init>") || !method.getClassEntry().equals(vanilla.getPoolClass()))
continue;
is.setMethod(new net.runelite.asm.pool.Method(
classToInject.getPoolClass(),
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();
}
}