Merge pull request #1879 from Lucwousin/inj-stuff

Project: move injector to another repository
This commit is contained in:
Owain van Brakel
2019-11-04 01:51:20 +01:00
committed by GitHub
85 changed files with 1916 additions and 7950 deletions

View File

@@ -29,10 +29,13 @@ import org.ajoberstar.grgit.Grgit
buildscript {
repositories {
maven(url = "https://plugins.gradle.org/m2/")
maven(url = "https://raw.githubusercontent.com/open-osrs/hosting/master")
mavenLocal()
}
dependencies {
classpath(Plugins.grgitPlugin)
classpath(Plugins.versionsPlugin)
classpath(Plugins.injectorPlugin)
}
}
@@ -57,7 +60,9 @@ fun isNonStable(version: String): Boolean {
}
allprojects {
apply<JavaLibraryPlugin>()
apply<MavenPlugin>()
apply<MavenPublishPlugin>()
group = "com.openosrs"
version = ProjectVersions.rlVersion
@@ -66,16 +71,23 @@ allprojects {
project.extra["gitCommitShort"] = localGitCommitShort
project.extra["rootPath"] = rootDir.toString().replace("\\", "/")
project.extra["injectedClassesPath"] = "${rootDir}/injector-plugin/out/injected-client/"
}
subprojects {
apply<JavaLibraryPlugin>()
apply<MavenPlugin>()
apply<MavenPublishPlugin>()
apply(plugin = Plugins.testLogger.first)
if (this.name != "rs-client") apply(plugin = "checkstyle")
if (this.name != "runescape-client") {
apply(plugin = "checkstyle")
configure<CheckstyleExtension> {
sourceSets = setOf(project.sourceSets.main.get())
configFile = file("${rootDir}/checkstyle/checkstyle.xml")
configProperties = mapOf("suppressionFile" to file("${rootDir}/checkstyle/suppressions.xml"))
maxWarnings = 0
toolVersion = "6.4.1"
isShowViolations = true
isIgnoreFailures = false
}
}
repositories {
mavenLocal()
@@ -90,16 +102,6 @@ subprojects {
}
}
configure<CheckstyleExtension> {
sourceSets = setOf(project.sourceSets.main.get())
configFile = file("${rootDir}/checkstyle/checkstyle.xml")
configProperties = mapOf("suppressionFile" to file("${rootDir}/checkstyle/suppressions.xml"))
maxWarnings = 0
toolVersion = "6.4.1"
isShowViolations = true
isIgnoreFailures = false
}
configure<PublishingExtension> {
repositories {
maven {

View File

@@ -38,7 +38,7 @@ object ProjectVersions {
object Plugins {
val grgitPlugin = "org.ajoberstar:grgit:2.3.0"
val versionsPlugin = "com.github.ben-manes:gradle-versions-plugin:0.27.0"
val injectorPlugin = "com.openosrs:injector-plugin:1.0.0"
val testLogger = Pair("com.adarshr.test-logger", "2.0.0")
val versions = Pair("com.github.ben-manes.versions", "0.27.0")
val buildScan = Pair("com.gradle.build-scan", "3.0")

View File

@@ -35,11 +35,12 @@ dependencies {
deobjars(group = "net.runelite.rs", name = "vanilla", version = ProjectVersions.rsversion.toString())
deobjars(project(":runescape-client"))
implementation(Libraries.gson)
implementation(Libraries.guava)
implementation(Libraries.fernflower)
implementation(Libraries.annotations)
implementation(Libraries.asmAll)
implementation(Libraries.asmUtil)
implementation(Libraries.fernflower)
implementation(Libraries.gson)
implementation(Libraries.guava)
implementation(Libraries.slf4jApi)
implementation(project(":runelite-api"))
implementation(project(":runescape-api"))

View File

@@ -0,0 +1,17 @@
package net.runelite.asm;
import java.util.Iterator;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import org.jetbrains.annotations.NotNull;
public interface Annotated extends Iterable<Annotation>
{
Annotations getAnnotations();
@NotNull
default Iterator<Annotation> iterator()
{
return getAnnotations().iterator();
}
}

View File

@@ -31,13 +31,12 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import static net.runelite.deob.DeobAnnotations.*;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class ClassFile
public class ClassFile implements Annotated, Named
{
private ClassGroup group;
@@ -100,10 +99,9 @@ public class ClassFile
visitor.visit(version, access, name.getName(), null, super_class.getName(), ints);
visitor.visitSource(source, null);
for (Annotation annotation : annotations.getAnnotations())
for (Annotation annotation : annotations)
{
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
annotation.accept(av);
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
}
for (Field field : fields)

View File

@@ -27,13 +27,16 @@ package net.runelite.asm;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.signature.Signature;
import static net.runelite.deob.DeobAnnotations.*;
import org.jetbrains.annotations.NotNull;
public class ClassGroup
public class ClassGroup implements Iterable<ClassFile>
{
private final List<ClassFile> classes = new ArrayList<>(); // to keep order
private final Map<String, ClassFile> classMap = new HashMap<>();
@@ -156,4 +159,17 @@ public class ClassGroup
return findClass(name);
}
@NotNull
@Override
public Iterator<ClassFile> iterator()
{
return this.classes.iterator();
}
@Override
public void forEach(Consumer<? super ClassFile> action)
{
this.classes.forEach(action);
}
}

View File

@@ -27,15 +27,13 @@ package net.runelite.asm;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.deob.DeobAnnotations;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Opcodes;
import static org.objectweb.asm.Opcodes.ACC_PRIVATE;
import static org.objectweb.asm.Opcodes.ACC_PROTECTED;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
public class Field
public class Field implements Annotated, Named
{
public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
@@ -53,15 +51,14 @@ public class Field
this.name = name;
this.type = type;
annotations = new Annotations();
this.annotations = new Annotations();
}
public void accept(FieldVisitor visitor)
{
for (Annotation annotation : annotations.getAnnotations())
{
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
annotation.accept(av);
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
}
visitor.visitEnd();

View File

@@ -25,12 +25,14 @@
package net.runelite.asm;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import net.runelite.asm.pool.Class;
import net.runelite.deob.DeobAnnotations;
import org.jetbrains.annotations.NotNull;
public class Interfaces
public class Interfaces implements Iterable<Class>
{
private final ClassFile classFile;
@@ -107,4 +109,10 @@ public class Interfaces
return names;
}
@NotNull
public Iterator<Class> iterator()
{
return this.interfaces.iterator();
}
}

View File

@@ -36,7 +36,6 @@ import net.runelite.asm.attributes.code.Parameter;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.objectweb.asm.Opcodes.ACC_FINAL;
@@ -47,7 +46,7 @@ import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
import static org.objectweb.asm.Opcodes.ACC_SYNCHRONIZED;
public class Method
public class Method implements Annotated, Named
{
public static final int ACCESS_MODIFIERS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED;
@@ -92,8 +91,7 @@ public class Method
for (Annotation annotation : annotations.getAnnotations())
{
AnnotationVisitor av = visitor.visitAnnotation(annotation.getType().toString(), true);
annotation.accept(av);
annotation.accept(visitor.visitAnnotation(annotation.getType().toString(), true));
}
if (code != null)

View File

@@ -0,0 +1,6 @@
package net.runelite.asm;
public interface Named
{
String getName();
}

View File

@@ -26,13 +26,16 @@
package net.runelite.asm.attributes;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import org.jetbrains.annotations.NotNull;
public class Annotations
public class Annotations implements Iterable<Annotation>
{
private final List<Annotation> annotations = new ArrayList<>();
@@ -71,15 +74,19 @@ public class Annotations
public Annotation addAnnotation(Type type, String name, Object value)
{
Annotation annotation = new Annotation(this);
annotation.setType(type);
Annotation annotation = new Annotation(type);
addAnnotation(annotation);
Element element = new Element(annotation);
element.setName(name);
element.setValue(value);
Element element = new SimpleElement(name, value);
annotation.addElement(element);
return annotation;
}
@NotNull
@Override
public Iterator<Annotation> iterator()
{
return this.annotations.iterator();
}
}

View File

@@ -77,7 +77,6 @@ public class Code
/**
* calculates the size of the lvt required for this method
* @return
*/
public int getMaxLocals()
{

View File

@@ -26,30 +26,26 @@
package net.runelite.asm.attributes.annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations;
import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.AnnotationVisitor;
public class Annotation
public class Annotation extends Element<List<Element>> implements Iterable<Element>
{
private final Annotations annotations;
private Type type;
private final List<Element> elements = new ArrayList<>();
private final Type type;
public Annotation(Annotations annotations)
public Annotation(Type type)
{
this.annotations = annotations;
this.value = new ArrayList<>();
this.type = type;
}
public Annotations getAnnotations()
{
return annotations;
}
public void setType(Type type)
public Annotation(String name, Type type)
{
this.value = new ArrayList<>();
this.name = name;
this.type = type;
}
@@ -60,23 +56,44 @@ public class Annotation
public List<Element> getElements()
{
return elements;
return value;
}
public Element getElement()
{
return elements.get(0);
return value.get(0);
}
public void addElement(Element element)
{
elements.add(element);
value.add(element);
}
@Override
public final void setValue(List<Element> value)
{
throw new UnsupportedOperationException();
}
public void accept(AnnotationVisitor visitor)
{
for (Element element : elements)
visitor.visit(element.getName(), element.getValue());
if (visitor == null)
{
return;
}
for (Element element : this)
{
accept(visitor, element.name, element.value);
}
visitor.visitEnd();
}
@NotNull
@Override
public Iterator<Element> iterator()
{
return this.value.iterator();
}
}

View File

@@ -0,0 +1,42 @@
package net.runelite.asm.attributes.annotation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import org.jetbrains.annotations.NotNull;
public class ArrayElement extends Element<List> implements Iterable<Object>
{
public ArrayElement(String name)
{
this.name = name;
this.value = new ArrayList<>();
}
@SuppressWarnings("unchecked")
public void addValue(Object value)
{
this.value.add(value);
}
@Override
public final void setValue(List value)
{
throw new UnsupportedOperationException();
}
@NotNull
@Override
@SuppressWarnings("unchecked")
public Iterator<Object> iterator()
{
return this.value.iterator();
}
@SuppressWarnings("unchecked")
public Stream<Object> stream()
{
return this.value.stream();
}
}

View File

@@ -25,21 +25,14 @@
package net.runelite.asm.attributes.annotation;
public class Element
import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
public abstract class Element<T>
{
private final Annotation annotation;
private String name;
private Object value;
String name = "value";
public Element(Annotation annotation)
{
this.annotation = annotation;
}
public Annotation getAnnotation()
{
return annotation;
}
T value;
public String getName()
{
@@ -51,12 +44,12 @@ public class Element
this.name = name;
}
public Object getValue()
public T getValue()
{
return value;
}
public void setValue(Object value)
public void setValue(T value)
{
this.value = value;
}
@@ -65,4 +58,34 @@ public class Element
{
return value.toString();
}
public static void accept(AnnotationVisitor visitor, final String name, final Object value)
{
if (visitor == null)
{
return;
}
if (value instanceof Annotation)
{
Annotation annotation = (Annotation) value;
annotation.accept(visitor.visitAnnotation(name, annotation.getType().toString()));
}
else if (value instanceof List)
{
AnnotationVisitor arr = visitor.visitArray(name);
List<?> arrayValue = (List<?>) value;
for (Object o : arrayValue)
{
accept(arr, null, o);
}
arr.visitEnd();
}
else
{
visitor.visit(name, value);
}
}
}

View File

@@ -0,0 +1,15 @@
package net.runelite.asm.attributes.annotation;
public class SimpleElement extends Element<Object>
{
public SimpleElement(Object value)
{
this.value = value;
}
public SimpleElement(String name, Object value)
{
this.name = name;
this.value = value;
}
}

View File

@@ -26,11 +26,14 @@ package net.runelite.asm.attributes.code;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import net.runelite.asm.attributes.Code;
import org.jetbrains.annotations.NotNull;
public class Instructions
public class Instructions implements Iterable<Instruction>
{
private final Code code;
private final List<Instruction> instructions = new ArrayList<>();
@@ -186,4 +189,25 @@ public class Instructions
return i;
}
public int size()
{
return this.instructions.size();
}
@NotNull
public Iterator<Instruction> iterator()
{
return this.instructions.iterator();
}
public ListIterator<Instruction> listIterator()
{
return this.instructions.listIterator();
}
public ListIterator<Instruction> listIterator(int i)
{
return this.instructions.listIterator(i);
}
}

View File

@@ -25,44 +25,59 @@
package net.runelite.asm.visitors;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.ArrayElement;
import net.runelite.asm.attributes.annotation.SimpleElement;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
public class MethodAnnotationVisitor extends AnnotationVisitor
public class AnnotationElementVisitor extends AnnotationVisitor
{
private final Method method;
private final Type type;
private final Annotation annotation;
public MethodAnnotationVisitor(Method method, Type type)
AnnotationElementVisitor(Annotation annotation)
{
super(Opcodes.ASM5);
this.method = method;
this.type = type;
annotation = new Annotation(method.getAnnotations());
annotation.setType(type);
this.annotation = annotation;
}
@Override
public void visit(String name, Object value)
{
Element element = new Element(annotation);
element.setName(name);
element.setValue(value);
SimpleElement element = new SimpleElement(name, value);
annotation.addElement(element);
}
@Override
public void visitEnd()
public AnnotationVisitor visitArray(String name)
{
method.getAnnotations().addAnnotation(annotation);
ArrayElement element = new ArrayElement(name);
this.annotation.addElement(element);
return new AnnotationVisitor(Opcodes.ASM5)
{
@Override
public void visit(String name, Object value)
{
element.addValue(value);
}
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor)
{
Annotation annotation = new Annotation(name, new Type(descriptor));
element.addValue(annotation);
return new AnnotationElementVisitor(annotation);
}
};
}
@Override
public AnnotationVisitor visitAnnotation(String name, String descriptor)
{
Annotation annotation = new Annotation(name, new Type(descriptor));
this.annotation.addElement(annotation);
return new AnnotationElementVisitor(annotation);
}
}

View File

@@ -1,68 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.asm.visitors;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
public class ClassAnnotationVisitor extends AnnotationVisitor
{
private final ClassFile classFile;
private final Type type;
private final Annotation annotation;
public ClassAnnotationVisitor(ClassFile classFile, Type type)
{
super(Opcodes.ASM5);
this.classFile = classFile;
this.type = type;
annotation = new Annotation(classFile.getAnnotations());
annotation.setType(type);
}
@Override
public void visit(String name, Object value)
{
Element element = new Element(annotation);
element.setName(name);
element.setValue(value);
annotation.addElement(element);
}
@Override
public void visitEnd()
{
classFile.getAnnotations().addAnnotation(annotation);
}
}

View File

@@ -22,12 +22,12 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.asm.visitors;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.FieldVisitor;
@@ -35,25 +35,25 @@ import org.objectweb.asm.Opcodes;
public class ClassFieldVisitor extends FieldVisitor
{
private final ClassFile classFile;
private final Field field;
public ClassFieldVisitor(ClassFile cf, int access, String name, Type desc, Object value)
ClassFieldVisitor(ClassFile cf, int access, String name, Type desc, Object value)
{
super(Opcodes.ASM5);
this.classFile = cf;
this.field = new Field(cf, name, desc);
this.field.setAccessFlags(access);
this.field.setValue(value);
field = new Field(cf, name, desc);
field.setAccessFlags(access);
field.setValue(value);
cf.addField(field);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
Type type = new Type(desc);
return new FieldAnnotationVisitor(field, type);
Annotation element = new Annotation(new Type(desc));
this.field.getAnnotations().addAnnotation(element);
return new AnnotationElementVisitor(element);
}
@Override
@@ -61,10 +61,4 @@ public class ClassFieldVisitor extends FieldVisitor
{
System.out.println(attr);
}
@Override
public void visitEnd()
{
classFile.addField(field);
}
}

View File

@@ -27,6 +27,7 @@ package net.runelite.asm.visitors;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.signature.Signature;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
@@ -69,8 +70,10 @@ public class ClassFileVisitor extends ClassVisitor
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
Type type = new Type(desc);
return new ClassAnnotationVisitor(classFile, type);
Annotation annotation = new Annotation(new Type(desc));
classFile.getAnnotations().addAnnotation(annotation);
return new AnnotationElementVisitor(annotation);
}
@Override

View File

@@ -32,6 +32,7 @@ import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.code.Exceptions;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
@@ -72,18 +73,14 @@ import static org.objectweb.asm.Opcodes.ICONST_5;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class CodeVisitor extends MethodVisitor
{
private static final Logger logger = LoggerFactory.getLogger(CodeVisitor.class);
private final ClassFile classFile;
private final Method method;
private Code code;
public CodeVisitor(ClassFile classFile, int access, String name, Signature signature, String[] sexceptions)
CodeVisitor(ClassFile classFile, int access, String name, Signature signature, String[] sexceptions)
{
super(Opcodes.ASM5);
@@ -111,8 +108,9 @@ public class CodeVisitor extends MethodVisitor
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible)
{
Type type = new Type(desc);
return new MethodAnnotationVisitor(method, type);
Annotation element = new Annotation(new Type(desc));
this.method.getAnnotations().addAnnotation(element);
return new AnnotationElementVisitor(element);
}
@Override
@@ -333,7 +331,7 @@ public class CodeVisitor extends MethodVisitor
if (cst instanceof org.objectweb.asm.Type)
{
org.objectweb.asm.Type t = (org.objectweb.asm.Type) cst;
entry = new net.runelite.asm.pool.Class((String) t.getClassName());
entry = new net.runelite.asm.pool.Class(t.getClassName());
}
LDC ldc = new LDC(code.getInstructions(), entry);

View File

@@ -1,68 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.asm.visitors;
import net.runelite.asm.Field;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Opcodes;
public class FieldAnnotationVisitor extends AnnotationVisitor
{
private final Field field;
private final Type type;
private final Annotation annotation;
public FieldAnnotationVisitor(Field field, Type type)
{
super(Opcodes.ASM5);
this.field = field;
this.type = type;
annotation = new Annotation(field.getAnnotations());
annotation.setType(type);
}
@Override
public void visit(String name, Object value)
{
Element element = new Element(annotation);
element.setName(name);
element.setValue(value);
annotation.addElement(element);
}
@Override
public void visitEnd()
{
field.getAnnotations().addAnnotation(annotation);
}
}

View File

@@ -193,6 +193,7 @@ public class Renamer implements Deobfuscator
}
@Override
@SuppressWarnings("unchecked")
public void run(ClassGroup group)
{
group.buildClassGraph();

View File

@@ -37,6 +37,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.InstructionType;
import net.runelite.asm.attributes.code.Instructions;
@@ -113,7 +114,6 @@ public class ConstantParameter implements Deobfuscator
List<StackContext> pops = invokeCtx.getPops();
outer:
// object is popped first, then param 1, 2, 3, etc. double and long take two slots.
for (int lvtOffset = offset, parameterIndex = 0;
parameterIndex < method.getDescriptor().size();
@@ -451,9 +451,7 @@ public class ConstantParameter implements Deobfuscator
}
// Add garbage value
Element element = new Element(obfuscatedSignature);
element.setName("garbageValue");
element.setValue(value.toString());
Element element = new SimpleElement("garbageValue", value.toString());
obfuscatedSignature.addElement(element);
}
}
@@ -464,12 +462,12 @@ public class ConstantParameter implements Deobfuscator
public void run(ClassGroup group)
{
Execution execution = new Execution(group);
execution.addExecutionVisitor(i -> findParameters(i));
execution.addExecutionVisitor(this::findParameters);
execution.populateInitialMethods();
execution.run();
execution = new Execution(group);
execution.addMethodContextVisitor(mc -> findDeadParameters(mc));
execution.addMethodContextVisitor(this::findDeadParameters);
execution.populateInitialMethods();
execution.run();

View File

@@ -32,6 +32,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import net.runelite.deob.DeobAnnotations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -124,15 +125,12 @@ public class AnnotationMapper
{
if (isCopyable(a))
{
Annotation annotation = new Annotation(to);
annotation.setType(a.getType());
Annotation annotation = new Annotation(a.getType());
to.addAnnotation(annotation);
for (Element e : a.getElements())
{
Element element = new Element(annotation);
element.setName(e.getName());
element.setValue(e.getValue());
Element element = new SimpleElement(e.getName(), e.getValue());
annotation.addElement(element);
}
@@ -155,7 +153,6 @@ public class AnnotationMapper
private boolean isCopyable(Annotation a)
{
return a.getType().equals(DeobAnnotations.EXPORT)
|| a.getType().equals(DeobAnnotations.IMPLEMENTS)
|| a.getType().equals(DeobAnnotations.HOOK);
|| a.getType().equals(DeobAnnotations.IMPLEMENTS);
}
}

View File

@@ -8,6 +8,7 @@ import net.runelite.asm.Method;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
import net.runelite.deob.Deob;
import net.runelite.deob.DeobAnnotations;
import org.slf4j.Logger;
@@ -23,6 +24,7 @@ public class AnnotationAdder
private final ClassGroup group;
private final Logger log = LoggerFactory.getLogger(AnnotationAdder.class);
@SuppressWarnings("unchecked")
public void run()
{
int impl = 0;
@@ -50,12 +52,9 @@ public class AnnotationAdder
{
Annotations an = c.getAnnotations();
Annotation implAn = new Annotation(an);
implAn.setType(DeobAnnotations.IMPLEMENTS);
Annotation implAn = new Annotation(DeobAnnotations.IMPLEMENTS);
Element value = new Element(implAn);
value.setValue(c.getClassName());
value.setName("value");
Element value = new SimpleElement(c.getClassName());
implAn.addElement(value);
an.addAnnotation(implAn);
@@ -81,12 +80,9 @@ public class AnnotationAdder
Annotation a = an.find(DeobAnnotations.EXPORT);
if (a == null)
{
a = new Annotation(an);
a.setType(DeobAnnotations.EXPORT);
a = new Annotation(DeobAnnotations.EXPORT);
Element value = new Element(a);
value.setValue(fieldName);
value.setName("value");
Element value = new SimpleElement(fieldName);
a.addElement(value);
an.addAnnotation(a);
@@ -114,12 +110,9 @@ public class AnnotationAdder
Annotation a = an.find(DeobAnnotations.EXPORT);
if (a == null)
{
a = new Annotation(an);
a.setType(DeobAnnotations.EXPORT);
a = new Annotation(DeobAnnotations.EXPORT);
Element value = new Element(a);
value.setValue(methodName);
value.setName("value");
Element value = new SimpleElement(methodName);
a.addElement(value);
an.addAnnotation(a);

View File

@@ -34,6 +34,7 @@ import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.annotation.Element;
import net.runelite.asm.attributes.annotation.SimpleElement;
public class AnnotationCopier
{
@@ -98,14 +99,11 @@ public class AnnotationCopier
if (!isType(a.getType()))
continue;
Annotation a2 = new Annotation(an2);
a2.setType(a.getType());
Annotation a2 = new Annotation(a.getType());
for (Element element : a.getElements())
{
Element element2 = new Element(a2);
element2.setName(element.getName());
element2.setValue(element.getValue());
Element element2 = new SimpleElement(element.getName(), element.getValue());
a2.addElement(element2);
}

View File

@@ -34,13 +34,9 @@ import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.deob.DeobAnnotations;
import net.runelite.deob.deobfuscators.Renamer;
import net.runelite.deob.util.NameMappings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AnnotationRenamer
{
private static final Logger logger = LoggerFactory.getLogger(AnnotationRenamer.class);
private ClassGroup group;
public AnnotationRenamer(ClassGroup group)

View File

@@ -25,9 +25,11 @@
package net.runelite.deob.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -79,6 +81,41 @@ public class JarUtil
return group;
}
public static ClassFile loadClass(byte[] bytes)
{
ClassReader reader = new ClassReader(bytes);
ClassFileVisitor cv = new ClassFileVisitor();
reader.accept(cv, ClassReader.SKIP_FRAMES);
return cv.getClassFile();
}
public static ClassGroup loadClasses(Collection<File> files) throws IOException
{
final ClassGroup group = new ClassGroup();
for (File file : files)
{
if (!file.getName().endsWith(".class"))
{
continue;
}
try (InputStream is = new FileInputStream(file))
{
ClassReader reader = new ClassReader(is);
ClassFileVisitor cv = new ClassFileVisitor();
reader.accept(cv, ClassReader.SKIP_FRAMES);
group.addClass(cv.getClassFile());
}
}
group.initialize();
return group;
}
public static void saveJar(ClassGroup group, File jarfile) throws IOException
{
try (JarOutputStream jout = new JarOutputStream(new FileOutputStream(jarfile), new Manifest()))

View File

@@ -276,8 +276,7 @@ public class HookImporter
{
for (Element e : a.getElements())
{
String str = (String) e.getValue();
return str;
return (String) e.getValue();
}
}
}
@@ -288,7 +287,7 @@ public class HookImporter
private Signature getObfuscatedMethodSignature(Method method)
{
String sig = getAnnotation(method.getAnnotations(), OBFUSCATED_SIGNATURE);
if (sig.isEmpty() == false)
if (!sig.isEmpty())
{
return toObSignature(new Signature(sig)); // if it is annoted, use that
}

View File

@@ -3,4 +3,4 @@ org.gradle.warning.mode=all
org.gradle.parallel=true
org.gradle.console=rich
org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
org.gradle.jvmargs=-XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

View File

@@ -28,31 +28,44 @@ apply<FernflowerPlugin>()
description = "Injected Client"
tasks {
compileJava {
dependsOn(":injector-plugin:assemble")
outputs.upToDateWhen { false }
doLast {
copy {
val f = file ("build/classes/java/main")
f.deleteRecursively()
f.mkdirs()
from(project.extra["injectedClassesPath"])
into("build/classes/java/main")
}
}
}
classes {
val f = file("build/classes/java/main/Placeholder.class")
f.delete()
}
// this is just here to show how the fernflower plugin could be used
//build {
// dependsOn(project.tasks.getByName("decompile"))
//}
plugins {
id("com.openosrs.injector")
}
configurations {
create("vanilla")
create("injected-client")
}
dependencies {
"vanilla"(Libraries.vanilla)
}
injector {
mixins.set(tasks.getByPath(":runelite-mixins:jar").outputs.files.singleFile)
rsapi.set(tasks.getByPath(":runescape-api:jar").outputs.files.singleFile)
rsclient.set(tasks.getByPath(":runescape-client:jar").outputs.files.singleFile)
vanilla.set(project.file(configurations["vanilla"].asPath))
}
artifacts {
add("runtime", tasks.inject.get().output) {
builtBy(tasks.inject)
}
}
// keep the sourcesets etc but remove useless tasks
tasks {
classes {
enabled = false
}
compileJava {
enabled = false
}
jar {
enabled = false
}
processResources {
enabled = false
}
}

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) 2019 ThatGamerBlue
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @author ThatGamerBlue
*
* This file exists to force gradle to execute the compileJava task
* so we can hijack it and run the injector-plugin
*/
public class Placeholder
{
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright (c) 2019 Owain van Brakel <https://github.com/Owain94>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
group = "com.openosrs.rs"
description = "Injector"
val deobfuscatedJar = "${project.extra["rootPath"]}/runescape-client/build/libs/runescape-client-${ProjectVersions.rlVersion}.jar"
val vanillaJar = "${buildDir}/vanilla-${ProjectVersions.rsversion}.jar"
val vanilla = configurations.create("vanilla")
dependencies {
annotationProcessor(Libraries.sisu)
compileOnly(Libraries.mavenPluginAnnotations)
implementation(Libraries.guava)
implementation(Libraries.mavenPluginApi)
implementation(Libraries.asmAll)
implementation(Libraries.asmUtil)
implementation(project(":deobfuscator"))
implementation(project(":runelite-mixins"))
implementation(project(":runelite-api"))
implementation(project(":runescape-api"))
testImplementation(Libraries.junit)
testImplementation(Libraries.mockitoCore)
testImplementation(project(":deobfuscator"))
testImplementation(project(path = ":deobfuscator", configuration = "testArchives"))
vanilla(Libraries.vanilla)
}
tasks {
register<Copy>("copyVanilla") {
copy {
from(configurations.get("vanilla"))
into("$buildDir")
}
}
register<JavaExec>("inject") {
dependsOn("copyVanilla")
classpath = project.sourceSets.main.get().runtimeClasspath
main = "net.runelite.injector.Injector"
args(listOf(deobfuscatedJar, vanillaJar, project.extra["injectedClassesPath"]))
}
compileJava {
dependsOn(":runescape-client:build")
inputs.dir(project(":runescape-client").projectDir.absolutePath)
inputs.dir(project(":runescape-api").projectDir.absolutePath)
inputs.dir(project(":runelite-mixins").projectDir.absolutePath)
}
jar {
dependsOn("inject")
}
}

View File

@@ -1,580 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.HashMap;
import java.util.Map;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Field;
import net.runelite.asm.Interfaces;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.ALoad;
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.LLoad;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import net.runelite.deob.deobfuscators.arithmetic.DMath;
import static net.runelite.injector.InjectUtil.getFieldType;
import net.runelite.injector.raw.ClearColorBuffer;
import net.runelite.injector.raw.DrawAfterWidgets;
import net.runelite.injector.raw.Occluder;
import net.runelite.injector.raw.RasterizerAlpha;
import net.runelite.injector.raw.RenderDraw;
import net.runelite.injector.raw.ScriptVM;
import net.runelite.mapping.Import;
import net.runelite.rs.api.RSClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.runelite.injector.raw.HidePlayerAttacks;
public class Inject
{
public static final java.lang.Class<?> CLIENT_CLASS = RSClient.class;
public static final String API_PACKAGE_BASE = "net.runelite.rs.api.RS";
public static final String RL_API_PACKAGE_BASE = "net.runelite.api.";
private static final Logger logger = LoggerFactory.getLogger(Inject.class);
private final InjectHookMethod hookMethod = new InjectHookMethod(this);
private final InjectGetter getters = new InjectGetter(this);
private final InjectSetter setters = new InjectSetter(this);
private final InjectInvoker invokes = new InjectInvoker(this);
private final InjectConstruct construct = new InjectConstruct(this);
private final MixinInjector mixinInjector = new MixinInjector(this);
// 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;
}
/**
* Convert a java.lang.Class to a Type
*
* @param c
* @return
*/
public static Type classToType(java.lang.Class<?> c)
{
int dimms = 0;
while (c.isArray())
{
c = c.getComponentType();
++dimms;
}
if (c.isPrimitive())
{
String s;
switch (c.getName())
{
case "int":
s = "I";
break;
case "long":
s = "J";
break;
case "boolean":
s = "Z";
break;
case "char":
s = "C";
break;
case "short":
s = "S";
break;
case "float":
s = "F";
break;
case "double":
s = "D";
break;
case "byte":
s = "B";
break;
case "void":
s = "V";
break;
default:
throw new RuntimeException("unknown primitive type " + c.getName());
}
return Type.getType(s, dimms);
}
return Type.getType("L" + c.getName().replace('.', '/') + ";", dimms);
}
public Signature getMethodSignature(Method m)
{
Signature signature = m.getDescriptor();
Annotation obfSignature = m.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE);
if (obfSignature != null)
{
//Annotation exists. Signature was updated by us during deobfuscation
signature = DeobAnnotations.getObfuscatedSignature(m);
}
return signature;
}
/**
* Build a Signature from a java method
*
* @param method
* @return
*/
public Signature javaMethodToSignature(java.lang.reflect.Method method)
{
Signature.Builder builder = new Signature.Builder()
.setReturnType(classToType(method.getReturnType()));
for (java.lang.Class<?> clazz : method.getParameterTypes())
{
builder.addArgument(classToType(clazz));
}
return builder.build();
}
public void run() throws InjectionException
{
Map<ClassFile, java.lang.Class> implemented = new HashMap<>();
// inject interfaces first, so the validateTypeIsConvertibleTo
// check below works
for (ClassFile cf : deobfuscated.getClasses())
{
Annotations an = cf.getAnnotations();
if (an == null || an.size() == 0)
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
if (obfuscatedName == null)
{
obfuscatedName = cf.getName();
}
ClassFile other = vanilla.findClass(obfuscatedName);
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
java.lang.Class implementingClass = injectInterface(cf, other);
// it can not implement an interface but still have exported static fields, which are
// moved to client
implemented.put(cf, implementingClass);
}
// Has to be done before mixins
// well, can be done after really
// but why do that when you can do it before
new RasterizerAlpha(this).inject();
// requires interfaces to be injected
mixinInjector.inject();
construct.inject(implemented);
for (ClassFile cf : deobfuscated.getClasses())
{
java.lang.Class implementingClass = implemented.get(cf);
Annotations an = cf.getAnnotations();
if (an == null || an.size() == 0)
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
if (obfuscatedName == null)
{
obfuscatedName = cf.getName();
}
ClassFile other = vanilla.findClass(obfuscatedName);
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
for (Field f : cf.getFields())
{
an = f.getAnnotations();
if (an == null || an.find(DeobAnnotations.EXPORT) == null)
{
continue; // not an exported field
}
Annotation exportAnnotation = an.find(DeobAnnotations.EXPORT);
String exportedName = exportAnnotation.getElement().getString();
obfuscatedName = DeobAnnotations.getObfuscatedName(an);
Annotation getterAnnotation = an.find(DeobAnnotations.OBFUSCATED_GETTER);
Number getter = null;
if (getterAnnotation != null)
{
getter = (Number) getterAnnotation.getElement().getValue();
}
// the ob jar is the same as the vanilla so this field must exist in this class.
Type obType = getFieldType(f);
Field otherf = other.findField(obfuscatedName, obType);
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() ? CLIENT_CLASS : implementingClass; // target api class for getter
if (targetApiClass == null)
{
assert !f.isStatic();
// non static field exported on non exported interface
// logger.debug("Non static exported field {} on non exported interface", exportedName);
continue;
}
java.lang.reflect.Method apiMethod = findImportMethodOnApi(targetApiClass, exportedName, true);
if (apiMethod != null)
{
Number setter = null;
if (getter != null)
{
setter = DMath.modInverse(getter); // inverse getter to get the setter
}
setters.injectSetter(targetClass, targetApiClass, otherf, exportedName, setter);
}
apiMethod = findImportMethodOnApi(targetApiClass, exportedName, false);
if (apiMethod == null)
{
//logger.debug("Unable to find import method on api class {} with imported name {}, not injecting getter", targetApiClass, exportedName);
continue;
}
// check that otherf is converable to apiMethod's
// return type
Type fieldType = otherf.getType();
Type returnType = classToType(apiMethod.getReturnType());
if (!validateTypeIsConvertibleTo(fieldType, returnType))
{
throw new InjectionException("Type " + fieldType + " is not convertable to " + returnType + " for getter " + apiMethod);
}
getters.injectGetter(targetClass, apiMethod, otherf, getter);
}
for (Method m : cf.getMethods())
{
hookMethod.process(m);
invokes.process(m, other, implementingClass);
}
}
logger.info("Injected {} getters, {} setters, {} invokers",
getters.getInjectedGetters(),
setters.getInjectedSetters(), invokes.getInjectedInvokers());
new DrawAfterWidgets(this).inject();
new ScriptVM(this).inject();
new ClearColorBuffer(this).inject();
new RenderDraw(this).inject();
// new DrawMenu(this).inject();
new Occluder(this).inject();
new HidePlayerAttacks(this).inject();
}
private java.lang.Class injectInterface(ClassFile cf, ClassFile other)
{
Annotations an = cf.getAnnotations();
if (an == null)
{
return null;
}
Annotation a = an.find(DeobAnnotations.IMPLEMENTS);
if (a == null)
{
return null;
}
String ifaceName = API_PACKAGE_BASE + a.getElement().getString();
java.lang.Class<?> apiClass;
try
{
apiClass = java.lang.Class.forName(ifaceName);
}
catch (ClassNotFoundException ex)
{
logger.trace("Class {} implements nonexistent interface {}, skipping interface injection",
cf.getName(),
ifaceName);
return null;
}
String ifaceNameInternal = ifaceName.replace('.', '/'); // to internal name
Class clazz = new Class(ifaceNameInternal);
Interfaces interfaces = other.getInterfaces();
interfaces.addInterface(clazz);
return apiClass;
}
public java.lang.reflect.Method findImportMethodOnApi(java.lang.Class<?> clazz, String name, Boolean setter)
{
for (java.lang.reflect.Method method : clazz.getDeclaredMethods())
{
if (method.isSynthetic())
{
/*
* If you override an interface method in another interface
* with a return type that is a child of the overriden methods
* return type, both methods end up in the interface, and both
* are *annotated*. But the base one is synthetic.
*/
continue;
}
Import i = method.getAnnotation(Import.class);
if (i == null || !name.equals(i.value()) || (setter != null && (method.getParameterCount() > 0) != setter))
{
continue;
}
return method;
}
return null;
}
/**
* create a load instruction for a variable of type from a given index
*
* @param instructions
* @param type
* @param index
* @return
*/
public Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index)
{
if (type.getDimensions() > 0 || !type.isPrimitive())
{
return new ALoad(instructions, index);
}
switch (type.toString())
{
case "B":
case "C":
case "I":
case "S":
case "Z":
return new ILoad(instructions, index);
case "D":
return new DLoad(instructions, index);
case "F":
return new FLoad(instructions, index);
case "J":
return new LLoad(instructions, index);
default:
throw new RuntimeException("Unknown type");
}
}
ClassFile toDeobClass(ClassFile obClass)
{
for (ClassFile cf : deobfuscated.getClasses())
{
String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
if (obClass.getName().equalsIgnoreCase(obfuscatedName))
{
return cf;
}
}
return null;
}
Type deobfuscatedTypeToApiType(Type type) throws InjectionException
{
if (type.isPrimitive())
{
return type;
}
ClassFile cf = deobfuscated.findClass(type.getInternalName());
if (cf == null)
{
return type; // not my type
}
java.lang.Class<?> rsApiType;
try
{
rsApiType = java.lang.Class.forName(API_PACKAGE_BASE + cf.getName().replace("/", "."));
}
catch (ClassNotFoundException ex)
{
throw new InjectionException("Deobfuscated type " + type.getInternalName() + " has no API type", ex);
}
java.lang.Class<?> rlApiType = null;
for (java.lang.Class<?> inter : rsApiType.getInterfaces())
{
if (inter.getName().startsWith(RL_API_PACKAGE_BASE))
{
rlApiType = inter;
}
}
// if (rlApiType == null)
// {
// throw new InjectionException("RS API type " + rsApiType + " does not extend RL API interface");
// }
final java.lang.Class<?> finalType = rlApiType == null ? rsApiType : rlApiType;
return Type.getType("L" + finalType.getName().replace('.', '/') + ";", type.getDimensions());
}
Type apiTypeToDeobfuscatedType(Type type)
{
if (type.isPrimitive())
{
return type;
}
String internalName = type.getInternalName().replace('/', '.');
if (!internalName.startsWith(API_PACKAGE_BASE))
{
return type; // not an rs api type
}
return Type.getType("L" + type.getInternalName().substring(API_PACKAGE_BASE.length()) + ";", type.getDimensions());
}
ClassFile findVanillaForInterface(java.lang.Class<?> clazz)
{
String className = clazz.getName().replace('.', '/');
for (ClassFile cf : getVanilla().getClasses())
{
for (net.runelite.asm.pool.Class cl : cf.getInterfaces().getInterfaces())
{
if (cl.getName().equals(className))
{
return cf;
}
}
}
return null;
}
private boolean validateTypeIsConvertibleTo(Type from, Type to) throws InjectionException
{
if (from.getDimensions() != to.getDimensions())
{
throw new InjectionException("Array dimension mismatch");
}
if (from.isPrimitive())
{
return true;
}
ClassFile vanillaClass = vanilla.findClass(from.getInternalName());
if (vanillaClass == null)
{
return true;
}
boolean okay = false;
for (Class inter : vanillaClass.getInterfaces().getInterfaces())
{
java.lang.Class c;
try
{
c = java.lang.Class.forName(inter.getName().replace('/', '.'));
}
catch (ClassNotFoundException ex)
{
continue;
}
okay |= check(c, to);
}
return okay;
}
private boolean check(java.lang.Class c, Type type)
{
String s = type.getInternalName()
.replace('/', '.');
if (c.getName().equals(s))
{
return true;
}
for (java.lang.Class c2 : c.getInterfaces())
{
if (check(c2, type))
{
return true;
}
}
return false;
}
public final ClassGroup getDeobfuscated()
{
return deobfuscated;
}
public final ClassGroup getVanilla()
{
return vanilla;
}
}

View File

@@ -1,171 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.CheckCast;
import net.runelite.asm.attributes.code.instructions.Dup;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.New;
import net.runelite.asm.attributes.code.instructions.Return;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import net.runelite.mapping.Construct;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InjectConstruct
{
private static final Logger logger = LoggerFactory.getLogger(InjectConstruct.class);
private final Inject inject;
InjectConstruct(Inject inject)
{
this.inject = inject;
}
public void inject(Map<ClassFile, java.lang.Class> implemented) throws InjectionException
{
for (Entry<ClassFile, java.lang.Class> entry : implemented.entrySet())
{
Class<?> clazz = entry.getValue();
ClassFile cf = entry.getKey();
if (clazz == null)
{
continue;
}
for (java.lang.reflect.Method method : clazz.getDeclaredMethods())
{
if (method.isSynthetic())
{
continue;
}
Construct construct = method.getAnnotation(Construct.class);
if (construct == null)
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
if (obfuscatedName == null)
{
obfuscatedName = cf.getName();
}
ClassGroup vanilla = inject.getVanilla();
ClassFile other = vanilla.findClass(obfuscatedName);
assert other != null : "unable to find vanilla class from obfuscated name: " + obfuscatedName;
injectConstruct(other, method);
}
}
}
void injectConstruct(ClassFile targetClass, java.lang.reflect.Method apiMethod) throws InjectionException
{
logger.info("Injecting construct for {}", apiMethod);
assert targetClass.findMethod(apiMethod.getName()) == null;
Class<?> typeToConstruct = apiMethod.getReturnType();
ClassFile vanillaClass = inject.findVanillaForInterface(typeToConstruct);
if (vanillaClass == null)
{
throw new InjectionException("Unable to find vanilla class which implements interface " + typeToConstruct);
}
Signature sig = inject.javaMethodToSignature(apiMethod);
Signature constructorSig = new Signature.Builder()
.addArguments(Stream.of(apiMethod.getParameterTypes())
.map(arg ->
{
ClassFile vanilla = inject.findVanillaForInterface(arg);
if (vanilla != null)
{
return new Type("L" + vanilla.getName() + ";");
}
return Inject.classToType(arg);
})
.collect(Collectors.toList()))
.setReturnType(Type.VOID)
.build();
Method vanillaConstructor = vanillaClass.findMethod("<init>", constructorSig);
if (vanillaConstructor == null)
{
throw new InjectionException("Unable to find constructor for " + vanillaClass.getName() + ".<init>" + constructorSig);
}
Method setterMethod = new Method(targetClass, apiMethod.getName(), sig);
setterMethod.setAccessFlags(ACC_PUBLIC);
targetClass.addMethod(setterMethod);
Code code = new Code(setterMethod);
setterMethod.setCode(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = instructions.getInstructions();
ins.add(new New(instructions, vanillaClass.getPoolClass()));
ins.add(new Dup(instructions));
int idx = 1;
int parameter = 0;
for (Type type : vanillaConstructor.getDescriptor().getArguments())
{
Instruction load = inject.createLoadForTypeIndex(instructions, type, idx);
idx += type.getSize();
ins.add(load);
Type paramType = sig.getTypeOfArg(parameter);
if (!type.equals(paramType))
{
CheckCast checkCast = new CheckCast(instructions);
checkCast.setType(type);
ins.add(checkCast);
}
++parameter;
}
ins.add(new InvokeSpecial(instructions, vanillaConstructor.getPoolMethod()));
ins.add(new Return(instructions));
}
}

View File

@@ -1,155 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.List;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
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.ALoad;
import net.runelite.asm.attributes.code.instructions.GetField;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.LMul;
import net.runelite.asm.attributes.code.instructions.Return;
import net.runelite.asm.signature.Signature;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class InjectGetter
{
private static final Logger logger = LoggerFactory.getLogger(InjectGetter.class);
private final Inject inject;
private int injectedGetters;
InjectGetter(Inject inject)
{
this.inject = inject;
}
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.getClassFile() == clazz;
Signature sig = new Signature.Builder()
.setReturnType(Inject.classToType(method.getReturnType()))
.build();
Method getterMethod = new Method(clazz, method.getName(), sig);
getterMethod.setAccessFlags(ACC_PUBLIC);
// create code
Code code = new Code(getterMethod);
getterMethod.setCode(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = instructions.getInstructions();
if (field.isStatic())
{
code.setMaxStack(1);
ins.add(new GetStatic(instructions, field.getPoolField()));
}
else
{
code.setMaxStack(2);
ins.add(new ALoad(instructions, 0));
ins.add(new GetField(instructions, field.getPoolField()));
}
if (getter != null)
{
code.setMaxStack(2);
assert getter instanceof Integer || getter instanceof Long;
if (getter instanceof Integer)
{
ins.add(new LDC(instructions, (int) getter));
ins.add(new IMul(instructions));
}
else
{
ins.add(new LDC(instructions, (long) getter));
ins.add(new LMul(instructions));
}
}
InstructionType returnType;
if (field.getType().isPrimitive() && field.getType().getDimensions() == 0)
{
switch (field.getType().toString())
{
case "B":
case "C":
case "I":
case "S":
case "Z":
returnType = InstructionType.IRETURN;
break;
case "D":
returnType = InstructionType.DRETURN;
break;
case "F":
returnType = InstructionType.FRETURN;
break;
case "J":
returnType = InstructionType.LRETURN;
break;
default:
throw new RuntimeException("Unknown type");
}
}
else
{
returnType = InstructionType.ARETURN;
}
ins.add(new Return(instructions, returnType));
clazz.addMethod(getterMethod);
++injectedGetters;
}
int getInjectedGetters()
{
return injectedGetters;
}
}

View File

@@ -1,396 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import com.google.common.collect.Lists;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
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.instruction.types.DupInstruction;
import net.runelite.asm.attributes.code.instruction.types.SetFieldInstruction;
import net.runelite.asm.attributes.code.instructions.ArrayStore;
import net.runelite.asm.attributes.code.instructions.CheckCast;
import net.runelite.asm.attributes.code.instructions.Dup;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.LMul;
import net.runelite.asm.attributes.code.instructions.PutField;
import net.runelite.asm.attributes.code.instructions.Swap;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.asm.signature.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class InjectHook
{
private static final Logger logger = LoggerFactory.getLogger(InjectHook.class);
private static final String HOOK_METHOD_SIGNATURE = "(I)V";
private static final String CLINIT = "<clinit>";
private final Inject inject;
private final Map<Field, HookInfo> hooked = new HashMap<>();
private int injectedHooks;
InjectHook(Inject inject)
{
this.inject = inject;
}
void hook(Field field, HookInfo hookInfo)
{
hooked.put(field, hookInfo);
}
void run()
{
Execution e = new Execution(inject.getVanilla());
e.populateInitialMethods();
Set<Instruction> done = new HashSet<>();
Set<Instruction> doneIh = new HashSet<>();
e.addExecutionVisitor((InstructionContext ic) ->
{
Instruction i = ic.getInstruction();
Instructions ins = i.getInstructions();
Code code = ins.getCode();
Method method = code.getMethod();
if (method.getName().equals(CLINIT))
{
return;
}
if (!(i instanceof SetFieldInstruction))
{
return;
}
if (!done.add(i))
{
return;
}
SetFieldInstruction sfi = (SetFieldInstruction) i;
Field fieldBeingSet = sfi.getMyField();
if (fieldBeingSet == null)
{
return;
}
HookInfo hookInfo = hooked.get(fieldBeingSet);
if (hookInfo == null)
{
return;
}
String hookName = hookInfo.fieldName;
assert hookName != null;
logger.trace("Found injection location for hook {} at instruction {}", hookName, sfi);
++injectedHooks;
StackContext value = ic.getPops().get(0);
StackContext objectStackContext = null;
if (sfi instanceof PutField)
{
objectStackContext = ic.getPops().get(1);
}
int idx = ins.getInstructions().indexOf(sfi);
assert idx != -1;
try
{
if (hookInfo.before)
{
injectCallbackBefore(ins, idx, hookInfo, null, objectStackContext, value);
}
else
{
// idx + 1 to insert after the set
injectCallback(ins, idx + 1, hookInfo, null, objectStackContext);
}
}
catch (InjectionException ex)
{
throw new RuntimeException(ex);
}
});
// these look like:
// getfield
// iload_0
// iconst_0
// iastore
e.addExecutionVisitor((InstructionContext ic) ->
{
Instruction i = ic.getInstruction();
Instructions ins = i.getInstructions();
Code code = ins.getCode();
Method method = code.getMethod();
if (method.getName().equals(CLINIT))
{
return;
}
if (!(i instanceof ArrayStore))
{
return;
}
if (!doneIh.add(i))
{
return;
}
ArrayStore as = (ArrayStore) i;
Field fieldBeingSet = as.getMyField(ic);
if (fieldBeingSet == null)
{
return;
}
HookInfo hookInfo = hooked.get(fieldBeingSet);
if (hookInfo == null)
{
return;
}
String hookName = hookInfo.fieldName;
StackContext value = ic.getPops().get(0);
StackContext index = ic.getPops().get(1);
StackContext arrayReference = ic.getPops().get(2);
InstructionContext arrayReferencePushed = arrayReference.getPushed();
StackContext objectStackContext = null;
if (arrayReferencePushed.getInstruction().getType() == InstructionType.GETFIELD)
{
objectStackContext = arrayReferencePushed.getPops().get(0);
}
// inject hook after 'i'
logger.info("Found array injection location for hook {} at instruction {}", hookName, i);
++injectedHooks;
int idx = ins.getInstructions().indexOf(i);
assert idx != -1;
try
{
if (hookInfo.before)
{
injectCallbackBefore(ins, idx, hookInfo, index, objectStackContext, value);
}
else
{
injectCallback(ins, idx + 1, hookInfo, index, objectStackContext);
}
}
catch (InjectionException ex)
{
throw new RuntimeException(ex);
}
});
e.run();
}
private void injectCallbackBefore(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext object, StackContext value) throws InjectionException
{
Signature signature = hookInfo.method.getDescriptor();
Type methodArgumentType = signature.getTypeOfArg(0);
if (!hookInfo.method.isStatic())
{
if (object == null)
{
throw new InjectionException("null object");
}
ins.getInstructions().add(idx++, new Dup(ins)); // dup value
idx = recursivelyPush(ins, idx, object);
ins.getInstructions().add(idx++, new Swap(ins));
if (hookInfo.getter != null)
{
assert hookInfo.getter instanceof Integer || hookInfo.getter instanceof Long;
if (hookInfo.getter instanceof Integer)
{
ins.getInstructions().add(idx++, new LDC(ins, (int) hookInfo.getter));
ins.getInstructions().add(idx++, new IMul(ins));
}
else
{
ins.getInstructions().add(idx++, new LDC(ins, (long) hookInfo.getter));
ins.getInstructions().add(idx++, new LMul(ins));
}
}
if (!value.type.equals(methodArgumentType))
{
CheckCast checkCast = new CheckCast(ins);
checkCast.setType(methodArgumentType);
ins.getInstructions().add(idx++, checkCast);
}
if (index != null)
{
idx = recursivelyPush(ins, idx, index);
}
InvokeVirtual invoke = new InvokeVirtual(ins,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(hookInfo.clazz),
hookInfo.method.getName(),
signature
)
);
ins.getInstructions().add(idx++, invoke);
}
else
{
ins.getInstructions().add(idx++, new Dup(ins)); // dup value
if (!value.type.equals(methodArgumentType))
{
CheckCast checkCast = new CheckCast(ins);
checkCast.setType(methodArgumentType);
ins.getInstructions().add(idx++, checkCast);
}
if (index != null)
{
idx = recursivelyPush(ins, idx, index);
}
InvokeStatic invoke = new InvokeStatic(ins,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(hookInfo.clazz),
hookInfo.method.getName(),
signature
)
);
ins.getInstructions().add(idx++, invoke);
}
}
private int recursivelyPush(Instructions ins, int idx, StackContext sctx)
{
InstructionContext ctx = sctx.getPushed();
if (ctx.getInstruction() instanceof DupInstruction)
{
DupInstruction dupInstruction = (DupInstruction) ctx.getInstruction();
sctx = dupInstruction.getOriginal(sctx);
ctx = sctx.getPushed();
}
for (StackContext s : Lists.reverse(ctx.getPops()))
{
idx = recursivelyPush(ins, idx, s);
}
ins.getInstructions().add(idx++, ctx.getInstruction().clone());
return idx;
}
private void injectCallback(Instructions ins, int idx, HookInfo hookInfo, StackContext index, StackContext objectPusher) throws InjectionException
{
if (!hookInfo.method.isStatic())
{
if (objectPusher == null)
{
throw new InjectionException("Null object pusher");
}
idx = recursivelyPush(ins, idx, objectPusher);
if (index != null)
{
idx = recursivelyPush(ins, idx, index);
}
else
{
ins.getInstructions().add(idx++, new LDC(ins, -1));
}
InvokeVirtual invoke = new InvokeVirtual(ins,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(hookInfo.clazz),
hookInfo.method.getName(),
new Signature(HOOK_METHOD_SIGNATURE)
)
);
ins.getInstructions().add(idx++, invoke);
}
else
{
if (index != null)
{
idx = recursivelyPush(ins, idx, index);
}
else
{
ins.getInstructions().add(idx++, new LDC(ins, -1));
}
InvokeStatic invoke = new InvokeStatic(ins,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(hookInfo.clazz),
hookInfo.method.getName(),
new Signature(HOOK_METHOD_SIGNATURE)
)
);
ins.getInstructions().add(idx++, invoke);
}
}
int getInjectedHooks()
{
return injectedHooks;
}
static class HookInfo
{
String fieldName;
String clazz;
Method method;
boolean before;
Number getter;
}
}

View File

@@ -1,255 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.Code;
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.instruction.types.InvokeInstruction;
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class InjectHookMethod
{
public static final String HOOKS = "net/runelite/client/callback/Hooks";
private static final Logger logger = LoggerFactory.getLogger(InjectHookMethod.class);
private final Inject inject;
InjectHookMethod(Inject inject)
{
this.inject = inject;
}
void process(Method method) throws InjectionException
{
Annotations an = method.getAnnotations();
if (an == null)
{
return;
}
Annotation a = an.find(DeobAnnotations.HOOK);
if (a == null)
{
return;
}
String hookName = a.getElement().getString();
boolean end = a.getElements().size() == 2 && a.getElements().get(1).getValue().equals(true);
inject(null, method, hookName, end, true);
}
public void inject(Method hookMethod, Method method, String name, boolean end, boolean useHooks) throws InjectionException
{
Annotations an = method.getAnnotations();
// Method is hooked
// Find equivalent method in vanilla, and insert callback at the beginning
ClassFile cf = method.getClassFile();
String obfuscatedMethodName = DeobAnnotations.getObfuscatedName(an),
obfuscatedClassName = DeobAnnotations.getObfuscatedName(cf.getAnnotations());
// might be a constructor
if (obfuscatedMethodName == null)
{
obfuscatedMethodName = method.getName();
}
assert obfuscatedClassName != null : "hook on method in class with no obfuscated name";
assert obfuscatedMethodName != null : "hook on method with no obfuscated name";
Signature obfuscatedSignature = inject.getMethodSignature(method);
ClassGroup vanilla = inject.getVanilla();
ClassFile vanillaClass = vanilla.findClass(obfuscatedClassName);
Method vanillaMethod = vanillaClass.findMethod(obfuscatedMethodName, obfuscatedSignature);
assert method.isStatic() == vanillaMethod.isStatic();
// Insert instructions at beginning of method
injectHookMethod(hookMethod, name, end, method, vanillaMethod, useHooks);
}
private void injectHookMethod(Method hookMethod, String hookName, boolean end, Method deobMethod, Method vanillaMethod, boolean useHooks) throws InjectionException
{
Code code = vanillaMethod.getCode();
if (code == null)
{
logger.warn(vanillaMethod + " code is null");
}
Instructions instructions = code.getInstructions();
Signature.Builder builder = new Signature.Builder()
.setReturnType(Type.VOID); // Hooks always return void
for (Type type : deobMethod.getDescriptor().getArguments())
{
builder.addArgument(inject.deobfuscatedTypeToApiType(type));
}
assert deobMethod.isStatic() == vanillaMethod.isStatic();
boolean modifiedSignature = false;
if (!deobMethod.isStatic() && useHooks)
{
// Add variable to signature
builder.addArgument(0, inject.deobfuscatedTypeToApiType(new Type(deobMethod.getClassFile().getName())));
modifiedSignature = true;
}
Signature signature = builder.build();
List<Integer> insertIndexes = findHookLocations(hookName, end, vanillaMethod);
insertIndexes.sort((a, b) -> Integer.compare(b, a));
for (int insertPos : insertIndexes)
{
if (!deobMethod.isStatic())
{
instructions.addInstruction(insertPos++, new ALoad(instructions, 0));
}
int signatureStart = modifiedSignature ? 1 : 0;
int index = deobMethod.isStatic() ? 0 : 1; // current variable index
for (int i = signatureStart; i < signature.size(); ++i)
{
Type type = signature.getTypeOfArg(i);
Instruction load = inject.createLoadForTypeIndex(instructions, type, index);
instructions.addInstruction(insertPos++, load);
index += type.getSize();
}
InvokeInstruction invoke;
// use old Hooks callback
if (useHooks)
{
// Invoke callback
invoke = new InvokeStatic(instructions,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(HOOKS),
hookName,
signature
)
);
}
else
{
// Invoke methodhook
assert hookMethod != null;
if (vanillaMethod.isStatic())
{
invoke = new InvokeStatic(instructions,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class("client"), // Static methods are in client
hookMethod.getName(),
signature
)
);
}
else
{
// otherwise invoke member function
//instructions.addInstruction(insertPos++, new ALoad(instructions, 0));
invoke = new InvokeVirtual(instructions,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(vanillaMethod.getClassFile().getName()),
hookMethod.getName(),
hookMethod.getDescriptor()
)
);
}
}
instructions.addInstruction(insertPos++, (Instruction) invoke);
}
logger.info("Injected method hook {} in {} with {} args: {}",
hookName, vanillaMethod, signature.size(),
signature.getArguments());
}
private List<Integer> findHookLocations(String hookName, boolean end, Method vanillaMethod) throws InjectionException
{
Instructions instructions = vanillaMethod.getCode().getInstructions();
if (end)
{
// find return
List<Instruction> returns = instructions.getInstructions().stream()
.filter(i -> i instanceof ReturnInstruction)
.collect(Collectors.toList());
List<Integer> indexes = new ArrayList<>();
for (Instruction ret : returns)
{
int idx = instructions.getInstructions().indexOf(ret);
assert idx != -1;
indexes.add(idx);
}
return indexes;
}
if (!vanillaMethod.getName().equals("<init>"))
{
return Arrays.asList(0);
}
// Find index after invokespecial
for (int i = 0; i < instructions.getInstructions().size(); ++i)
{
Instruction in = instructions.getInstructions().get(i);
if (in.getType() == InstructionType.INVOKESPECIAL)
{
return Arrays.asList(i + 1); // one after
}
}
throw new IllegalStateException("constructor with no invokespecial");
}
}

View File

@@ -1,291 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.List;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Annotations;
import net.runelite.asm.attributes.Code;
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.ALoad;
import net.runelite.asm.attributes.code.instructions.BiPush;
import net.runelite.asm.attributes.code.instructions.CheckCast;
import net.runelite.asm.attributes.code.instructions.DLoad;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.LLoad;
import net.runelite.asm.attributes.code.instructions.Return;
import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
import static net.runelite.deob.DeobAnnotations.EXPORT;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class InjectInvoker
{
private static final Logger logger = LoggerFactory.getLogger(InjectInvoker.class);
private final Inject inject;
private int injectedInvokers;
InjectInvoker(Inject inject)
{
this.inject = inject;
}
/**
* Inject an invoker for a method
*
* @param m Method in the deobfuscated client to inject an invoker for
* @param other Class in the vanilla client of the same class m is a
* member of
* @param implementingClass Java class for the API interface the class
* will implement
*/
void process(Method m, ClassFile other, java.lang.Class<?> implementingClass)
{
Annotations an = m.getAnnotations();
if (an == null || an.find(EXPORT) == null)
{
return; // not an exported method
}
String exportedName = DeobAnnotations.getExportedName(an);
String obfuscatedName = DeobAnnotations.getObfuscatedName(an);
if (obfuscatedName == null)
{
obfuscatedName = m.getName();
}
String garbage = DeobAnnotations.getDecoder(m);
Method otherm = other.findMethod(obfuscatedName, inject.getMethodSignature(m));
assert otherm != null;
assert m.isStatic() == otherm.isStatic();
ClassGroup vanilla = inject.getVanilla();
ClassFile targetClass = m.isStatic() ? vanilla.findClass("client") : other;
// Place into implementing class, unless the method is static
java.lang.Class<?> targetClassJava = m.isStatic() ? Inject.CLIENT_CLASS : implementingClass;
if (targetClassJava == null)
{
assert !m.isStatic();
// non static exported method on non exported interface, weird.
// logger.debug("Non static exported method {} on non exported interface", exportedName);
return;
}
java.lang.reflect.Method apiMethod = inject.findImportMethodOnApi(targetClassJava, exportedName, null); // api method to invoke 'otherm'
if (apiMethod == null)
{
// logger.debug("Unable to find api method on {} with imported name {}, not injecting invoker", targetClassJava, exportedName);
return;
}
injectInvoker(targetClass, apiMethod, m, otherm, garbage);
++injectedInvokers;
}
private void injectInvoker(ClassFile clazz, java.lang.reflect.Method method, Method deobfuscatedMethod, Method invokeMethod, String garbage)
{
// clazz = clazz to add invoker to
// method = api method to override
// deobfuscatedMethod = deobfuscated method, used to get the deobfuscated signature
// invokeMethod = method to invoke, obfuscated
if (clazz.findMethod(method.getName(), deobfuscatedMethod.getDescriptor()) != null)
{
logger.warn("Not injecting method {} because it already exists!", method);
return; // this can happen from exporting a field and method with the same name
}
assert invokeMethod.isStatic() == deobfuscatedMethod.isStatic();
assert invokeMethod.isStatic() || invokeMethod.getClassFile() == clazz;
Type lastGarbageArgumentType = null;
if (deobfuscatedMethod.getDescriptor().getArguments().size() != invokeMethod.getDescriptor().getArguments().size())
{
// allow for obfuscated method to have a single bogus signature at the end
assert deobfuscatedMethod.getDescriptor().size() + 1 == invokeMethod.getDescriptor().size();
List<Type> arguments = invokeMethod.getDescriptor().getArguments();
lastGarbageArgumentType = arguments.get(arguments.size() - 1);
}
// Injected method signature is always the same as the API
Signature apiSignature = inject.javaMethodToSignature(method);
Method invokerMethodSignature = new Method(clazz, method.getName(), apiSignature);
invokerMethodSignature.setAccessFlags(ACC_PUBLIC);
// create code attribute
Code code = new Code(invokerMethodSignature);
invokerMethodSignature.setCode(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = instructions.getInstructions();
code.setMaxStack(1 + invokeMethod.getDescriptor().size()); // this + arguments
// load function arguments onto the stack.
int index = 0;
if (!invokeMethod.isStatic())
{
ins.add(new ALoad(instructions, index++)); // this
}
else
{
++index; // this method is always non static
}
for (int i = 0; i < deobfuscatedMethod.getDescriptor().size(); ++i)
{
Type type = deobfuscatedMethod.getDescriptor().getTypeOfArg(i);
Instruction loadInstruction = inject.createLoadForTypeIndex(instructions, type, index);
ins.add(loadInstruction);
Signature invokeDesc = invokeMethod.getDescriptor();
Type obType = invokeDesc.getTypeOfArg(i);
if (!type.equals(obType))
{
CheckCast checkCast = new CheckCast(instructions);
checkCast.setType(obType);
ins.add(checkCast);
}
if (loadInstruction instanceof DLoad || loadInstruction instanceof LLoad)
{
index += 2;
}
else
{
index += 1;
}
}
if (lastGarbageArgumentType != null)
{
// 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.toString())
{
case "Z":
case "B":
case "C":
ins.add(new BiPush(instructions, Byte.parseByte(garbage)));
break;
case "S":
ins.add(new SiPush(instructions, Short.parseShort(garbage)));
break;
case "I":
ins.add(new LDC(instructions, Integer.parseInt(garbage)));
break;
case "D":
ins.add(new LDC(instructions, Double.parseDouble(garbage)));
break;
case "F":
ins.add(new LDC(instructions, Float.parseFloat(garbage)));
break;
case "J":
ins.add(new LDC(instructions, Long.parseLong(garbage)));
break;
default:
throw new RuntimeException("Unknown type");
}
}
if (invokeMethod.isStatic())
{
ins.add(new InvokeStatic(instructions, invokeMethod.getPoolMethod()));
}
else
{
ins.add(new InvokeVirtual(instructions, invokeMethod.getPoolMethod()));
}
Type returnValue = invokeMethod.getDescriptor().getReturnValue();
InstructionType returnType;
if (returnValue.isPrimitive() && returnValue.getDimensions() == 0)
{
switch (returnValue.toString())
{
case "Z":
case "I":
returnType = InstructionType.IRETURN;
break;
case "J":
returnType = InstructionType.LRETURN;
break;
case "F":
returnType = InstructionType.FRETURN;
break;
case "D":
returnType = InstructionType.DRETURN;
break;
case "V":
returnType = InstructionType.RETURN;
break;
default:
assert false;
return;
}
}
else
{
returnType = InstructionType.ARETURN;
}
ins.add(new Return(instructions, returnType));
clazz.addMethod(invokerMethodSignature);
}
int getInjectedInvokers()
{
return injectedInvokers;
}
}

View File

@@ -1,152 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.clientver.ClientVersion;
import net.runelite.deob.util.JarUtil;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
@Mojo(
name = "runelite-injector",
defaultPhase = LifecyclePhase.GENERATE_RESOURCES
)
public class InjectMojo extends AbstractMojo
{
private final Log log = getLog();
@Parameter(defaultValue = "${project.build.outputDirectory}")
private File outputDirectory;
@Parameter(defaultValue = "./runescape-client/target/rs-client-${project.version}.jar", readonly = true, required = true)
private String rsClientPath;
@Parameter(defaultValue = "${net.runelite.rs:vanilla:jar}", readonly = true, required = true)
private String vanillaPath;
@Override
public void execute() throws MojoExecutionException, MojoFailureException
{
ClientVersion ver = new ClientVersion(new File(vanillaPath));
int version;
try
{
version = ver.getVersion();
}
catch (IOException ex)
{
throw new MojoExecutionException("Unable to read vanilla client version", ex);
}
log.info("Vanilla client version " + version);
ClassGroup rs;
ClassGroup vanilla;
try
{
rs = JarUtil.loadJar(new File(rsClientPath));
vanilla = JarUtil.loadJar(new File(vanillaPath));
}
catch (IOException ex)
{
throw new MojoExecutionException("Unable to load dependency jars", ex);
}
Injector injector = new Injector(rs, vanilla);
try
{
injector.inject();
}
catch (InjectionException ex)
{
throw new MojoExecutionException("Error injecting client", ex);
}
InjectorValidator iv = new InjectorValidator(vanilla);
iv.validate();
if (iv.getError() > 0)
{
throw new MojoExecutionException("Error building injected jar");
}
if (iv.getMissing() > 0)
{
throw new MojoExecutionException("Unable to inject all methods");
}
try
{
writeClasses(vanilla, outputDirectory);
}
catch (IOException ex)
{
throw new MojoExecutionException("Unable to write classes", ex);
}
log.info("Injector wrote " + vanilla.getClasses().size() + " classes, " + iv.getOkay() + " injected methods");
}
private void writeClasses(ClassGroup group, File outputDirectory) throws IOException
{
for (ClassFile cf : group.getClasses())
{
File classFile = getClassFile(outputDirectory, cf);
byte[] classData = JarUtil.writeClass(group, cf);
try (FileOutputStream fout = new FileOutputStream(classFile, false))
{
fout.write(classData);
}
}
}
private File getClassFile(File base, ClassFile cf)
{
File f = base;
String[] parts = cf.getName().split("/");
for (int i = 0; i < parts.length - 1; ++i)
{
String part = parts[i];
f = new File(f, part);
}
f.mkdirs();
f = new File(f, parts[parts.length - 1] + ".class");
return f;
}
}

View File

@@ -1,158 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.util.List;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.CheckCast;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.LMul;
import net.runelite.asm.attributes.code.instructions.PutField;
import net.runelite.asm.attributes.code.instructions.PutStatic;
import net.runelite.asm.attributes.code.instructions.VReturn;
import net.runelite.asm.signature.Signature;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
class InjectSetter
{
private static final Logger logger = LoggerFactory.getLogger(InjectSetter.class);
private final Inject inject;
private int injectedSetters;
InjectSetter(Inject inject)
{
this.inject = inject;
}
/**
* inject a setter into the vanilla classgroup
*
* @param targetClass Class where to inject the setter (field's class,
* or client)
* @param targetApiClass API targetClass implements, which may have the
* setter declared
* @param field Field of vanilla that will be set
* @param exportedName exported name of field
*/
void injectSetter(ClassFile targetClass, Class<?> targetApiClass, Field field, String exportedName, Number setter)
{
java.lang.reflect.Method method = inject.findImportMethodOnApi(targetApiClass, exportedName, true);
if (method == null)
{
logger.warn("Setter injection for field {} but an API method was not found on {}", exportedName, targetApiClass);
return;
}
if (method.getParameterCount() != 1)
{
logger.warn("Setter {} with not parameter count != 1?", exportedName);
return;
}
logger.info("Injecting setter for {} on {}", exportedName, targetApiClass);
assert targetClass.findMethod(method.getName()) == null;
assert field.isStatic() || field.getClassFile() == targetClass;
Signature sig = new Signature.Builder()
.setReturnType(Type.VOID)
.addArgument(Inject.classToType(method.getParameterTypes()[0]))
.build();
Method setterMethod = new Method(targetClass, method.getName(), sig);
setterMethod.setAccessFlags(ACC_PUBLIC);
targetClass.addMethod(setterMethod);
++injectedSetters;
Code code = new Code(setterMethod);
setterMethod.setCode(code);
Instructions instructions = code.getInstructions();
List<Instruction> ins = instructions.getInstructions();
// load this
if (!field.isStatic())
{
ins.add(new ALoad(instructions, 0));
}
// load argument
Type argumentType = sig.getTypeOfArg(0);
ins.add(inject.createLoadForTypeIndex(instructions, argumentType, 1));
// cast argument to field type
Type fieldType = field.getType();
if (!argumentType.equals(fieldType))
{
CheckCast checkCast = new CheckCast(instructions);
checkCast.setType(fieldType);
ins.add(checkCast);
}
if (setter != null)
{
assert setter instanceof Integer || setter instanceof Long;
if (setter instanceof Integer)
{
ins.add(new LDC(instructions, (int) setter));
ins.add(new IMul(instructions));
}
else
{
ins.add(new LDC(instructions, (long) setter));
ins.add(new LMul(instructions));
}
}
if (field.isStatic())
{
ins.add(new PutStatic(instructions, field));
}
else
{
ins.add(new PutField(instructions, field));
}
ins.add(new VReturn(instructions));
}
int getInjectedSetters()
{
return injectedSetters;
}
}

View File

@@ -1,281 +0,0 @@
package net.runelite.injector;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.signature.Signature;
import net.runelite.deob.DeobAnnotations;
public class InjectUtil
{
public static ClassFile toObClass(final ClassGroup vanilla, final ClassFile deobCf) throws InjectionException
{
final String obfuscatedName = DeobAnnotations.getObfuscatedName(deobCf.getAnnotations());
final ClassFile obCf = vanilla.findClass(obfuscatedName);
if (obCf == null)
{
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedName));
}
return obCf;
}
public static Field toObField(final ClassGroup vanilla, final Field field) throws InjectionException
{
String obfuscatedClassName = DeobAnnotations.getObfuscatedName(field.getClassFile().getAnnotations());
String obfuscatedFieldName = DeobAnnotations.getObfuscatedName(field.getAnnotations()); // obfuscated name of field
Type type = getFieldType(field);
ClassFile obfuscatedClass = vanilla.findClass(obfuscatedClassName);
if (obfuscatedClass == null)
{
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", obfuscatedClassName));
}
Field obfuscatedField = obfuscatedClass.findFieldDeep(obfuscatedFieldName, type);
if (obfuscatedField == null)
{
throw new InjectionException(String.format("Field \"%s\" could not be found.", obfuscatedFieldName));
}
return obfuscatedField;
}
public static ClassFile toDeobClass(final ClassFile obCf, final ClassGroup deob) throws InjectionException
{
final ClassFile wowThatWasQuick = deob.findObfuscatedName(obCf.getName());
if (wowThatWasQuick == null)
{
throw new InjectionException("It wasn't obfscated enough, or a bit too much. Whatever it was it, wasn't in deob");
}
return wowThatWasQuick;
}
public static Type getFieldType(final Field f)
{
Type type = f.getType();
Annotation obfSignature = f.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE);
if (obfSignature != null)
{
//Annotation exists. Type was updated by us during deobfuscation
type = DeobAnnotations.getObfuscatedType(f);
}
return type;
}
/**
* Find a static method in ClassGroup group. Check the class with name hint first.
* (useful for static methods which are in the class they belong to)
*/
public static Method findStaticMethod(final ClassGroup group, final String name, final String hint) throws InjectionException
{
final ClassFile cf = group.findClass(hint);
if (cf == null)
{
throw new InjectionException(String.format("ClassFile \"%s\" could not be found.", hint));
}
Method m = cf.findStaticMethod(name);
if (m == null)
{
m = group.findStaticMethod(name);
}
return m;
}
/**
* Find a static method in ClassGroup group. Throws exception if not found.
*/
public static Method findStaticMethod(final ClassGroup group, final String name) throws InjectionException
{
Method m = group.findStaticMethod(name);
if (m == null)
{
throw new InjectionException(String.format("Static method \"%s\" could not be found.", name));
}
return m;
}
/**
* Find a static method in ClassGroup group. Throws exception if not found.
*/
public static Method findStaticMethod(final ClassGroup group, final String name, Signature sig) throws InjectionException
{
Method m = group.findStaticMethod(name, sig);
if (m == null)
{
throw new InjectionException(String.format("Static method \"%s\" could not be found.", name));
}
return m;
}
public static Method findMethod(Inject inject, String name) throws InjectionException
{
return findMethod(inject, name, null);
}
public static Method findMethod(Inject inject, String name, String hint) throws InjectionException
{
if (hint != null)
{
ClassFile c = inject.getDeobfuscated().findClass(hint);
if (c == null)
{
throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)");
}
Method deob = c.findMethod(name);
if (deob != null)
{
String obfuscatedName = DeobAnnotations.getObfuscatedName(deob.getAnnotations());
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(deob);
ClassFile ob = toObClass(inject.getVanilla(), c);
return ob.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : deob.getDescriptor());
}
}
for (ClassFile c : inject.getDeobfuscated().getClasses())
{
for (Method m : c.getMethods())
{
if (!m.getName().equals(name))
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations());
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m);
ClassFile c2 = toObClass(inject.getVanilla(), c);
return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor());
}
}
throw new InjectionException("Couldn't find method " + name);
}
public static Method findStaticMethod(Inject inject, String name) throws InjectionException
{
for (ClassFile c : inject.getDeobfuscated().getClasses())
{
for (Method m : c.getMethods())
{
if (!m.isStatic() || !m.getName().equals(name))
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(m.getAnnotations());
Signature obfuscatedSignature = DeobAnnotations.getObfuscatedSignature(m);
ClassFile c2 = toObClass(inject.getVanilla(), c);
return c2.findMethod(obfuscatedName, (obfuscatedSignature != null) ? obfuscatedSignature : m.getDescriptor());
}
}
throw new InjectionException("Couldn't find static method " + name);
}
public static Field findObField(Inject inject, String name) throws InjectionException
{
for (ClassFile c : inject.getVanilla().getClasses())
{
for (Field f : c.getFields())
{
if (!f.getName().equals(name))
{
continue;
}
return f;
}
}
throw new InjectionException(String.format("Field \"%s\" could not be found.", name));
}
public static Field findDeobField(Inject inject, String name) throws InjectionException
{
return findDeobField(inject, name, null);
}
public static Field findDeobField(Inject inject, String name, String hint) throws InjectionException
{
if (hint != null)
{
ClassFile c = inject.getDeobfuscated().findClass(hint);
if (c == null)
{
throw new InjectionException("Class " + hint + " doesn't exist. (check capitalization)");
}
for (Field f : c.getFields())
{
if (!f.getName().equals(name))
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations());
ClassFile c2 = toObClass(inject.getVanilla(), c);
return c2.findField(obfuscatedName);
}
}
for (ClassFile c : inject.getDeobfuscated().getClasses())
{
for (Field f : c.getFields())
{
if (!f.getName().equals(name))
{
continue;
}
String obfuscatedName = DeobAnnotations.getObfuscatedName(f.getAnnotations());
ClassFile c2 = toObClass(inject.getVanilla(), c);
return c2.findField(obfuscatedName);
}
}
throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name));
}
public static Field findDeobFieldButUseless(Inject inject, String name) throws InjectionException
{
for (ClassFile c : inject.getDeobfuscated().getClasses())
{
for (Field f : c.getFields())
{
if (!f.getName().equals(name))
{
continue;
}
return f;
}
}
throw new InjectionException(String.format("Mapped field \"%s\" could not be found.", name));
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
public class InjectionException extends Exception
{
public InjectionException(String message)
{
super(message);
}
public InjectionException(Throwable cause)
{
super(cause);
}
public InjectionException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.io.File;
import java.io.IOException;
import com.google.common.io.Files;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.util.JarUtil;
public class Injector
{
private final ClassGroup deobfuscated, vanilla;
public Injector(ClassGroup deobfuscated, ClassGroup vanilla)
{
this.deobfuscated = deobfuscated;
this.vanilla = vanilla;
}
public static void main(String[] args) throws IOException, InjectionException
{
if (args.length < 3)
{
System.exit(-1);
}
ClassGroup deobfuscated = JarUtil.loadJar(new File(args[0]));
ClassGroup vanilla = JarUtil.loadJar(new File(args[1]));
Injector u = new Injector(
deobfuscated,
vanilla
);
u.inject();
InjectorValidator iv = new InjectorValidator(vanilla);
iv.validate();
u.save(new File(args[2]));
}
public void inject() throws InjectionException
{
Inject instance = new Inject(deobfuscated, vanilla);
instance.run();
}
private void save(File out) throws IOException
{
out.mkdirs();
for (ClassFile cf : vanilla.getClasses())
{
File f = new File(out, cf.getClassName() + ".class");
byte[] data = JarUtil.writeClass(vanilla, cf);
Files.write(data, f);
}
}
}

View File

@@ -1,196 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.signature.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Verifies the injected jar is valid
*
* @author Adam
*/
class InjectorValidator
{
private static final Logger logger = LoggerFactory.getLogger(InjectorValidator.class);
private static final String API_PACKAGE_BASE = "net/runelite/rs/api/";
private final ClassGroup group;
private int error, missing, okay;
InjectorValidator(ClassGroup group)
{
this.group = group;
}
void validate()
{
for (ClassFile cf : group.getClasses())
{
validate(cf);
}
logger.info("{} overridden methods, {} missing", okay, missing);
}
private void validate(ClassFile cf)
{
// find methods of the interface not implemented in the class
for (net.runelite.asm.pool.Class clazz : cf.getInterfaces().getInterfaces())
{
if (!clazz.getName().startsWith(API_PACKAGE_BASE))
{
continue;
}
Class<?> c;
try
{
c = Class.forName(clazz.getName().replace('/', '.'));
}
catch (ClassNotFoundException ex)
{
logger.warn(null, ex);
continue;
}
if (cf.isAbstract())
{
// Abstract classes don't have to implement anything
continue;
}
for (Method method : c.getMethods())
{
if (method.isSynthetic() || method.isDefault())
{
continue;
}
// could check method signature here too but it is
// annoying to deal with both runelite api and java
// reflection api
if (cf.findMethodDeep(method.getName()) == null)
{
logger.warn("Class {} implements interface {} but not does implement method {}",
cf.getName(), c.getSimpleName(), method);
++missing;
}
else
{
++okay;
}
}
}
Set<NameAndSignature> signatures = new HashSet<>();
for (net.runelite.asm.Method method : cf.getMethods())
{
NameAndSignature nas = new NameAndSignature(method.getName(), method.getDescriptor());
if (signatures.contains(nas))
{
logger.error("Class {} has duplicate method with same name and signature {} {}",
cf.getName(), method.getName(), method.getDescriptor());
++error;
}
signatures.add(nas);
}
}
int getError()
{
return error;
}
int getMissing()
{
return missing;
}
int getOkay()
{
return okay;
}
static final class NameAndSignature
{
String name;
Signature signature;
NameAndSignature(String name, Signature signature)
{
this.name = name;
this.signature = signature;
}
@Override
public int hashCode()
{
int hash = 3;
hash = 67 * hash + Objects.hashCode(this.name);
hash = 67 * hash + Objects.hashCode(this.signature);
return hash;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final NameAndSignature other = (NameAndSignature) obj;
if (!Objects.equals(this.name, other.name))
{
return false;
}
if (!Objects.equals(this.signature, other.signature))
{
return false;
}
return true;
}
}
}

View File

@@ -1,998 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import net.runelite.api.mixins.Mixin;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.annotation.Annotation;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instruction.types.FieldInstruction;
import net.runelite.asm.attributes.code.instruction.types.InvokeInstruction;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.asm.attributes.code.instruction.types.ReturnInstruction;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.ANewArray;
import net.runelite.asm.attributes.code.instructions.CheckCast;
import net.runelite.asm.attributes.code.instructions.GetField;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.InvokeDynamic;
import net.runelite.asm.attributes.code.instructions.InvokeSpecial;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.Pop;
import net.runelite.asm.attributes.code.instructions.PutField;
import net.runelite.asm.signature.Signature;
import net.runelite.asm.visitors.ClassFileVisitor;
import net.runelite.deob.DeobAnnotations;
import static net.runelite.injector.InjectUtil.findStaticMethod;
import static net.runelite.injector.InjectUtil.toDeobClass;
import static net.runelite.injector.InjectUtil.toObClass;
import static net.runelite.injector.InjectUtil.toObField;
import org.objectweb.asm.ClassReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MixinInjector
{
private static final Logger logger = LoggerFactory.getLogger(MixinInjector.class);
private static final Type INJECT = new Type("Lnet/runelite/api/mixins/Inject;");
private static final Type SHADOW = new Type("Lnet/runelite/api/mixins/Shadow;");
private static final Type COPY = new Type("Lnet/runelite/api/mixins/Copy;");
private static final Type REPLACE = new Type("Lnet/runelite/api/mixins/Replace;");
private static final Type FIELDHOOK = new Type("Lnet/runelite/api/mixins/FieldHook;");
private static final Type METHODHOOK = new Type("Lnet/runelite/api/mixins/MethodHook;");
private static final Type JAVAX_INJECT = new Type("Ljavax/inject/Inject;");
private static final Type NAMED = new Type("Ljavax/inject/Named;");
private static final String MIXIN_BASE = "net.runelite.mixins";
private static final String ASSERTION_FIELD = "$assertionsDisabled";
private final Inject inject;
// field name -> Field of injected fields
private final Map<String, Field> injectedFields = new HashMap<>();
// Use net.runelite.asm.pool.Field instead of Field because the pool version has hashcode implemented
private final Map<net.runelite.asm.pool.Field, Field> shadowFields = new HashMap<>();
MixinInjector(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
ClassPath classPath;
try
{
classPath = ClassPath.from(this.getClass().getClassLoader());
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
// key: mixin class
// value: mixin targets
Map<Class<?>, List<ClassFile>> mixinClasses = new HashMap<>();
// Find mixins and populate mixinClasses
for (ClassInfo classInfo : classPath.getTopLevelClasses(MIXIN_BASE))
{
Class<?> mixinClass = classInfo.load();
List<ClassFile> mixinTargets = new ArrayList<>();
for (Mixin mixin : mixinClass.getAnnotationsByType(Mixin.class))
{
Class<?> implementInto = mixin.value();
ClassFile targetCf = inject.findVanillaForInterface(implementInto);
if (targetCf == null)
{
throw new InjectionException("No class implements " + implementInto + " for mixin " + mixinClass);
}
mixinTargets.add(targetCf);
}
mixinClasses.put(mixinClass, mixinTargets);
}
inject(mixinClasses);
}
public void inject(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
{
injectFields(mixinClasses);
findShadowFields(mixinClasses);
for (Class<?> mixinClass : mixinClasses.keySet())
{
try
{
for (ClassFile cf : mixinClasses.get(mixinClass))
{
// Make a new mixin ClassFile copy every time,
// so they don't share Code references
ClassFile mixinCf = loadClass(mixinClass);
injectMethods(mixinCf, cf, shadowFields);
}
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
}
injectFieldHooks(mixinClasses);
injectMethodHooks(mixinClasses);
}
/**
* Finds fields that are marked @Inject and inject them into the target
*/
private void injectFields(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
{
// Inject fields, and put them in injectedFields if they can be used by other mixins
for (Class<?> mixinClass : mixinClasses.keySet())
{
ClassFile mixinCf;
try
{
mixinCf = loadClass(mixinClass);
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
for (ClassFile cf : targetCfs)
{
for (Field field : mixinCf.getFields())
{
// Always inject $assertionsEnabled if its missing.
if (ASSERTION_FIELD.equals(field.getName()))
{
if (cf.findField(ASSERTION_FIELD, Type.BOOLEAN) != null)
{
continue;
}
}
else
{
Annotation inject = field.getAnnotations().find(INJECT);
if (inject == null)
{
continue;
}
}
Field copy = new Field(cf, field.getName(), field.getType());
copy.setAccessFlags(field.getAccessFlags());
copy.setPublic();
copy.setValue(field.getValue());
Annotation jInject = field.getAnnotations().find(JAVAX_INJECT);
if (jInject != null)
{
copy.getAnnotations().addAnnotation(jInject);
logger.info("Added javax inject to {}.{}", cf.getClassName(), copy.getName());
Annotation named = field.getAnnotations().find(NAMED);
if (named != null)
{
copy.getAnnotations().addAnnotation(named);
logger.info("Added javax named to {}.{}", cf.getClassName(), copy.getName());
}
}
cf.addField(copy);
if (injectedFields.containsKey(field.getName()) && !field.getName().equals(ASSERTION_FIELD))
{
java.util.logging.Logger.getAnonymousLogger().severe("Duplicate field : " + field.getName());
throw new InjectionException("Injected field names must be globally unique");
}
injectedFields.put(field.getName(), copy);
}
}
}
}
/**
* Find fields which are marked @Shadow, and what they shadow
*/
private void findShadowFields(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
{
// Find shadow fields
// Injected static fields take precedence when looking up shadowed fields
for (Class<?> mixinClass : mixinClasses.keySet())
{
ClassFile mixinCf;
try
{
mixinCf = loadClass(mixinClass);
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
for (Field field : mixinCf.getFields())
{
Annotation shadow = field.getAnnotations().find(SHADOW);
if (shadow != null)
{
if (!field.isStatic())
{
throw new InjectionException("Can only shadow static fields");
}
String shadowName = shadow.getElement().getString(); // shadow this field
Field injectedField = injectedFields.get(shadowName);
if (injectedField != null)
{
// Shadow a field injected by a mixin
shadowFields.put(field.getPoolField(), injectedField);
}
else
{
// Shadow a field already in the gamepack
Field shadowField = InjectUtil.findDeobFieldButUseless(inject, shadowName);
if (shadowField == null)
{
throw new InjectionException("Shadow of nonexistent field " + shadowName);
}
Field obShadow = toObField(inject.getVanilla(), shadowField);
assert obShadow != null;
shadowFields.put(field.getPoolField(), obShadow);
}
}
}
}
}
private ClassFile loadClass(Class<?> clazz) throws IOException
{
try (InputStream is = clazz.getClassLoader().getResourceAsStream(clazz.getName().replace('.', '/') + ".class"))
{
ClassReader reader = new ClassReader(is);
ClassFileVisitor cv = new ClassFileVisitor();
reader.accept(cv, 0);
return cv.getClassFile();
}
}
private void injectMethods(ClassFile mixinCf, ClassFile cf, Map<net.runelite.asm.pool.Field, Field> shadowFields)
throws InjectionException
{
// Keeps mappings between methods annotated with @Copy -> the copied method within the vanilla pack
Map<net.runelite.asm.pool.Method, CopiedMethod> copiedMethods = new HashMap<>();
// Handle the copy mixins first, so all other mixins know of the copies
for (Method method : mixinCf.getMethods())
{
Annotation copyAnnotation = method.getAnnotations().find(COPY);
if (copyAnnotation == null)
{
continue;
}
String deobMethodName = (String) copyAnnotation.getElement().getValue();
Method deobMethod;
if (method.isStatic())
{
deobMethod = findStaticMethod(inject.getDeobfuscated(), deobMethodName, method.getDescriptor().rsApiToRsClient());
}
else
{
ClassFile deobCf = toDeobClass(cf, inject.getDeobfuscated());
deobMethod = deobCf.findMethod(deobMethodName, method.getDescriptor().rsApiToRsClient());
}
if (deobMethod == null)
{
throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf);
}
if (method.isStatic() != deobMethod.isStatic())
{
throw new InjectionException("Mixin method " + method + " should be " + (deobMethod.isStatic() ? "static" : "non-static"));
}
// Find the vanilla class where the method to copy is in
String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations());
ClassFile obCf = inject.getVanilla().findClass(obClassName);
assert obCf != null : "unable to find vanilla class from obfuscated name " + obClassName;
String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations());
Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod);
if (obMethodName == null)
{
obMethodName = deobMethod.getName();
}
if (obMethodSignature == null)
{
obMethodSignature = deobMethod.getDescriptor();
}
Method obMethod = obCf.findMethod(obMethodName, obMethodSignature);
if (obMethod == null)
{
throw new InjectionException("Failed to find the ob method " + obMethodName + " for mixin " + mixinCf);
}
if (method.getDescriptor().size() > obMethod.getDescriptor().size())
{
throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method");
}
Method copy = new Method(cf, "copy$" + deobMethodName, obMethodSignature);
moveCode(copy, obMethod.getCode());
copy.setAccessFlags(obMethod.getAccessFlags());
copy.setPublic();
copy.getExceptions().getExceptions().addAll(obMethod.getExceptions().getExceptions());
copy.getAnnotations().getAnnotations().addAll(obMethod.getAnnotations().getAnnotations());
cf.addMethod(copy);
/*
If the desc for the mixin method and the desc for the ob method
are the same in length, assume that the mixin method is taking
care of the garbage parameter itself.
*/
boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size()
&& deobMethod.getDescriptor().size() < obMethodSignature.size();
copiedMethods.put(method.getPoolMethod(), new CopiedMethod(copy, hasGarbageValue));
logger.debug("Injected copy of {} to {}", obMethod, copy);
}
// Handle the rest of the mixin types
for (Method method : mixinCf.getMethods())
{
boolean isClinit = "<clinit>".equals(method.getName());
boolean isInit = "<init>".equals(method.getName());
boolean hasInject = method.getAnnotations().find(INJECT) != null;
// You can't annotate clinit, so its always injected
if ((hasInject && isInit) || isClinit)
{
if (!"()V".equals(method.getDescriptor().toString()))
{
throw new InjectionException("Injected constructors cannot have arguments");
}
Method[] originalMethods = cf.getMethods().stream()
.filter(n -> n.getName().equals(method.getName()))
.toArray(Method[]::new);
// If there isn't a <clinit> already just inject ours, otherwise rename it
// This is always true for <init>
String name = method.getName();
if (originalMethods.length > 0)
{
name = "rl$$" + (isInit ? "init" : "clinit");
}
String numberlessName = name;
for (int i = 1; cf.findMethod(name, method.getDescriptor()) != null; i++)
{
name = numberlessName + i;
}
Method copy = new Method(cf, name, method.getDescriptor());
moveCode(copy, method.getCode());
copy.setAccessFlags(method.getAccessFlags());
copy.setPrivate();
assert method.getExceptions().getExceptions().isEmpty();
// Remove the call to the superclass's ctor
if (isInit)
{
Instructions instructions = copy.getCode().getInstructions();
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
for (; listIter.hasNext(); )
{
Instruction instr = listIter.next();
if (instr instanceof InvokeSpecial)
{
InvokeSpecial invoke = (InvokeSpecial) instr;
assert invoke.getMethod().getName().equals("<init>");
listIter.remove();
int pops = invoke.getMethod().getType().getArguments().size() + 1;
for (int i = 0; i < pops; i++)
{
listIter.add(new Pop(instructions));
}
break;
}
}
}
setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods);
cf.addMethod(copy);
// Call our method at the return point of the matching method(s)
for (Method om : originalMethods)
{
Instructions instructions = om.getCode().getInstructions();
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
for (; listIter.hasNext(); )
{
Instruction instr = listIter.next();
if (instr instanceof ReturnInstruction)
{
listIter.previous();
if (isInit)
{
listIter.add(new ALoad(instructions, 0));
listIter.add(new InvokeSpecial(instructions, copy.getPoolMethod()));
}
else if (isClinit)
{
listIter.add(new InvokeStatic(instructions, copy.getPoolMethod()));
}
listIter.next();
}
}
}
logger.debug("Injected mixin method {} to {}", copy, cf);
}
else if (hasInject)
{
// Make sure the method doesn't invoke copied methods
for (Instruction i : method.getCode().getInstructions().getInstructions())
{
if (i instanceof InvokeInstruction)
{
InvokeInstruction ii = (InvokeInstruction) i;
if (copiedMethods.containsKey(ii.getMethod()))
{
throw new InjectionException("Injected methods cannot invoke copied methods");
}
}
}
Method copy = new Method(cf, method.getName(), method.getDescriptor());
moveCode(copy, method.getCode());
copy.setAccessFlags(method.getAccessFlags());
copy.setPublic();
assert method.getExceptions().getExceptions().isEmpty();
setOwnersToTargetClass(mixinCf, cf, copy, shadowFields, copiedMethods);
cf.addMethod(copy);
logger.debug("Injected mixin method {} to {}", copy, cf);
}
else if (method.getAnnotations().find(REPLACE) != null)
{
Annotation replaceAnnotation = method.getAnnotations().find(REPLACE);
String deobMethodName = (String) replaceAnnotation.getElement().getValue();
ClassFile deobCf = inject.toDeobClass(cf);
Method deobMethod = findDeobMethod(deobCf, deobMethodName, method.getDescriptor());
if (deobMethod == null)
{
throw new InjectionException("Failed to find the deob method " + deobMethodName + " for mixin " + mixinCf);
}
if (method.isStatic() != deobMethod.isStatic())
{
throw new InjectionException("Mixin method " + method + " should be "
+ (deobMethod.isStatic() ? "static" : "non-static"));
}
String obMethodName = DeobAnnotations.getObfuscatedName(deobMethod.getAnnotations());
Signature obMethodSignature = DeobAnnotations.getObfuscatedSignature(deobMethod);
// Deob signature is the same as ob signature
if (obMethodName == null)
{
obMethodName = deobMethod.getName();
}
if (obMethodSignature == null)
{
obMethodSignature = deobMethod.getDescriptor();
}
// Find the vanilla class where the method to copy is in
String obClassName = DeobAnnotations.getObfuscatedName(deobMethod.getClassFile().getAnnotations());
ClassFile obCf = inject.getVanilla().findClass(obClassName);
Method obMethod = obCf.findMethod(obMethodName, obMethodSignature);
assert obMethod != null : "obfuscated method " + obMethodName + obMethodSignature + " does not exist";
if (method.getDescriptor().size() > obMethod.getDescriptor().size())
{
throw new InjectionException("Mixin methods cannot have more parameters than their corresponding ob method");
}
Type returnType = method.getDescriptor().getReturnValue();
Type deobReturnType = inject.apiTypeToDeobfuscatedType(returnType);
if (!returnType.equals(deobReturnType))
{
ClassFile deobReturnTypeClassFile = inject.getDeobfuscated()
.findClass(deobReturnType.getInternalName());
if (deobReturnTypeClassFile != null)
{
ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobReturnTypeClassFile);
Instructions instructions = method.getCode().getInstructions();
ListIterator<Instruction> listIter = instructions.getInstructions().listIterator();
for (; listIter.hasNext(); )
{
Instruction instr = listIter.next();
if (instr instanceof ReturnInstruction)
{
listIter.previous();
CheckCast checkCast = new CheckCast(instructions);
checkCast.setType(new Type(obReturnTypeClass.getName()));
listIter.add(checkCast);
listIter.next();
}
}
}
}
moveCode(obMethod, method.getCode());
boolean hasGarbageValue = method.getDescriptor().size() != obMethod.getDescriptor().size()
&& deobMethod.getDescriptor().size() < obMethodSignature.size();
if (hasGarbageValue)
{
int garbageIndex = obMethod.isStatic()
? obMethod.getDescriptor().size() - 1
: obMethod.getDescriptor().size();
/*
If the mixin method doesn't have the garbage parameter,
the compiler will have produced code that uses the garbage
parameter's local variable index for other things,
so we'll have to add 1 to all loads/stores to indices
that are >= garbageIndex.
*/
shiftLocalIndices(obMethod.getCode().getInstructions(), garbageIndex);
}
setOwnersToTargetClass(mixinCf, cf, obMethod, shadowFields, copiedMethods);
logger.debug("Replaced method {} with mixin method {}", obMethod, method);
}
}
}
private void moveCode(Method method, Code code)
{
Code newCode = new Code(method);
newCode.setMaxStack(code.getMaxStack());
newCode.getInstructions().getInstructions().addAll(code.getInstructions().getInstructions());
// Update instructions for each instruction
for (Instruction i : newCode.getInstructions().getInstructions())
{
i.setInstructions(newCode.getInstructions());
}
newCode.getExceptions().getExceptions().addAll(code.getExceptions().getExceptions());
for (net.runelite.asm.attributes.code.Exception e : newCode.getExceptions().getExceptions())
{
e.setExceptions(newCode.getExceptions());
}
method.setCode(newCode);
}
private void setOwnersToTargetClass(ClassFile mixinCf, ClassFile cf, Method method,
Map<net.runelite.asm.pool.Field, Field> shadowFields,
Map<net.runelite.asm.pool.Method, CopiedMethod> copiedMethods)
throws InjectionException
{
ListIterator<Instruction> iterator = method.getCode().getInstructions().getInstructions().listIterator();
while (iterator.hasNext())
{
Instruction i = iterator.next();
if (i instanceof ANewArray)
{
Type type = ((ANewArray) i).getType_();
ClassFile deobCf = inject.getDeobfuscated().findClass(type.toString().replace("Lnet/runelite/rs/api/RS", "").replace(";", ""));
if (deobCf != null)
{
ClassFile obReturnTypeClass = toObClass(inject.getVanilla(), deobCf);
Type newType = new Type("L" + obReturnTypeClass.getName() + ";");
((ANewArray) i).setType(newType);
logger.info("Replaced {} type {} with type {}", i, type, newType);
}
}
if (i instanceof InvokeInstruction)
{
InvokeInstruction ii = (InvokeInstruction) i;
CopiedMethod copiedMethod = copiedMethods.get(ii.getMethod());
if (copiedMethod != null)
{
ii.setMethod(copiedMethod.obMethod.getPoolMethod());
// Pass through garbage value if the method has one
if (copiedMethod.hasGarbageValue)
{
int garbageIndex = copiedMethod.obMethod.isStatic()
? copiedMethod.obMethod.getDescriptor().size() - 1
: copiedMethod.obMethod.getDescriptor().size();
iterator.previous();
iterator.add(new ILoad(method.getCode().getInstructions(), garbageIndex));
iterator.next();
}
}
else if (ii.getMethod().getClazz().getName().equals(mixinCf.getName()))
{
ii.setMethod(new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(cf.getName()),
ii.getMethod().getName(),
ii.getMethod().getType()
));
}
}
else if (i instanceof FieldInstruction)
{
FieldInstruction fi = (FieldInstruction) i;
Field shadowed = shadowFields.get(fi.getField());
if (shadowed != null)
{
fi.setField(shadowed.getPoolField());
}
else if (fi.getField().getClazz().getName().equals(mixinCf.getName()))
{
fi.setField(new net.runelite.asm.pool.Field(
new net.runelite.asm.pool.Class(cf.getName()),
fi.getField().getName(),
fi.getField().getType()
));
}
}
else if (i instanceof PushConstantInstruction)
{
PushConstantInstruction pi = (PushConstantInstruction) i;
if (mixinCf.getPoolClass().equals(pi.getConstant()))
{
pi.setConstant(cf.getPoolClass());
}
}
verify(mixinCf, i);
}
}
private void shiftLocalIndices(Instructions instructions, int startIdx)
{
for (Instruction i : instructions.getInstructions())
{
if (i instanceof LVTInstruction)
{
LVTInstruction lvti = (LVTInstruction) i;
if (lvti.getVariableIndex() >= startIdx)
{
lvti.setVariableIndex(lvti.getVariableIndex() + 1);
}
}
}
}
private Method findDeobMethod(ClassFile deobCf, String deobMethodName, Signature descriptor)
throws InjectionException
{
List<Method> matchingMethods = new ArrayList<>();
for (Method m : deobCf.getMethods())
{
if (!deobMethodName.equals(m.getName()))
{
continue;
}
Type returnType = inject.apiTypeToDeobfuscatedType(descriptor.getReturnValue());
Type returnType2 = m.getDescriptor().getReturnValue();
if (!returnType.equals(returnType2))
{
continue;
}
List<Type> args = descriptor.getArguments();
List<Type> args2 = m.getDescriptor().getArguments();
if (args.size() > args2.size())
{
continue;
}
boolean matchingArgs = true;
for (int i = 0; i < args.size(); i++)
{
Type type = inject.apiTypeToDeobfuscatedType(args.get(i));
Type type2 = args2.get(i);
if (!type.equals(type2))
{
matchingArgs = false;
break;
}
}
if (!matchingArgs)
{
continue;
}
matchingMethods.add(m);
}
if (matchingMethods.size() > 1)
{
// this happens when it has found several deob methods for some mixin method,
// to get rid of the error, refine your search by making your mixin method have more parameters
throw new InjectionException("There are several matching methods when there should only be one");
}
else if (matchingMethods.size() == 1)
{
return matchingMethods.get(0);
}
Method method = deobCf.findMethod(deobMethodName);
if (method == null)
{
// Look for static methods if an instance method couldn't be found
for (ClassFile deobCf2 : inject.getDeobfuscated().getClasses())
{
if (deobCf2 != deobCf)
{
method = deobCf2.findMethod(deobMethodName);
if (method != null)
{
break;
}
}
}
}
return method;
}
private void verify(ClassFile mixinCf, Instruction i) throws InjectionException
{
if (i instanceof FieldInstruction)
{
FieldInstruction fi = (FieldInstruction) i;
if (fi.getField().getClazz().getName().equals(mixinCf.getName()))
{
if (i instanceof PutField || i instanceof GetField)
{
throw new InjectionException("Access to non static member field of mixin");
}
Field field = fi.getMyField();
if (field != null && !field.isPublic())
{
throw new InjectionException("Static access to non public field " + field);
}
}
}
else if (i instanceof InvokeStatic)
{
InvokeStatic is = (InvokeStatic) i;
if (is.getMethod().getClazz() != mixinCf.getPoolClass()
&& is.getMethod().getClazz().getName().startsWith(MIXIN_BASE.replace(".", "/")))
{
throw new InjectionException("Invoking static methods of other mixins is not supported");
}
}
else if (i instanceof InvokeDynamic)
{
// RS classes don't verify under java 7+ due to the
// super() invokespecial being inside of a try{}
throw new InjectionException("Injected bytecode must be Java 6 compatible");
}
}
private void injectFieldHooks(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
{
InjectHook injectHook = new InjectHook(inject);
for (Class<?> mixinClass : mixinClasses.keySet())
{
ClassFile mixinCf;
try
{
mixinCf = loadClass(mixinClass);
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
for (ClassFile cf : targetCfs)
{
for (Method method : mixinCf.getMethods())
{
Annotation fieldHook = method.getAnnotations().find(FIELDHOOK);
if (fieldHook != null)
{
String hookName = fieldHook.getElement().getString();
boolean before = fieldHook.getElements().size() == 2 && fieldHook.getElements().get(1).getValue().equals(true);
ClassFile deobCf = inject.toDeobClass(cf);
Field targetField = deobCf.findField(hookName);
if (targetField == null)
{
// first try non static fields, then static
targetField = InjectUtil.findDeobFieldButUseless(inject, hookName);
}
if (targetField == null)
{
throw new InjectionException("Field hook for nonexistent field " + hookName + " on " + method);
}
Annotation an = targetField.getAnnotations().find(DeobAnnotations.OBFUSCATED_GETTER);
Number getter = null;
if (an != null)
{
getter = (Number) an.getElement().getValue();
}
Field obField = toObField(inject.getVanilla(), targetField);
if (method.isStatic() != targetField.isStatic())
{
throw new InjectionException("Field hook method static flag must match target field");
}
// cf is the target class to invoke
InjectHook.HookInfo hookInfo = new InjectHook.HookInfo();
hookInfo.clazz = cf.getName();
hookInfo.fieldName = hookName;
hookInfo.method = method;
hookInfo.before = before;
hookInfo.getter = getter;
injectHook.hook(obField, hookInfo);
}
}
}
}
injectHook.run();
logger.info("Injected {} field hooks", injectHook.getInjectedHooks());
}
private void injectMethodHooks(Map<Class<?>, List<ClassFile>> mixinClasses) throws InjectionException
{
InjectHookMethod injectHookMethod = new InjectHookMethod(inject);
for (Class<?> mixinClass : mixinClasses.keySet())
{
ClassFile mixinCf;
try
{
mixinCf = loadClass(mixinClass);
}
catch (IOException ex)
{
throw new InjectionException(ex);
}
List<ClassFile> targetCfs = mixinClasses.get(mixinClass);
for (ClassFile cf : targetCfs)
{
for (Method method : mixinCf.getMethods())
{
Annotation methodHook = method.getAnnotations().find(METHODHOOK);
if (methodHook == null)
{
continue;
}
String hookName = methodHook.getElement().getString();
boolean end = methodHook.getElements().size() == 2 && methodHook.getElements().get(1).getValue().equals(true);
ClassFile deobCf = inject.toDeobClass(cf);
Method targetMethod = findDeobMethod(deobCf, hookName, method.getDescriptor());
if (targetMethod == null)
{
throw new InjectionException("Method hook for nonexistent method " + hookName + " on " + method);
}
if (method.isStatic() != targetMethod.isStatic())
{
throw new InjectionException("Method hook static flag must match target - " + hookName);
}
injectHookMethod.inject(method, targetMethod, hookName, end, false);
}
}
}
}
private static class CopiedMethod
{
private Method obMethod;
private boolean hasGarbageValue;
private CopiedMethod(Method obMethod, boolean hasGarbageValue)
{
this.obMethod = obMethod;
this.hasGarbageValue = hasGarbageValue;
}
}
}

View File

@@ -1,124 +0,0 @@
package net.runelite.injector.raw;
import java.util.ListIterator;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import net.runelite.injector.InjectUtil;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClearColorBuffer
{
private static final Logger log = LoggerFactory.getLogger(ClearColorBuffer.class);
private static final net.runelite.asm.pool.Method clearBuffer = new net.runelite.asm.pool.Method(
new Class("net.runelite.client.callback.Hooks"),
"clearColorBuffer",
new Signature("(IIIII)V")
);
private final Inject inject;
public ClearColorBuffer(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
injectColorBufferHooks();
}
private void injectColorBufferHooks() throws InjectionException
{
net.runelite.asm.pool.Method fillRectangle = InjectUtil.findStaticMethod(inject, "Rasterizer2D_fillRectangle").getPoolMethod();
int count = 0;
int replaced = 0;
for (ClassFile cf : inject.getVanilla().getClasses())
{
for (Method m : cf.getMethods())
{
if (!m.isStatic())
{
continue;
}
Code c = m.getCode();
if (c == null)
{
continue;
}
Instructions ins = c.getInstructions();
ListIterator<Instruction> it = ins.getInstructions().listIterator();
for (; it.hasNext(); )
{
Instruction i = it.next();
if (!(i instanceof InvokeStatic))
{
continue;
}
if (!((InvokeStatic) i).getMethod().equals(fillRectangle))
{
continue;
}
int indexToReturnTo = it.nextIndex();
count++;
it.previous();
Instruction current = it.previous();
if (current instanceof LDC && ((LDC) current).getConstantAsInt() == 0)
{
int varIdx = 0;
for (; ; )
{
current = it.previous();
if (current instanceof ILoad && ((ILoad) current).getVariableIndex() == 3 - varIdx)
{
varIdx++;
log.debug(varIdx + " we can count yay");
continue;
}
break;
}
if (varIdx == 4)
{
for (; !(current instanceof InvokeStatic); )
{
current = it.next();
}
assert it.nextIndex() == indexToReturnTo;
it.set(new InvokeStatic(ins, clearBuffer));
replaced++;
log.debug("Found drawRectangle at {}. Found: {}, replaced {}", m.getName(), count, replaced);
}
else
{
log.debug("Welp, guess this wasn't it chief " + m);
}
}
while (it.nextIndex() != indexToReturnTo)
{
it.next();
}
}
}
}
}
}

View File

@@ -1,264 +0,0 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector.raw;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Set;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.Label;
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import static net.runelite.injector.InjectHookMethod.HOOKS;
import static net.runelite.injector.InjectUtil.findStaticMethod;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DrawAfterWidgets
{
private static final Logger logger = LoggerFactory.getLogger(DrawAfterWidgets.class);
private final Inject inject;
public DrawAfterWidgets(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
injectDrawAfterWidgets();
}
private void injectDrawAfterWidgets() throws InjectionException
{
/*
This call has to be injected using raw injection because the
drawWidgets method gets inlined in some revisions. If it wouldn't be,
mixins would be used to add the call to the end of drawWidgets.
--> This hook depends on the positions of "if (535573958 * kl != -1)" and "jz.db();".
Revision 180 - client.gs():
______________________________________________________
@Export("drawLoggedIn")
final void drawLoggedIn() {
if(rootInterface != -1) {
ClientPreferences.method1809(rootInterface);
}
int var1;
for(var1 = 0; var1 < rootWidgetCount; ++var1) {
if(__client_od[var1]) {
__client_ot[var1] = true;
}
__client_oq[var1] = __client_od[var1];
__client_od[var1] = false;
}
__client_oo = cycle;
__client_lq = -1;
__client_ln = -1;
UserComparator6.__fg_jh = null;
if(rootInterface != -1) {
rootWidgetCount = 0;
Interpreter.method1977(rootInterface, 0, 0, SoundCache.canvasWidth, Huffman.canvasHeight, 0, 0, -1);
}
< -- here appearantly
Rasterizer2D.Rasterizer2D_resetClip();
______________________________________________________
*/
boolean injected = false;
Method noClip = findStaticMethod(inject, "Rasterizer2D_resetClip"); // !!!!!
if (noClip == null)
{
throw new InjectionException("Mapped method \"Rasterizer2D_resetClip\" could not be found.");
}
net.runelite.asm.pool.Method poolNoClip = noClip.getPoolMethod();
for (ClassFile c : inject.getVanilla().getClasses())
{
for (Method m : c.getMethods())
{
if (m.getCode() == null)
{
continue;
}
Instructions instructions = m.getCode().getInstructions();
Set<Label> labels = new HashSet<>();
// Let's find "invokestatic <some class>.noClip()" and its label
ListIterator<Instruction> labelIterator = instructions.getInstructions().listIterator();
while (labelIterator.hasNext())
{
Instruction i = labelIterator.next();
if (!(i instanceof InvokeStatic))
{
continue;
}
InvokeStatic is = (InvokeStatic) i;
if (!is.getMethod().equals(poolNoClip))
{
continue;
}
labelIterator.previous();
Instruction i2 = labelIterator.previous();
labelIterator.next();
labelIterator.next();
// Find the label that marks the code path for the instruction
if (!(i2 instanceof Label))
{
continue;
}
// There can be several noClip invocations in a method, so let's catch them all
labels.add((Label) i2);
}
if (labels.isEmpty())
{
// If we get here, we're either in the wrong method
// or Jagex has removed the "if (535573958 * kl != -1)"
// logger.debug("Could not find the label for jumping to the " + noClip + " call in " + m);
continue;
}
Set<Label> labelsToInjectAfter = new HashSet<>();
ListIterator<Instruction> jumpIterator = instructions.getInstructions().listIterator();
while (jumpIterator.hasNext())
{
Instruction i = jumpIterator.next();
if (!(i instanceof JumpingInstruction))
{
continue;
}
JumpingInstruction ji = (JumpingInstruction) i;
Label label = null;
for (Label l : labels)
{
if (ji.getJumps().contains(l))
{
label = l;
break;
}
}
if (label == null)
{
continue;
}
jumpIterator.previous();
Set<Instruction> insns = new HashSet<>();
insns.add(jumpIterator.previous());
insns.add(jumpIterator.previous());
insns.add(jumpIterator.previous());
insns.add(jumpIterator.previous());
// Get the iterator back to i's position
jumpIterator.next();
jumpIterator.next();
jumpIterator.next();
jumpIterator.next();
jumpIterator.next();
/*
Check that these instruction types are passed into the if-statement:
ICONST_M1
GETSTATIC client.kr : I
LDC 634425425
IMUL
We cannot depend on the order of these because of the obfuscation,
so let's make it easier by just checking that they are there.
*/
if (insns.stream().filter(i2 -> i2 instanceof PushConstantInstruction).count() != 2
|| insns.stream().filter(i2 -> i2 instanceof IMul).count() != 1
|| insns.stream().filter(i2 -> i2 instanceof GetStatic).count() != 1)
{
continue;
}
// At this point, we have found the real injection point
labelsToInjectAfter.add(label);
}
for (Label l : labelsToInjectAfter)
{
InvokeStatic invoke = new InvokeStatic(instructions,
new net.runelite.asm.pool.Method(
new net.runelite.asm.pool.Class(HOOKS),
"drawAfterWidgets",
new Signature("()V")
)
);
instructions.addInstruction(instructions.getInstructions().indexOf(l) + 1, invoke);
logger.info("injectDrawAfterWidgets injected a call after " + l);
injected = true;
}
}
}
if (!injected)
{
throw new InjectionException("injectDrawAfterWidgets failed to inject!");
}
}
}

View File

@@ -1,135 +0,0 @@
package net.runelite.injector.raw;
import java.util.ListIterator;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.Label;
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IfEq;
import net.runelite.asm.attributes.code.instructions.IfNe;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.pool.Class;
import net.runelite.asm.pool.Field;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import static net.runelite.injector.InjectUtil.findDeobField;
import static net.runelite.injector.InjectUtil.findStaticMethod;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DrawMenu
{
private final Logger log = LoggerFactory.getLogger(DrawMenu.class);
private final Inject inject;
private static final net.runelite.asm.pool.Method hook = new net.runelite.asm.pool.Method(
new Class("net.runelite.client.callback.Hooks"),
"drawMenu",
new Signature("()Z")
);
//Label Getstatic client.isMenuOpen
//Ifne -> Label Drawmenu
//Jump -> Label Drawtext
//Label drawtext
//Ldc xxx
//Getstatic client. something with viewport size?
//Imul
//Iconst_m1
//Ifne -> Label after draw menu <- info we need
//Getstatic / LDC (same getstatic and LDC before)
//Getstatic / LDC
public DrawMenu(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
Field isMenuOpen = findDeobField(inject, "isMenuOpen").getPoolField();
net.runelite.asm.pool.Method topLeftText = findStaticMethod(inject, "drawMenuActionTextAt").getPoolMethod();
for (ClassFile cf : inject.getVanilla().getClasses())
{
for (Method m : cf.getMethods())
{
Code c = m.getCode();
if (c == null)
{
continue;
}
Instructions ins = c.getInstructions();
ListIterator<Instruction> it = ins.getInstructions().listIterator();
int injectIndex = -1;
Label after = null;
boolean foundBefore = false;
boolean foundAfter = false;
while (it.hasNext())
{
Instruction i = it.next();
if (!(i instanceof GetStatic) && !(i instanceof InvokeStatic))
{
continue;
}
if (!foundBefore && i instanceof GetStatic)
{
if (!((GetStatic) i).getField().equals(isMenuOpen))
{
continue;
}
i = it.next();
if (!(i instanceof IfEq) && !(i instanceof IfNe))
{
continue;
}
if (i instanceof IfEq)
{
injectIndex = it.nextIndex();
}
else
{
injectIndex = ins.getInstructions().indexOf(((IfNe) i).getJumps().get(0)) + 1;
}
foundBefore = true;
}
else if (!foundAfter && i instanceof InvokeStatic
&& ((InvokeStatic) i).getMethod().equals(topLeftText))
{
i = it.next();
assert i instanceof JumpingInstruction;
after = ((JumpingInstruction) i).getJumps().get(0);
foundAfter = true;
}
if (foundBefore && foundAfter)
{
break;
}
}
if (!foundBefore || !foundAfter || injectIndex == -1)
{
continue;
}
ins.addInstruction(injectIndex, new IfNe(ins, after));
ins.addInstruction(injectIndex, new InvokeStatic(ins, hook));
log.info("Injected drawmenu hook in {} at index {}", m, injectIndex);
return;
}
}
}
}

View File

@@ -1,202 +0,0 @@
package net.runelite.injector.raw;
import com.google.common.base.Stopwatch;
import java.util.Iterator;
import java.util.ListIterator;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.Label;
import net.runelite.asm.attributes.code.instruction.types.ComparisonInstruction;
import net.runelite.asm.attributes.code.instruction.types.JumpingInstruction;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.BiPush;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IAnd;
import net.runelite.asm.attributes.code.instructions.IfACmpEq;
import net.runelite.asm.attributes.code.instructions.IfACmpNe;
import net.runelite.asm.attributes.code.instructions.IfICmpNe;
import net.runelite.asm.attributes.code.instructions.IfNe;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.pool.Field;
import net.runelite.injector.Inject;
import net.runelite.injector.InjectUtil;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HidePlayerAttacks
{
private final Logger log = LoggerFactory.getLogger(HidePlayerAttacks.class);
private final Inject inject;
public HidePlayerAttacks(Inject inject)
{
this.inject = inject;
}
private Method addPlayerOptions;
private net.runelite.asm.pool.Method shouldHideAttackOptionFor;
public void inject() throws InjectionException
{
Stopwatch stopwatch = Stopwatch.createStarted();
addPlayerOptions = InjectUtil.findStaticMethod(inject, "addPlayerToMenu");
shouldHideAttackOptionFor = inject.getVanilla().findClass("client").findMethod("shouldHideAttackOptionFor").getPoolMethod();
injectHideAttack();
injectHideCast();
stopwatch.stop();
log.info("HidePlayerAttacks took {}", stopwatch.toString());
}
private void injectHideAttack() throws InjectionException
{
final Field AttackOption_hidden = InjectUtil.findDeobField(inject, "AttackOption_hidden", "AttackOption").getPoolField();
final Field attackOption = InjectUtil.findDeobField(inject, "playerAttackOption", "Client").getPoolField();
// GETSTATIC GETSTATIC
// GETSTATIC GETSTATIC
// IFACMPEQ -> label continue IFACMPNE -> label whatever lets carry on
// MORE OBFUSCATION
int injectIdx = -1;
Instruction labelIns = null;
Label label = null;
Instructions ins = addPlayerOptions.getCode().getInstructions();
Iterator<Instruction> iterator = ins.getInstructions().iterator();
while (iterator.hasNext())
{
Instruction i = iterator.next();
if (!(i instanceof GetStatic))
{
continue;
}
Field field = ((GetStatic) i).getField();
if (!field.equals(AttackOption_hidden) && !field.equals(attackOption))
{
continue;
}
i = iterator.next();
if (!(i instanceof GetStatic))
{
continue;
}
field = ((GetStatic) i).getField();
if (!field.equals(AttackOption_hidden) && !field.equals(attackOption))
{
continue;
}
i = iterator.next();
if (!(i instanceof ComparisonInstruction && i instanceof JumpingInstruction))
{
log.info("You're not supposed to see this lol");
continue;
}
if (i instanceof IfACmpEq)
{
injectIdx = ins.getInstructions().indexOf(i) + 1;
label = ((IfACmpEq) i).getJumps().get(0);
}
else if (i instanceof IfACmpNe)
{
injectIdx = ins.getInstructions().indexOf(((IfACmpNe) i).getJumps().get(0)) + 1;
// We're gonna have to inject a extra label
labelIns = iterator.next();
}
break;
}
if (injectIdx <= 0 || label == null && labelIns == null)
{
throw new InjectionException("HidePlayerAttacks failed");
}
// Load the player
ALoad i1 = new ALoad(ins, 0);
// Get the boolean
InvokeStatic i2 = new InvokeStatic(ins, shouldHideAttackOptionFor);
ins.addInstruction(injectIdx, i1);
ins.addInstruction(injectIdx + 1, i2);
if (label == null)
{
label = ins.createLabelFor(labelIns);
ins.rebuildLabels();
injectIdx = ins.getInstructions().indexOf(i2) + 1;
}
// Compare n such
IfNe i3 = new IfNe(ins, label);
ins.addInstruction(injectIdx, i3);
}
private void injectHideCast() throws InjectionException
{
// LABEL before
// BIPUSH 8
// LDC (garbage)
// GETSTATIC selectedSpellFlags
// IMUL
// BIPUSH 8
// IAND
// IF_ICMPNE -> skip adding option
//
// <--- Inject call here
// <--- Inject comparison here (duh)
//
// add option n such
Instructions ins = addPlayerOptions.getCode().getInstructions();
log.info(String.valueOf(ins.getInstructions().size()));
ListIterator<Instruction> iterator = ins.getInstructions().listIterator();
while (iterator.hasNext())
{
Instruction i = iterator.next();
if (!(i instanceof BiPush) || (byte) ((BiPush) i).getConstant() != 8)
{
continue;
}
i = iterator.next();
if (!(i instanceof IAnd))
{
continue;
/*log.info(i.getClass().getName() + i.getClass().getSuperclass() + i.getType().getName() +
i.getType().getInstructionClass() + i.getInstructions() + i.toString());
throw new InjectionException("Yikes I didn't expect this");**/
}
if (!(i instanceof IfICmpNe))
{
continue;
}
Label target = ((IfICmpNe) i).getJumps().get(0);
// Load the player
ALoad i1 = new ALoad(ins, 0);
// Get the boolean
InvokeStatic i2 = new InvokeStatic(ins, shouldHideAttackOptionFor);
// Compare n such
IfNe i3 = new IfNe(ins, target);
iterator.add(i1);
iterator.add(i2);
iterator.add(i3);
return;
}
}
}

View File

@@ -1,83 +0,0 @@
package net.runelite.injector.raw;
import com.google.common.base.Stopwatch;
import java.util.ListIterator;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.BiPush;
import net.runelite.injector.Inject;
import net.runelite.injector.InjectUtil;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Occluder
{
private final Inject inject;
private static final Logger log = LoggerFactory.getLogger(Occluder.class);
private static final byte OLDVALUE = 25;
private static final byte NEWVALUE = 90;
public Occluder(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
Stopwatch stopwatch = Stopwatch.createStarted();
Method occlude = InjectUtil.findMethod(inject, "occlude");
int replaced = 0;
if (occlude == null)
{
throw new InjectionException("Occlude couldn't be found");
}
Code code = occlude.getCode();
if (code == null)
{
throw new InjectionException("Occlude code was null");
}
Instructions ins = code.getInstructions();
ListIterator<Instruction> it = ins.getInstructions().listIterator();
while (it.hasNext())
{
Instruction i = it.next();
if (!(i instanceof BiPush))
{
continue;
}
boolean shouldChange = (byte) ((BiPush) i).getConstant() == OLDVALUE;
if (!shouldChange)
{
continue;
}
replaced++;
Instruction biPush = new BiPush(ins, NEWVALUE);
it.set(biPush);
}
stopwatch.stop();
if (replaced != 10)
{
throw new InjectionException("Only found " + replaced + " 25's to replace in occlude instead of expected 10");
}
log.info("Changed {} values in occlude()", replaced);
log.info("Occluder took {}", stopwatch.toString());
}
}

View File

@@ -1,268 +0,0 @@
/*
* Copyright (c) 2018, Lucas <https://github.com/Lucwousin>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector.raw;
import com.google.common.collect.Lists;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.attributes.Code;
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.instruction.types.FieldInstruction;
import net.runelite.asm.attributes.code.instruction.types.GetFieldInstruction;
import net.runelite.asm.attributes.code.instruction.types.LVTInstruction;
import net.runelite.asm.attributes.code.instruction.types.PushConstantInstruction;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.IALoad;
import net.runelite.asm.attributes.code.instructions.IAStore;
import net.runelite.asm.attributes.code.instructions.IAdd;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.IOr;
import net.runelite.asm.attributes.code.instructions.IShR;
import net.runelite.asm.attributes.code.instructions.ISub;
import net.runelite.asm.attributes.code.instructions.IUShR;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.attributes.code.instructions.SiPush;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.MethodContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.asm.execution.VariableContext;
import net.runelite.asm.pool.Class;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import net.runelite.injector.InjectUtil;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RasterizerAlpha
{
private static final Logger logger = LoggerFactory.getLogger(RasterizerAlpha.class);
private static final net.runelite.asm.pool.Method DRAWALPHA = new net.runelite.asm.pool.Method(
new Class("client"),
"drawAlpha",
new Signature("([IIII)V")
);
private static final int ALPHA = 0xff000000;
private final Inject inject;
public RasterizerAlpha(Inject inject)
{
this.inject = inject;
}
/*
* This class exists cause of removing colour banding.
*
* Push array on stack
* Push array index on stack
* Push colour on stack -> we're interested in where the colour comes from
* Put colour in array, popping array, index and colour
*
*
*
*/
public void inject() throws InjectionException
{
final Field r2dPx = InjectUtil.findDeobField(inject, "Rasterizer2D_pixels", "Rasterizer2D");
final Method draw = InjectUtil.findMethod(inject, "draw", "Client");
final ClassFile rasterizer2D = r2dPx.getClassFile();
final Execution ex = new Execution(rasterizer2D.getGroup());
ex.staticStep = false;
ex.step = false;
ex.addMethod(draw);
int[] counts = new int[2];
ex.addMethodContextVisitor((MethodContext mc) ->
{
Instructions instrs = getInstrs(mc);
if (instrs == null)
return;
int count = 0;
int orCount = 0;
for (InstructionContext ic : mc.getInstructionContexts())
{
Instruction instruction = ic.getInstruction();
if (!(instruction instanceof IAStore))
continue;
// Field field = astore.getMyField(ic);
// doesn't track into methods so doing it here
StackContext array = ic.getPops().get(2);
if (!isSameField(r2dPx, array))
continue;
// This is the colour that's being set
StackContext colour = ic.getPops().get(0);
// resolve gets the original value pusher
InstructionContext colPusher = colour.getPushed().resolve(colour);
Instruction colPushI = colPusher.getInstruction();
// If it's not a >> or a | we're not interested
if (colPushI instanceof LVTInstruction // when called from a method we didn't execute
|| colPushI instanceof PushConstantInstruction &&
!((PushConstantInstruction) colPushI).getConstant().equals(0)
|| colPushI instanceof IALoad)
{
// OR with 0xFF000000, unless 0
int storeIdx = instrs.getInstructions().indexOf(instruction);
instrs.addInstruction(storeIdx++, new LDC(instrs, ALPHA));
instrs.addInstruction(storeIdx, new IOr(instrs, InstructionType.IOR));
++orCount;
continue;
}
else if (!(
colPushI instanceof IShR ||
colPushI instanceof IUShR ||
colPushI instanceof IAdd))
{
continue;
}
// So we know we're dealing with alpha here, now we need the alpha value
// earlier on in the method there's been a 256 - XXX, where xxx is alpha
for (InstructionContext ins : mc.getInstructionContexts())
{
if (!(ins.getInstruction() instanceof SiPush))
continue;
SiPush pci = (SiPush) ins.getInstruction();
if ((short) pci.getConstant() != (short) 256)
continue;
InstructionContext isub = ins.getPushes().get(0).getPopped().get(0);
if (!(isub.getInstruction() instanceof ISub))
continue;
StackContext alphaPop = isub.getPops().get(0);
InstructionContext alphaPusher = alphaPop.getPushed().resolve(alphaPop);
InstructionContext isubResult = isub.getPushes().get(0).getPopped().get(0);
if (pushesToSameField(isubResult, alphaPusher))
{
alphaPusher = resolveFieldThroughInvokes(alphaPop);
if (alphaPusher == null)
throw new RuntimeException("Alpha var is overwritten and we don't know what pushed it"); // cheeky unchecked
}
int storeIdx = instrs.getInstructions().indexOf(instruction);
Instruction alphaPushI = alphaPusher.getInstruction();
if (alphaPushI instanceof GetStatic)
{
instrs.addInstruction(storeIdx++, new LDC(instrs, 255));
instrs.addInstruction(storeIdx++, new GetStatic(instrs, ((GetStatic) alphaPushI).getField()));
instrs.addInstruction(storeIdx++, new ISub(instrs, InstructionType.ISUB));
}
else if (alphaPushI instanceof LVTInstruction)
{
instrs.addInstruction(storeIdx++, new ILoad(instrs, ((LVTInstruction) alphaPushI).getVariableIndex()));
}
instrs.getInstructions().set(storeIdx, new InvokeStatic(instrs, DRAWALPHA));
++count;
break;
}
}
if (orCount != 0)
{
counts[0] += orCount;
logger.info("Added {} OR's into {}", orCount, mc.getMethod());
}
if (count != 0)
{
counts[1] += count;
logger.info("Injected {} DrawAlpha invokes into {}", count, mc.getMethod());
}
});
ex.run();
logger.info("Injected {} DrawAlpha invokes and {} ors", counts[1], counts[0]);
}
private static boolean pushesToSameField(InstructionContext cA, InstructionContext cB)
{
if (cA.getInstruction() instanceof FieldInstruction && cB instanceof FieldInstruction)
{
Field a = ((FieldInstruction) cA.getInstruction()).getMyField();
Field b = ((FieldInstruction) cB.getInstruction()).getMyField();
return a == b;
}
return false;
}
private static Instructions getInstrs(MethodContext mc)
{
Code c = mc.getMethod().getCode();
if (c == null)
return null;
return c.getInstructions();
}
private static InstructionContext resolveFieldThroughInvokes(StackContext stackContext)
{
InstructionContext pusher = stackContext.getPushed().resolve(stackContext);
if (pusher.getInstruction() instanceof GetFieldInstruction)
return pusher;
// No field I wanna trace, rn at least
if (!(pusher.getInstruction() instanceof LVTInstruction))
return null;
int vidx = ((LVTInstruction) pusher.getInstruction()).getVariableIndex();
VariableContext vc = pusher.getVariables().get(vidx);
stackContext = Lists.reverse(vc.getInstructionWhichStored().getPops()).get(vidx);
return resolveFieldThroughInvokes(stackContext);
}
private static boolean isSameField(Field f, StackContext array)
{
InstructionContext ic = resolveFieldThroughInvokes(array);
if (ic == null)
return false;
return ((GetFieldInstruction) ic.getInstruction()).getMyField() == f;
}
}

View File

@@ -1,86 +0,0 @@
package net.runelite.injector.raw;
import com.google.common.base.Stopwatch;
import java.util.ArrayList;
import java.util.List;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.pool.Class;
import net.runelite.asm.pool.Method;
import net.runelite.asm.signature.Signature;
import net.runelite.injector.Inject;
import static net.runelite.injector.InjectUtil.findMethod;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RenderDraw
{
private static final Logger log = LoggerFactory.getLogger(RenderDraw.class);
private static final net.runelite.asm.pool.Method renderDraw = new net.runelite.asm.pool.Method(
new Class("net.runelite.client.callback.Hooks"),
"renderDraw",
new Signature("(Lnet/runelite/api/Entity;IIIIIIIIJ)V")
);
private final Inject inject;
public RenderDraw(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
Stopwatch stopwatch = Stopwatch.createStarted();
net.runelite.asm.Method obmethod = findMethod(inject, "drawTile", "Scene");
Method renderDraw = findMethod(inject, "draw", "Entity").getPoolMethod();
Instructions ins = obmethod.getCode().getInstructions();
replace(ins, renderDraw);
log.info("RenderDraw took {}", stopwatch.toString());
}
private void replace(Instructions ins, net.runelite.asm.pool.Method meth) throws InjectionException
{
List<Instruction> insList = new ArrayList<>();
int count = 0;
for (Instruction i : ins.getInstructions())
{
if (i instanceof InvokeVirtual)
{
if (((InvokeVirtual) i).getMethod().equals(meth))
{
int index = ins.getInstructions().indexOf(i);
count++;
log.debug("Found renderDraw at index {}, {} found.", index, count);
insList.add(i);
}
}
}
if (count < 21)
{
throw new InjectionException("Not all renderDraws were found");
}
if (count != 21)
{
log.warn("Found {} draws while 21 were expected. Rev update?", count);
}
else
{
log.info("RenderDraw replaced {} method calls", count);
}
for (Instruction i : insList)
{
Instruction invoke = new InvokeStatic(ins, renderDraw);
ins.replace(i, invoke);
}
}
}

View File

@@ -1,306 +0,0 @@
/*
* Copyright (c) 2018 Abex
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector.raw;
import java.util.HashSet;
import java.util.ListIterator;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.code.Instruction;
import net.runelite.asm.attributes.code.Instructions;
import net.runelite.asm.attributes.code.Label;
import net.runelite.asm.attributes.code.instructions.ALoad;
import net.runelite.asm.attributes.code.instructions.AStore;
import net.runelite.asm.attributes.code.instructions.Dup;
import net.runelite.asm.attributes.code.instructions.GetField;
import net.runelite.asm.attributes.code.instructions.IALoad;
import net.runelite.asm.attributes.code.instructions.IInc;
import net.runelite.asm.attributes.code.instructions.ILoad;
import net.runelite.asm.attributes.code.instructions.IMul;
import net.runelite.asm.attributes.code.instructions.IStore;
import net.runelite.asm.attributes.code.instructions.IfNe;
import net.runelite.asm.attributes.code.instructions.InvokeStatic;
import net.runelite.asm.attributes.code.instructions.PutField;
import net.runelite.asm.attributes.code.instructions.PutStatic;
import net.runelite.asm.execution.Execution;
import net.runelite.asm.execution.InstructionContext;
import net.runelite.asm.execution.MethodContext;
import net.runelite.asm.execution.StackContext;
import net.runelite.deob.DeobAnnotations;
import net.runelite.injector.Inject;
import static net.runelite.injector.InjectUtil.findDeobField;
import static net.runelite.injector.InjectUtil.findObField;
import static net.runelite.injector.InjectUtil.findStaticMethod;
import net.runelite.injector.InjectionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ScriptVM
{
private static final Logger log = LoggerFactory.getLogger(ScriptVM.class);
private final Inject inject;
public ScriptVM(Inject inject)
{
this.inject = inject;
}
public void inject() throws InjectionException
{
injectScriptVMHooks();
}
private void injectScriptVMHooks() throws InjectionException
{
final ClassGroup vanilla = inject.getVanilla();
/*
This hooks local variable assignments in the copied version of runScript:
- The currently executing script > client.currentScript
- The currently executing script's program counter > client.currentScriptPC
- The currently executing opcode > client.vmExecuteOpcode(I)Z
The currently executing script variable is located as the outermost Script local
The PC is located by its use in PutField ScriptFrame::invokedFromPC
The currently executing opcode is found by searching for iaload with the script's instruction array
The script's instruction array is identified by looking for the getfield from script.instructions
bn.g @ rev 163 :
// Jump back to here if vmExecuteOpcode returns true
aload6 // Script.instructions
iinc 5 1 // ++PC
iload5 // PC
iaload
istore8
// <- Inject here
iload8
bipush 100
if_icmpge L52
*/
final String scriptObName = DeobAnnotations.getObfuscatedName(inject.getDeobfuscated().findClass("Script").getAnnotations());
final Method runScript = findStaticMethod(vanilla, "copy$runScript");
final Method vmExecuteOpcode = findStaticMethod(vanilla, "vmExecuteOpcode");
final Field scriptInstructions = findDeobField(inject, "opcodes");
final Field scriptStatePC = findDeobField(inject, "pc");
final Field currentScriptField = findObField(inject, "currentScript");
final Field currentScriptPCField = findObField(inject, "currentScriptPC");
Execution e = new Execution(inject.getVanilla());
e.addMethod(runScript);
e.noInvoke = true;
AtomicReference<MethodContext> pcontext = new AtomicReference<>(null);
e.addMethodContextVisitor(pcontext::set);
e.run();
Instructions instrs = runScript.getCode().getInstructions();
Set<AStore> scriptStores = new HashSet<>();
Integer pcLocalVar = null;
Integer instructionArrayLocalVar = null;
IStore currentOpcodeStore = null;
ALoad localInstructionLoad = null;
MethodContext methodContext = pcontext.get();
for (InstructionContext instrCtx : methodContext.getInstructionContexts())
{
Instruction instr = instrCtx.getInstruction();
if (instr instanceof AStore)
{
AStore store = (AStore) instr;
StackContext storedVarCtx = instrCtx.getPops().get(0);
// Find AStores that store a Script
if (storedVarCtx.getType().getInternalName().equals(scriptObName))
{
scriptStores.add(store);
}
// Find AStores that store the instructions
InstructionContext pusherCtx = storedVarCtx.getPushed();
if (pusherCtx.getInstruction() instanceof GetField)
{
GetField getField = (GetField) pusherCtx.getInstruction();
if (getField.getMyField().equals(scriptInstructions))
{
instructionArrayLocalVar = store.getVariableIndex();
}
}
}
// Find the local that invokedFromPc is set from
if (instr instanceof PutField)
{
PutField put = (PutField) instr;
if (put.getMyField() == scriptStatePC)
{
StackContext pc = instrCtx.getPops().get(0);
assert Type.INT.equals(pc.getType()) : pc.getType();
InstructionContext mulctx = pc.pushed;
assert mulctx.getInstruction() instanceof IMul;
pcLocalVar = mulctx.getPops().stream()
.map(StackContext::getPushed)
.filter(i -> i.getInstruction() instanceof ILoad)
.map(i -> ((ILoad) i.getInstruction()).getVariableIndex())
.findFirst()
.orElse(null);
}
}
}
// Find opcode load
// This has to run after the first loop because it relies on instructionArrayLocalVar being set
if (instructionArrayLocalVar == null)
{
throw new InjectionException("Unable to find local instruction array");
}
for (InstructionContext instrCtx : methodContext.getInstructionContexts())
{
Instruction instr = instrCtx.getInstruction();
if (instr instanceof IALoad)
{
StackContext array = instrCtx.getPops().get(1);
// Check where the array came from (looking for a getField scriptInstructions
InstructionContext pushedCtx = array.getPushed();
Instruction pushed = pushedCtx.getInstruction();
if (pushed instanceof ALoad)
{
ALoad arrayLoad = (ALoad) pushed;
if (arrayLoad.getVariableIndex() == instructionArrayLocalVar)
{
//Find the istore
IStore istore = (IStore) instrCtx.getPushes().get(0).getPopped().stream()
.map(InstructionContext::getInstruction)
.filter(i -> i instanceof IStore)
.findFirst()
.orElse(null);
if (istore != null)
{
currentOpcodeStore = istore;
localInstructionLoad = arrayLoad;
}
}
}
}
}
// Add PutStatics to all Script AStores
{
int outerSciptIdx = scriptStores.stream()
.mapToInt(AStore::getVariableIndex)
.reduce(Math::min)
.orElseThrow(() -> new InjectionException("Unable to find any Script AStores in runScript"));
log.debug("Found script index {}", outerSciptIdx);
ListIterator<Instruction> instrIter = instrs.getInstructions().listIterator();
while (instrIter.hasNext())
{
Instruction instr = instrIter.next();
if (instr instanceof AStore)
{
AStore il = (AStore) instr;
if (il.getVariableIndex() == outerSciptIdx)
{
instrIter.previous();
instrIter.add(new Dup(instrs));
instrIter.add(new PutStatic(instrs, currentScriptField));
instrIter.next();
}
}
}
}
// Add PutStatics to all PC IStores and IIncs
{
if (pcLocalVar == null)
{
throw new InjectionException("Unable to find ILoad for invokedFromPc IStore");
}
log.debug("Found pc index {}", pcLocalVar);
ListIterator<Instruction> instrIter = instrs.getInstructions().listIterator();
while (instrIter.hasNext())
{
Instruction instr = instrIter.next();
if (instr instanceof IStore)
{
IStore il = (IStore) instr;
if (il.getVariableIndex() == pcLocalVar)
{
instrIter.previous();
instrIter.add(new Dup(instrs));
instrIter.add(new PutStatic(instrs, currentScriptPCField));
instrIter.next();
}
}
if (instr instanceof IInc)
{
IInc iinc = (IInc) instr;
if (iinc.getVariableIndex() == pcLocalVar)
{
instrIter.add(new ILoad(instrs, pcLocalVar));
instrIter.add(new PutStatic(instrs, currentScriptPCField));
}
}
}
}
// Inject call to vmExecuteOpcode
log.debug("Found instruction array index {}", instructionArrayLocalVar);
if (currentOpcodeStore == null)
{
throw new InjectionException("Unable to find IStore for current opcode");
}
int istorepc = instrs.getInstructions().indexOf(currentOpcodeStore);
assert istorepc >= 0;
Label nextIteration = instrs.createLabelFor(localInstructionLoad);
instrs.addInstruction(istorepc + 1, new ILoad(instrs, currentOpcodeStore.getVariableIndex()));
instrs.addInstruction(istorepc + 2, new InvokeStatic(instrs, vmExecuteOpcode.getPoolMethod()));
instrs.addInstruction(istorepc + 3, new IfNe(instrs, nextIteration));
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Method;
import net.runelite.asm.signature.Signature;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import org.mockito.Matchers;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class InjectConstructTest
{
@Test
public void testInjectConstruct() throws Exception
{
ClassFile targetClass = new ClassFile();
targetClass.setName("test");
ClassFile vanillaClass = new ClassFile();
vanillaClass.setName("ab");
Method constructor = new Method(vanillaClass, "<init>", new Signature("()V"));
vanillaClass.addMethod(constructor);
Inject inject = mock(Inject.class);
when(inject.findVanillaForInterface(Matchers.any(Class.class)))
.thenReturn(vanillaClass);
InjectConstruct injectConstruct = new InjectConstruct(inject);
injectConstruct.injectConstruct(targetClass, APIClass.class.getDeclaredMethod("create"));
assertNotNull(targetClass.findMethod("create"));
}
interface APIClass
{
APIClass create();
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import net.runelite.asm.ClassFile;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import net.runelite.asm.Type;
import net.runelite.asm.attributes.Code;
import net.runelite.asm.attributes.code.Instruction;
import static net.runelite.asm.attributes.code.InstructionType.CHECKCAST;
import net.runelite.asm.attributes.code.Instructions;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class InjectSetterTest
{
@Test
public void testInjectSetterInt() throws NoSuchMethodException
{
Inject inject = mock(Inject.class);
when(inject.findImportMethodOnApi(any(Class.class), anyString(), any(Boolean.class)))
.thenReturn(APIClass.class.getDeclaredMethod("setTest", int.class));
when(inject.createLoadForTypeIndex(any(Instructions.class), any(Type.class), anyInt()))
.thenReturn(mock(Instruction.class));
InjectSetter instance = new InjectSetter(inject);
ClassFile targetClass = new ClassFile();
targetClass.setName("test");
Field field = new Field(targetClass, "test", Type.INT);
targetClass.addField(field);
instance.injectSetter(targetClass, APIClass.class, field, "test", null);
Method injectedMethod = targetClass.findMethod("setTest");
assertNotNull(injectedMethod);
Code code = injectedMethod.getCode();
Instructions instructions = code.getInstructions();
assertFalse(instructions.getInstructions().stream()
.filter(i -> i.getType() == CHECKCAST)
.findAny()
.isPresent());
}
@Test
public void testInjectSetterObject() throws NoSuchMethodException
{
Inject inject = mock(Inject.class);
when(inject.findImportMethodOnApi(any(Class.class), anyString(), any(Boolean.class)))
.thenReturn(APIClass.class.getDeclaredMethod("setTestObject", Object.class));
when(inject.createLoadForTypeIndex(any(Instructions.class), any(Type.class), anyInt()))
.thenReturn(mock(Instruction.class));
InjectSetter instance = new InjectSetter(inject);
ClassFile targetClass = new ClassFile();
targetClass.setName("test");
Field field = new Field(targetClass, "testObject", Type.STRING);
targetClass.addField(field);
instance.injectSetter(targetClass, APIClass.class, field, "testObject", null);
Method injectedMethod = targetClass.findMethod("setTestObject");
assertNotNull(injectedMethod);
Code code = injectedMethod.getCode();
Instructions instructions = code.getInstructions();
assertTrue(instructions.getInstructions().stream()
.filter(i -> i.getType() == CHECKCAST)
.findAny()
.isPresent());
}
interface APIClass
{
void setTest(int i);
void setTestObject(Object str);
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.io.File;
import java.io.IOException;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.DeobTestProperties;
import net.runelite.deob.TemporyFolderLocation;
import net.runelite.deob.util.JarUtil;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
public class InjectTest
{
@Rule
public DeobTestProperties properties = new DeobTestProperties();
@Rule
public TemporaryFolder folder = TemporyFolderLocation.getTemporaryFolder();
private ClassGroup deob, vanilla;
@Before
public void before() throws IOException
{
deob = JarUtil.loadJar(new File(properties.getRsClient()));
vanilla = JarUtil.loadJar(new File(properties.getVanillaClient()));
}
@After
public void after() throws IOException
{
JarUtil.saveJar(vanilla, folder.newFile());
}
@Test
@Ignore
public void testRun() throws InjectionException
{
Inject instance = new Inject(deob, vanilla);
instance.run();
InjectorValidator iv = new InjectorValidator(vanilla);
iv.validate();
}
}

View File

@@ -1,267 +0,0 @@
/*
* Copyright (c) 2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.runelite.api.mixins.Copy;
import net.runelite.api.mixins.Replace;
import net.runelite.api.mixins.Shadow;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.ClassUtil;
import net.runelite.asm.Field;
import net.runelite.asm.Method;
import static net.runelite.asm.Type.INT;
import net.runelite.asm.attributes.code.instructions.GetStatic;
import net.runelite.asm.attributes.code.instructions.InvokeVirtual;
import net.runelite.asm.attributes.code.instructions.LDC;
import net.runelite.asm.signature.Signature;
import net.runelite.mapping.ObfuscatedName;
import net.runelite.mapping.ObfuscatedSignature;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_STATIC;
@ObfuscatedName("net/runelite/injector/VanillaTarget")
class DeobTarget
{
@ObfuscatedName("ob_foo4")
private static int foo4;
@ObfuscatedName("ob_foo3")
@ObfuscatedSignature(
signature = "(I)V",
garbageValue = "123"
)
private void foo3()
{
// De-obfuscated foo3
System.out.println("foo3");
}
}
class VanillaTarget
{
private static int ob_foo4;
private void ob_foo3(int garbageValue)
{
// Obfuscated foo3
if (garbageValue != 123)
{
return;
}
System.out.println("foo3");
}
}
abstract class Source
{
@net.runelite.api.mixins.Inject
private static int foo;
@Shadow("foo4")
private static int foo4;
@net.runelite.api.mixins.Inject
private void foo2()
{
}
@Copy("foo3")
abstract void foo3();
@Replace("foo3")
private void rl$foo3()
{
System.out.println("replaced");
System.out.println(foo4);
foo3();
}
}
// Test shadowing the "foo" field injected by Source
abstract class Source2
{
@Shadow("foo")
private static int foo;
@net.runelite.api.mixins.Inject
private void foo5()
{
System.out.println(foo);
}
}
public class MixinInjectorTest
{
@Test
public void testInject() throws Exception
{
InputStream deobIn = getClass().getResourceAsStream("DeobTarget.class");
ClassFile deobTarget = ClassUtil.loadClass(deobIn);
ClassGroup deob = new ClassGroup();
deob.addClass(deobTarget);
InputStream vanillaIn = getClass().getResourceAsStream("VanillaTarget.class");
ClassFile vanillaTarget = ClassUtil.loadClass(vanillaIn);
ClassGroup vanilla = new ClassGroup();
vanilla.addClass(vanillaTarget);
Map<Class<?>, List<ClassFile>> mixinClasses = new HashMap<>();
mixinClasses.put(Source.class, Collections.singletonList(vanillaTarget));
mixinClasses.put(Source2.class, Collections.singletonList(vanillaTarget));
Inject inject = new Inject(deob, vanilla);
new MixinInjector(inject).inject(mixinClasses);
// Check if "foo" has been injected
Field foo = vanillaTarget.findField("foo");
assertNotNull(foo);
assertEquals(INT, foo.getType());
assertEquals(ACC_PUBLIC | ACC_STATIC, foo.getAccessFlags());
// Check if "foo2()V" has been injected
Method foo2 = vanillaTarget.findMethod("foo2");
assertNotNull(foo2);
assertEquals(new Signature("()V"), foo2.getDescriptor());
assertEquals(ACC_PUBLIC, foo2.getAccessFlags());
// Check if "ob_foo3(I)V" was copied
Method foo3 = vanillaTarget.findMethod("copy$foo3");
assertNotNull(foo3);
assertEquals(new Signature("(I)V"), foo3.getDescriptor());
assertEquals(ACC_PUBLIC, foo3.getAccessFlags());
// Check if "ob_foo3(I)V" was replaced
Method ob_foo3 = vanillaTarget.findMethod("ob_foo3");
assertNotNull(ob_foo3);
assertEquals(new Signature("(I)V"), ob_foo3.getDescriptor());
assertEquals(ob_foo3
.getCode()
.getInstructions()
.getInstructions()
.stream()
.filter(i -> i instanceof LDC && ((LDC) i).getConstant().equals("replaced"))
.count(), 1);
// Check that the "foo4" field access in the new code body was mapped correctly
assertEquals(ob_foo3
.getCode()
.getInstructions()
.getInstructions()
.stream()
.filter(i ->
{
if (!(i instanceof GetStatic))
{
return false;
}
net.runelite.asm.pool.Field field = ((GetStatic) i).getField();
if (!field.getClazz().getName().equals("net/runelite/injector/VanillaTarget"))
{
return false;
}
if (!field.getName().equals("ob_foo4"))
{
return false;
}
return true;
})
.count(), 1);
// Check that the "foo3()" call in the new code body was mapped to the copy
assertEquals(ob_foo3
.getCode()
.getInstructions()
.getInstructions()
.stream()
.filter(i ->
{
if (!(i instanceof InvokeVirtual))
{
return false;
}
net.runelite.asm.pool.Method method = ((InvokeVirtual) i).getMethod();
if (!method.getClazz().getName().equals("net/runelite/injector/VanillaTarget"))
{
return false;
}
if (!method.getName().equals("copy$foo3"))
{
return false;
}
return true;
})
.count(), 1);
// Check if "foo5()V" was injected
Method foo5 = vanillaTarget.findMethod("foo5");
assertNotNull(foo5);
assertEquals(new Signature("()V"), foo5.getDescriptor());
assertEquals(ACC_PUBLIC, foo5.getAccessFlags());
// Check that the shadow "foo" field access was mapped correctly
assertEquals(foo5
.getCode()
.getInstructions()
.getInstructions()
.stream()
.filter(i ->
{
if (!(i instanceof GetStatic))
{
return false;
}
net.runelite.asm.pool.Field field = ((GetStatic) i).getField();
if (!field.getClazz().getName().equals("net/runelite/injector/VanillaTarget"))
{
return false;
}
if (!field.getName().equals("foo"))
{
return false;
}
return true;
})
.count(), 1);
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.injector.raw;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.asm.ClassUtil;
import net.runelite.injector.Inject;
import org.junit.Test;
public class DrawAfterWidgetsTest
{
@Test
public void testInjectDrawWidgetsRev160() throws Exception
{
// Rev 160 does not have the drawWidgets call inlined
ClassFile deobClient = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Client_deob160.class"));
ClassFile deobRasterizer = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_deob160.class"));
ClassGroup deob = new ClassGroup();
deob.addClass(deobClient);
deob.addClass(deobRasterizer);
ClassFile obClient = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Client_ob160.class"));
ClassFile obRasterizer = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_ob160.class"));
ClassGroup vanilla = new ClassGroup();
vanilla.addClass(obClient);
vanilla.addClass(obRasterizer);
Inject inject = new Inject(deob, vanilla);
new DrawAfterWidgets(inject).inject();
}
@Test
public void testInjectDrawWidgetsRev180() throws Exception
{
// Rev 180 has the drawWidgets call inlined
ClassFile deobClient = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Client_deob180.class"));
ClassFile deobRasterizer = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_deob180.class"));
ClassGroup deob = new ClassGroup();
deob.addClass(deobClient);
deob.addClass(deobRasterizer);
ClassFile obClient = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Client_ob180.class"));
ClassFile obRasterizer = ClassUtil.loadClass(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_ob180.class"));
ClassGroup vanilla = new ClassGroup();
vanilla.addClass(obClient);
vanilla.addClass(obRasterizer);
Inject inject = new Inject(deob, vanilla);
new DrawAfterWidgets(inject).inject();
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(
{
ElementType.FIELD, ElementType.METHOD
})
public @interface Export
{
String value();
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(
{
ElementType.FIELD, ElementType.METHOD
})
public @interface Import
{
String value();
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright (c) 2016-2017, Adam <Adam@sigterm.info>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ObfuscatedGetter
{
int intValue() default 0;
long longValue() default 0L;
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (c) 2018, Lotto <https://github.com/devLotto>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.mapping;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Used to indicate a method can only be called from within mixins.
* Calling methods annotated with this annotation outside mixins results in a AbstractMethodError.
* Only works in net.runelite.rs.api.*
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Protect
{
}

View File

@@ -143,7 +143,5 @@ tasks {
shadowJar {
archiveClassifier.set("shaded")
exclude("net/runelite/injector/**")
}
}

View File

@@ -185,7 +185,6 @@ public class LootManager
final Tile tile = itemSpawned.getTile();
final LocalPoint location = tile.getLocalLocation();
final int packed = location.getSceneX() << 8 | location.getSceneY();
log.debug("storing items in {}", packed);
itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location));
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
}

View File

@@ -23,7 +23,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
group = "us.runelitepl.rs"
group = "com.openosrs.rs"
description = "RuneScape API"
dependencies {

View File

@@ -450,15 +450,15 @@ public interface RSClient extends RSGameShell, Client
void setIndexedSpritePalette(int[] indexedSpritePalette);
@Import("archive6")
RSAbstractArchive getMusicTracks();
RSArchive getMusicTracks();
@Import("archive8")
@Override
RSAbstractArchive getIndexSprites();
RSArchive getIndexSprites();
@Import("archive12")
@Override
RSAbstractArchive getIndexScripts();
RSArchive getIndexScripts();
@Import("widgetClickMasks")
@Override
@@ -1040,7 +1040,7 @@ public interface RSClient extends RSGameShell, Client
RSPcmStreamMixer getSoundEffectAudioQueue();
@Import("archive4")
RSAbstractArchive getIndexCache4();
RSArchive getIndexCache4();
@Import("decimator")
RSDecimator getSoundEffectResampler();

View File

@@ -446,7 +446,7 @@ public interface RSWidget extends Widget
@Import("getFont")
@Override
RSAbstractFont getFont();
RSFont getFont();
@Import("fill")
@Override

View File

@@ -5,7 +5,7 @@ import net.runelite.mapping.Import;
public interface RSWorldMapElement extends RSDualNode, MapElementConfig
{
@Import("getSprite")
@Import("getSpriteBool")
@Override
RSSprite getMapIcon(boolean var1);
}

View File

@@ -38,7 +38,6 @@ include(":deobfuscator")
include(":runelite-script-assembler-plugin")
include(":runelite-client")
include(":runelite-mixins")
include(":injector-plugin")
include(":injected-client")
include(":runelite-plugin-archetype")
include(":http-service")
@@ -47,7 +46,7 @@ include(":wiki-scraper")
for (project in rootProject.children) {
project.apply {
projectDir = file("$name")
projectDir = file(name)
buildFileName = "$name.gradle.kts"
require(projectDir.isDirectory) { "Project '${project.path} must have a $projectDir directory" }