This commit is contained in:
TheRealNull
2020-12-19 17:52:08 -05:00
parent b6f47eb581
commit 473fe2fa43
93 changed files with 1600 additions and 1296 deletions

View File

@@ -40,7 +40,7 @@ dependencies {
implementation("com.google.guava:guava:29.0-jre")
implementation(project(":deobfuscator"))
testCompileOnly("com.openosrs:injection-annotations:1.0")
testCompileOnly(project(":injection-annotations"))
testImplementation("junit:junit:4.13")
}

View File

@@ -62,11 +62,10 @@ public interface InjectUtil
/**
* Finds a static method in deob and converts it to ob
*
* @param data InjectData instance
* @param name The name of the method you want to find
* @param data InjectData instance
* @param name The name of the method you want to find
* @param classHint The name of the class you expect the method to be in, or null
* @param sig The signature the method has in deob, or null
*
* @param sig The signature the method has in deob, or null
* @return The obfuscated version of the found method
*/
static Method findMethod(
@@ -81,8 +80,8 @@ public interface InjectUtil
/**
* Finds a method in injectData's deobfuscated classes.
*
* @param data InjectData instance
* @param name (Exported) method name
* @param data InjectData instance
* @param name (Exported) method name
* @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure
* @throws InjectException If the hint class couldn't be found, or no method matching the settings was found
*/
@@ -90,7 +89,7 @@ public interface InjectUtil
InjectData data,
String name,
@Nullable String classHint)
throws InjectException
throws InjectException
{
return findMethod(data, name, classHint, null, false, false);
}
@@ -98,13 +97,12 @@ public interface InjectUtil
/**
* Finds a method in injectData's deobfuscated classes.
*
* @param data InjectData instance
* @param name (Exported) method name
* @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure
* @param sig The deobfuscated methods' signature, or null, if you're unsure
* @param notStatic If this is true, only check non-static methods. If classHint isn't null, check only subclasses
* @param data InjectData instance
* @param name (Exported) method name
* @param classHint The (exported) name of a class you expect (or know) the method to be in, or null, if you're not sure
* @param sig The deobfuscated methods' signature, or null, if you're unsure
* @param notStatic If this is true, only check non-static methods. If classHint isn't null, check only subclasses
* @param returnDeob If this is true, this method will return the deobfuscated method, instead of turning it into vanilla first
*
* @throws InjectException If the hint class couldn't be found, or no method matching the settings was found
*/
static Method findMethod(
@@ -114,7 +112,7 @@ public interface InjectUtil
@Nullable Predicate<Signature> sig,
boolean notStatic,
boolean returnDeob)
throws InjectException
throws InjectException
{
final ClassGroup deob = data.getDeobfuscated();
if (classHint != null)
@@ -125,23 +123,41 @@ public interface InjectUtil
if (notStatic)
{
if (sig == null)
{
m = InjectUtil.findMethodDeep(cf, name, s -> true);
}
else
{
m = InjectUtil.findMethodDeep(cf, name, sig);
}
}
else
{
m = cf.findMethod(name);
}
if (m != null)
{
return returnDeob ? m : data.toVanilla(m);
}
}
for (ClassFile cf : deob)
{
for (Method m : cf.getMethods())
{
if (m.getName().equals(name))
{
if (!notStatic || !m.isStatic())
{
if (sig == null || sig.test(m.getDescriptor()))
{
return returnDeob ? m : data.toVanilla(m);
}
}
}
}
}
throw new InjectException(String.format("Couldn't find %s", name));
}
@@ -150,7 +166,9 @@ public interface InjectUtil
{
ClassFile clazz = group.findClass(name);
if (clazz == null)
{
throw new InjectException("Hint class " + name + " doesn't exist");
}
return clazz;
}
@@ -161,10 +179,18 @@ public interface InjectUtil
static Method findMethodDeep(ClassFile clazz, String name, Predicate<Signature> type)
{
do
{
for (Method method : clazz.getMethods())
{
if (method.getName().equals(name))
{
if (type.test(method.getDescriptor()))
{
return method;
}
}
}
}
while ((clazz = clazz.getParent()) != null);
throw new InjectException(String.format("Method %s couldn't be found", name + type.toString()));
@@ -172,7 +198,7 @@ public interface InjectUtil
/**
* Fail-fast implementation of ClassGroup.findStaticField
*
* <p>
* well...
*/
static Field findStaticField(ClassGroup group, String name)
@@ -181,7 +207,9 @@ public interface InjectUtil
{
Field f = clazz.findField(name);
if (f != null && f.isStatic())
{
return f;
}
}
throw new InjectException("Couldn't find static field " + name);
@@ -190,11 +218,10 @@ public interface InjectUtil
/**
* Finds a static field in deob and converts it to ob
*
* @param data InjectData instance
* @param name The name of the field you want to find
* @param data InjectData instance
* @param name The name of the field you want to find
* @param classHint The name of the class you expect the field to be in, or null
* @param type The type the method has in deob, or null
*
* @param type The type the method has in deob, or null
* @return The obfuscated version of the found field
*/
static Field findStaticField(InjectData data, String name, String classHint, Type type)
@@ -207,23 +234,35 @@ public interface InjectUtil
ClassFile clazz = findClassOrThrow(deob, classHint);
if (type == null)
{
field = clazz.findField(name);
}
else
{
field = clazz.findField(name, type);
}
if (field != null)
{
return field;
}
}
for (ClassFile clazz : deob)
{
if (type == null)
{
field = clazz.findField(name);
}
else
{
field = clazz.findField(name, type);
}
if (field != null)
{
return field;
}
}
throw new InjectException(String.format("Static field %s doesn't exist", (type != null ? type + " " : "") + name));
@@ -237,8 +276,12 @@ public interface InjectUtil
Field f;
do
{
if ((f = clazz.findField(name)) != null)
{
return f;
}
}
while ((clazz = clazz.getParent()) != null);
throw new InjectException("Couldn't find field " + name);
@@ -259,12 +302,18 @@ public interface InjectUtil
field = clazz.findField(name);
if (field != null)
{
return field;
}
}
for (ClassFile clazz : group)
{
if ((field = clazz.findField(name)) != null)
{
return field;
}
}
throw new InjectException("Field " + name + " doesn't exist");
}
@@ -293,11 +342,15 @@ public interface InjectUtil
static Type apiToDeob(InjectData data, Type api)
{
if (api.isPrimitive())
{
return api;
}
final String internalName = api.getInternalName();
if (internalName.startsWith(API_BASE))
{
return Type.getType("L" + api.getInternalName().substring(API_BASE.length()) + ";", api.getDimensions());
}
else if (internalName.startsWith(RL_API_BASE))
{
Class rlApiC = new Class(internalName);
@@ -332,11 +385,15 @@ public interface InjectUtil
static Type deobToVanilla(InjectData data, Type deobT)
{
if (deobT.isPrimitive())
{
return deobT;
}
final ClassFile deobClass = data.getDeobfuscated().findClass(deobT.getInternalName());
if (deobClass == null)
{
return deobT;
}
return Type.getType("L" + data.toVanilla(deobClass).getName() + ";", deobT.getDimensions());
}
@@ -352,18 +409,24 @@ public interface InjectUtil
List<Type> bb = b.getArguments();
if (aa.size() != bb.size())
{
return false;
}
for (int i = 0; i < aa.size(); i++)
{
if (!aa.get(i).equals(bb.get(i)))
{
return false;
}
}
return true;
}
/**
* Gets the obfuscated name from something's annotations.
*
* <p>
* If the annotation doesn't exist return the current name instead.
*/
static <T extends Annotated & Named> String getObfuscatedName(T from)
@@ -387,7 +450,9 @@ public interface InjectUtil
static Instruction createLoadForTypeIndex(Instructions instructions, Type type, int index)
{
if (type.getDimensions() > 0 || !type.isPrimitive())
{
return new ALoad(instructions, index);
}
switch (type.toString())
{
@@ -414,7 +479,9 @@ public interface InjectUtil
static Instruction createReturnForType(Instructions instructions, Type type)
{
if (!type.isPrimitive())
{
return new Return(instructions, InstructionType.ARETURN);
}
switch (type.toString())
{
@@ -440,9 +507,13 @@ public interface InjectUtil
static Instruction createInvokeFor(Instructions instructions, net.runelite.asm.pool.Method method, boolean isStatic)
{
if (isStatic)
{
return new InvokeStatic(instructions, method);
}
else
{
return new InvokeVirtual(instructions, method);
}
}
/**
@@ -463,9 +534,13 @@ public interface InjectUtil
into.accept(new LDC(instrs, getter));
if (getter instanceof Integer)
{
into.accept(new IMul(instrs));
}
else if (getter instanceof Long)
{
into.accept(new LMul(instrs));
}
}
/**

View File

@@ -67,7 +67,7 @@ public class Injection extends InjectData implements InjectTaskHandler
inject(new RSApiInjector(this));
inject(new DrawAfterWidgets(this));
//inject(new DrawAfterWidgets(this));
inject(new ScriptVM(this));
@@ -107,7 +107,9 @@ public class Injection extends InjectData implements InjectTaskHandler
log.lifecycle("{} {}", name, injector.getCompletionMsg());
if (injector instanceof Validator)
{
validate((Validator) injector);
}
}
private void validate(Validator validator)

View File

@@ -35,7 +35,9 @@ public class InjectorValidator implements Validator
for (Class intf : cf.getInterfaces())
{
if (!intf.getName().startsWith(API_BASE))
{
continue;
}
RSApiClass apiC = rsApi.findClass(intf.getName());
if (apiC == null)
@@ -61,7 +63,9 @@ public class InjectorValidator implements Validator
for (RSApiMethod apiMethod : apiClass)
{
if (apiMethod.isSynthetic() || apiMethod.isDefault())
{
continue;
}
if (clazz.findMethodDeep(apiMethod.getName(), apiMethod.getSignature()) == null)
{

View File

@@ -149,7 +149,7 @@ public abstract class InjectData
/**
* Do something with all paired classes.
*
* <p>
* Key = deobfuscated, Value = vanilla
*/
public void forEachPair(BiConsumer<ClassFile, ClassFile> action)

View File

@@ -42,38 +42,40 @@ import net.runelite.deob.DeobAnnotations;
public class CreateAnnotations extends AbstractInjector
{
public CreateAnnotations(InjectData inject)
{
super(inject);
}
public CreateAnnotations(InjectData inject)
{
super(inject);
}
public void inject()
{
for (final ClassFile deobClass : inject.getDeobfuscated())
{
injectFields(deobClass);
injectMethods(deobClass);
public void inject()
{
for (final ClassFile deobClass : inject.getDeobfuscated())
{
injectFields(deobClass);
injectMethods(deobClass);
if (deobClass.getName().startsWith("class"))
continue;
if (deobClass.getName().startsWith("class"))
{
continue;
}
deobClass.addAnnotation(DeobAnnotations.IMPLEMENTS, deobClass.getName());
}
}
deobClass.addAnnotation(DeobAnnotations.IMPLEMENTS, deobClass.getName());
}
}
private void injectFields(ClassFile deobClass)
{
for (Field deobField : deobClass.getFields())
{
deobField.addAnnotation(DeobAnnotations.EXPORT, deobField.getName());
}
}
private void injectFields(ClassFile deobClass)
{
for (Field deobField : deobClass.getFields())
{
deobField.addAnnotation(DeobAnnotations.EXPORT, deobField.getName());
}
}
private void injectMethods(ClassFile deobClass)
{
for (Method deobMethod : deobClass.getMethods())
{
deobMethod.addAnnotation(DeobAnnotations.EXPORT, deobMethod.getName());
}
}
private void injectMethods(ClassFile deobClass)
{
for (Method deobMethod : deobClass.getMethods())
{
deobMethod.addAnnotation(DeobAnnotations.EXPORT, deobMethod.getName());
}
}
}

View File

@@ -69,7 +69,9 @@ public class InjectConstruct extends AbstractInjector
{
Annotation construct = apiMethod.findAnnotation(CONSTRUCT);
if (construct == null)
{
continue;
}
final Method method = apiMethod.getMethod();
final Class clazz = method.getClazz();
@@ -103,7 +105,9 @@ public class InjectConstruct extends AbstractInjector
final net.runelite.asm.Method constructor = classToConstruct.findMethod("<init>", constr);
if (constructor == null)
{
throw new InjectException("Unable to find constructor for " + classToConstruct.getName() + ".<init>" + constr);
}
net.runelite.asm.Method setterMethod = new net.runelite.asm.Method(targetClass, apiMethod.getName(), apiMethod.getType());

View File

@@ -86,7 +86,9 @@ public class InjectHook extends AbstractInjector
public void inject()
{
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> entry : mixinTargets.entrySet())
{
injectMethods(entry.getKey(), entry.getValue());
}
injectHooks();
@@ -103,7 +105,9 @@ public class InjectHook extends AbstractInjector
{
final Annotation fieldHook = mixinMethod.findAnnotation(FIELDHOOK);
if (fieldHook == null)
{
continue;
}
final String hookName = fieldHook.getValueString();
final boolean before = isBefore(fieldHook);
@@ -141,7 +145,7 @@ public class InjectHook extends AbstractInjector
Set<Instruction> doneIh = new HashSet<>();
e.addExecutionVisitor((
InstructionContext ic) ->
InstructionContext ic) ->
{
Instruction i = ic.getInstruction();
Instructions ins = i.getInstructions();
@@ -149,23 +153,33 @@ public class InjectHook extends AbstractInjector
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;
}
log.trace("Found injection location for hook {} at instruction {}", hookInfo.method.getName(), sfi);
++injectedHooks;
@@ -174,7 +188,9 @@ public class InjectHook extends AbstractInjector
StackContext objectStackContext = null;
if (sfi instanceof PutField)
{
objectStackContext = ic.getPops().get(1);
}
int idx = ins.getInstructions().indexOf(sfi);
assert idx != -1;
@@ -182,10 +198,14 @@ public class InjectHook extends AbstractInjector
try
{
if (hookInfo.before)
{
injectCallbackBefore(ins, idx, hookInfo, null, objectStackContext, value);
}
else
// idx + 1 to insert after the set
// idx + 1 to insert after the set
{
injectCallback(ins, idx + 1, hookInfo, null, objectStackContext);
}
}
catch (InjectException ex)
{
@@ -206,23 +226,33 @@ public class InjectHook extends AbstractInjector
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;
}
StackContext value = ic.getPops().get(0);
StackContext index = ic.getPops().get(1);
@@ -232,7 +262,9 @@ public class InjectHook extends AbstractInjector
StackContext objectStackContext = null;
if (arrayReferencePushed.getInstruction().getType() == InstructionType.GETFIELD)
{
objectStackContext = arrayReferencePushed.getPops().get(0);
}
// inject hook after 'i'
log.debug("[DEBUG] Found array injection location for hook {} at instruction {}", hookInfo.method.getName(), i);
@@ -244,9 +276,13 @@ public class InjectHook extends AbstractInjector
try
{
if (hookInfo.before)
{
injectCallbackBefore(ins, idx, hookInfo, index, objectStackContext, value);
}
else
{
injectCallback(ins, idx + 1, hookInfo, index, objectStackContext);
}
}
catch (InjectException ex)
{
@@ -265,7 +301,9 @@ public class InjectHook extends AbstractInjector
if (!hookInfo.method.isStatic())
{
if (object == null)
{
throw new InjectException("null object");
}
ins.getInstructions().add(idx++, new Dup(ins)); // dup value
idx = recursivelyPush(ins, idx, object);
@@ -313,7 +351,9 @@ public class InjectHook extends AbstractInjector
}
for (StackContext s : Lists.reverse(ctx.getPops()))
{
idx = recursivelyPush(ins, idx, s);
}
ins.getInstructions().add(idx++, ctx.getInstruction().clone());
return idx;
@@ -324,15 +364,21 @@ public class InjectHook extends AbstractInjector
if (!hookInfo.method.isStatic())
{
if (objectPusher == null)
{
throw new InjectException("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));
}
Instruction invoke = hookInfo.getInvoke(ins);
ins.getInstructions().add(idx, invoke);

View File

@@ -65,7 +65,9 @@ public class InjectHookMethod extends AbstractInjector
public void inject()
{
for (Map.Entry<Provider<ClassFile>, List<ClassFile>> entry : mixinTargets.entrySet())
{
injectMethods(entry.getKey(), entry.getValue());
}
log.info("[INFO] Injected {} method hooks", injected);
}
@@ -80,10 +82,14 @@ public class InjectHookMethod extends AbstractInjector
{
final Annotation methodHook = mixinMethod.findAnnotation(METHODHOOK);
if (methodHook == null)
{
continue;
}
if (!mixinMethod.getDescriptor().isVoid())
{
throw new InjectException("Method hook " + mixinMethod.getPoolMethod() + " doesn't have void return type");
}
final String hookName = methodHook.getValueString();
final boolean end = isEnd(methodHook);
@@ -116,8 +122,12 @@ public class InjectHookMethod extends AbstractInjector
{
it = ins.listIterator(ins.size());
while (it.hasPrevious())
{
if (it.previous() instanceof ReturnInstruction)
{
insertVoke(method, hookMethod, it);
}
}
return;
}
@@ -126,8 +136,12 @@ public class InjectHookMethod extends AbstractInjector
if (method.getName().equals("<init>"))
{
while (it.hasNext())
{
if (it.next() instanceof InvokeSpecial)
{
break;
}
}
assert it.hasNext() : "Constructor without invokespecial";
}
@@ -141,7 +155,9 @@ public class InjectHookMethod extends AbstractInjector
int varIdx = 0;
if (!method.isStatic())
{
iterator.add(new ALoad(instructions, varIdx++));
}
for (Type type : hookMethod.getType().getArguments())
{

View File

@@ -36,7 +36,9 @@ public class InterfaceInjector extends AbstractInjector
final String impls = DeobAnnotations.getImplements(deobCf);
if (impls == null)
{
return;
}
final String fullName = API_BASE + impls;
if (!inject.getRsApi().hasClass(fullName))

View File

@@ -179,13 +179,19 @@ public class MixinInjector extends AbstractInjector
copy.setValue(field.getValue());
for (Map.Entry<Type, Annotation> e : field.getAnnotations().entrySet())
{
if (!e.getKey().toString().startsWith("Lnet/runelite/api/mixins"))
{
copy.addAnnotation(e.getValue());
}
}
targetClass.addField(copy);
if (injectedFields.containsKey(field.getName()) && !ASSERTION_FIELD.equals(field.getName()))
{
throw new InjectException("Duplicate field: " + field.getName());
}
injectedFields.put(field.getName(), copy);
}
@@ -207,10 +213,14 @@ public class MixinInjector extends AbstractInjector
Annotation shadow = field.findAnnotation(SHADOW);
if (shadow == null)
{
continue;
}
if (!field.isStatic())
{
throw new InjectException("Shadowed fields must be static");
}
String shadowed = shadow.getValueString();
@@ -226,7 +236,9 @@ public class MixinInjector extends AbstractInjector
}
if ((targetField.getAccessFlags() & Opcodes.ACC_PRIVATE) != 0)
{
throw new InjectException("Shadowed fields can't be private");
}
shadowFields.put(
field.getPoolField(),
@@ -249,7 +261,9 @@ public class MixinInjector extends AbstractInjector
{
Annotation copyA = mixinMethod.findAnnotation(COPY);
if (copyA == null)
{
continue;
}
String copiedName = copyA.getValueString();
Signature deobSig = InjectUtil.apiToDeob(inject, mixinMethod.getDescriptor());
@@ -258,13 +272,17 @@ public class MixinInjector extends AbstractInjector
Method deobSourceMethod = InjectUtil.findMethod(inject, copiedName, inject.toDeob(targetClass.getName()).getName(), deobSig::equals, notStat, true);
if (mixinMethod.isStatic() != deobSourceMethod.isStatic())
{
throw new InjectException("Mixin method " + mixinMethod + " should be " + (deobSourceMethod.isStatic() ? "static" : "non-static"));
}
// The actual method we're copying, including code etc
Method sourceMethod = inject.toVanilla(deobSourceMethod);
if (mixinMethod.getDescriptor().size() > sourceMethod.getDescriptor().size())
{
throw new InjectException("Mixin methods cannot have more parameters than their corresponding ob method");
}
Method copy = new Method(targetClass, "copy$" + copiedName, sourceMethod.getDescriptor());
moveCode(copy, sourceMethod.getCode());
@@ -272,7 +290,9 @@ public class MixinInjector extends AbstractInjector
copy.setPublic();
copy.getExceptions().getExceptions().addAll(sourceMethod.getExceptions().getExceptions());
for (var a : sourceMethod.getAnnotations().values())
{
copy.addAnnotation(a);
}
targetClass.addMethod(copy);
++copied;
@@ -280,7 +300,7 @@ public class MixinInjector extends AbstractInjector
* 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 = mixinMethod.getDescriptor().size() != sourceMethod.getDescriptor().size()
&& deobSourceMethod.getDescriptor().size() < copy.getDescriptor().size();
@@ -298,7 +318,9 @@ public class MixinInjector extends AbstractInjector
if ((hasInject && isInit) || isClinit)
{
if (!"()V".equals(mixinMethod.getDescriptor().toString()))
{
throw new InjectException("Injected constructors cannot have arguments");
}
Method[] originalMethods = targetClass.getMethods().stream()
.filter(m -> m.getName().equals(mixinMethod.getName()))
@@ -309,11 +331,15 @@ public class MixinInjector extends AbstractInjector
// If there isn't a <clinit> already just inject ours, otherwise rename it
// This is always true for <init>
if (originalMethods.length > 0)
{
name = "rl$$" + (isInit ? "init" : "clinit");
}
String numberlessName = name;
for (int i = 1; targetClass.findMethod(name, mixinMethod.getDescriptor()) != null; i++)
{
name = numberlessName + i;
}
Method copy = new Method(targetClass, name, mixinMethod.getDescriptor());
moveCode(copy, mixinMethod.getCode());
@@ -337,7 +363,9 @@ public class MixinInjector extends AbstractInjector
int pops = invoke.getMethod().getType().getArguments().size() + 1;
for (int i = 0; i < pops; i++)
{
listIter.add(new Pop(instructions));
}
break;
}
@@ -503,11 +531,15 @@ public class MixinInjector extends AbstractInjector
// Update instructions for each instruction
for (Instruction i : newCode.getInstructions())
{
i.setInstructions(newCode.getInstructions());
}
newCode.getExceptions().getExceptions().addAll(sourceCode.getExceptions().getExceptions());
for (net.runelite.asm.attributes.code.Exception e : newCode.getExceptions().getExceptions())
{
e.setExceptions(newCode.getExceptions());
}
targetMethod.setCode(newCode);
}
@@ -598,7 +630,9 @@ public class MixinInjector extends AbstractInjector
PushConstantInstruction pi = (PushConstantInstruction) i;
if (mixinCf.getPoolClass().equals(pi.getConstant()))
{
pi.setConstant(cf.getPoolClass());
}
}
verify(mixinCf, i);
@@ -614,11 +648,15 @@ public class MixinInjector extends AbstractInjector
if (fi.getField().getClazz().getName().equals(mixinCf.getName()))
{
if (i instanceof PutField || i instanceof GetField)
{
throw new InjectException("Access to non static member field of mixin");
}
Field field = fi.getMyField();
if (field != null && !field.isPublic())
{
throw new InjectException("Static access to non public field " + field);
}
}
}
else if (i instanceof InvokeStatic)
@@ -627,12 +665,16 @@ public class MixinInjector extends AbstractInjector
if (is.getMethod().getClazz() != mixinCf.getPoolClass()
&& is.getMethod().getClazz().getName().startsWith(MIXIN_BASE))
{
throw new InjectException("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{}
// RS classes don't verify under java 7+ due to the
// super() invokespecial being inside of a try{}
{
throw new InjectException("Injected bytecode must be Java 6 compatible");
}
}
private Method findDeobMatching(ClassFile deobClass, Method mixinMethod, String deobName)
@@ -642,18 +684,26 @@ public class MixinInjector extends AbstractInjector
for (Method method : deobClass.getMethods())
{
if (!deobName.equals(method.getName()))
{
continue;
}
if (InjectUtil.apiToDeobSigEquals(inject, method.getDescriptor(), mixinMethod.getDescriptor()))
{
matching.add(method);
}
}
if (matching.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
// 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 InjectException("There are several matching methods when there should only be one");
}
else if (matching.size() == 1)
{
return matching.get(0);
}
return inject.getDeobfuscated().findStaticMethod(deobName);
}
@@ -695,15 +745,17 @@ public class MixinInjector extends AbstractInjector
@Value
private static class CopiedMethod
{
@Nonnull Method copy;
@Nullable Integer garbage;
@Nonnull
Method copy;
@Nullable Integer garbage;
}
@Value
private static class ShadowField
{
@Nonnull Field targetField;
@Nullable Number obfuscatedGetter;
@Nonnull
Field targetField;
@Nullable Number obfuscatedGetter;
}
private static Provider<ClassFile> mixinProvider(ClassFile mixin)
@@ -716,7 +768,9 @@ public class MixinInjector extends AbstractInjector
public ClassFile get()
{
if (bytes != null)
{
return JarUtil.loadClass(bytes);
}
bytes = JarUtil.writeClass(mixin.getGroup(), mixin);
return mixin;

View File

@@ -84,7 +84,9 @@ public class RSApiInjector extends AbstractInjector
final List<RSApiMethod> matching = findImportsFor(deobField, deobField.isStatic(), implementingClass);
if (matching == null)
{
continue;
}
final Type deobType = deobField.getType();
@@ -153,7 +155,9 @@ public class RSApiInjector extends AbstractInjector
final Number getter = DeobAnnotations.getObfuscatedGetter(deobField);
if (deobField.isStatic() != vanillaField.isStatic()) // Can this even happen
{
throw new InjectException("Something went horribly wrong, and this should honestly never happen, but you never know. Btw it's the static-ness");
}
inject(matching, deobField, vanillaField, getter);
}
@@ -166,7 +170,9 @@ public class RSApiInjector extends AbstractInjector
final List<RSApiMethod> matching = findImportsFor(deobMethod, deobMethod.isStatic(), implementingClass);
if (matching == null)
{
continue;
}
final Signature deobSig = deobMethod.getDescriptor();
@@ -211,7 +217,9 @@ public class RSApiInjector extends AbstractInjector
apiMethod.setInjected(true);
}
else if (matching.size() != 0)
{
throw new InjectException("Multiple api imports matching method " + deobMethod.getPoolMethod());
}
}
}
@@ -225,7 +233,9 @@ public class RSApiInjector extends AbstractInjector
matched.removeIf(RSApiMethod::isInjected);
if (matched.size() > 2)
{
throw new InjectException("More than 2 imported api methods for field " + deobField.getPoolField());
}
final Field vanillaField = inject.toVanilla(deobField);
final Number getter = DeobAnnotations.getObfuscatedGetter(deobField);
@@ -238,7 +248,9 @@ public class RSApiInjector extends AbstractInjector
{
final String exportedName = InjectUtil.getExportedName(object);
if (exportedName == null)
{
return null;
}
final List<RSApiMethod> matching = new ArrayList<>();

View File

@@ -271,10 +271,12 @@ public class DrawAfterWidgets extends AbstractInjector
for (Field f : c.getFields())
{
if (f.isStatic())
{
if (f.getName().equals(s))
{
return inject.toVanilla(f);
}
}
}
}
return null;
@@ -287,10 +289,12 @@ public class DrawAfterWidgets extends AbstractInjector
for (Field f : c.getFields())
{
if (!f.isStatic())
{
if (f.getName().equals(s))
{
return f;
}
}
}
}
return null;

View File

@@ -87,7 +87,9 @@ public class DrawMenu extends AbstractInjector
{
Instruction instruction = ic.getInstruction();
if (!(instruction instanceof GetStatic))
{
continue;
}
if (((GetStatic) instruction).getField().equals(isMenuOpen))
{
@@ -100,25 +102,35 @@ public class DrawMenu extends AbstractInjector
// If the popper is a IfNe the label it's pointing to is the drawMenu one and topLeft is directly after it
// else it's the other way around, obviously
if (isMenuOpenPopI instanceof IfNe)
{
injectInvokeAfter = ((IfNe) isMenuOpenPopI).getTo();
}
else
{
injectInvokeAfter = isMenuOpenPopI;
}
}
else if (((GetStatic) instruction).getField().equals(gameDrawMode))
{
List<Instruction> instrL = instruction.getInstructions().getInstructions();
for (int i = instrL.indexOf(instruction); !(instruction instanceof Label); i--)
{
instruction = instrL.get(i);
}
labelToJumpTo = (Label) instruction;
}
if (injectInvokeAfter != null && labelToJumpTo != null)
{
break;
}
}
if (injectInvokeAfter == null || labelToJumpTo == null)
{
throw new InjectException("Couldn't find the right location for DrawMenu to inject");
}
final Instructions instrs = mc.getMethod().getCode().getInstructions();
int idx = instrs.getInstructions().indexOf(injectInvokeAfter);

View File

@@ -49,12 +49,16 @@ public class Occluder extends AbstractInjector
Instruction i = it.next();
if (!(i instanceof BiPush))
{
continue;
}
boolean shouldChange = (byte) ((BiPush) i).getConstant() == OLDVALUE;
if (!shouldChange)
{
continue;
}
replaced++;
@@ -64,6 +68,8 @@ public class Occluder extends AbstractInjector
}
if (replaced != 10)
{
throw new InjectException("Only found " + replaced + " 25's to replace in occlude instead of expected 10");
}
}
}

View File

@@ -74,7 +74,9 @@ public class RasterizerAlpha extends AbstractInjector
{
Instructions instrs = getInstrs(mc);
if (instrs == null)
{
return;
}
int count = 0;
int orCount = 0;
@@ -84,14 +86,18 @@ public class RasterizerAlpha extends AbstractInjector
{
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);
@@ -111,15 +117,21 @@ public class RasterizerAlpha extends AbstractInjector
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();
@@ -168,12 +180,16 @@ public class RasterizerAlpha extends AbstractInjector
// If we're copying from the same field we don't have to apply extra alpha again
if (colPushI instanceof IALoad
&& isSameField(r2dPx, colPusher.getPops().get(1)))
{
continue;
}
// If the value is 0, it's supposed to be transparent, not black
if (colPushI instanceof PushConstantInstruction
&& ((PushConstantInstruction) colPushI).getConstant().equals(0))
{
continue;
}
// rasterPx[idx] = color | 0xff000000 (the | 0xff000000 is what's added)
int storeIdx = instrs.getInstructions().indexOf(instruction);
@@ -217,7 +233,9 @@ public class RasterizerAlpha extends AbstractInjector
{
Code c = mc.getMethod().getCode();
if (c == null)
{
return null;
}
return c.getInstructions();
}
@@ -226,11 +244,15 @@ public class RasterizerAlpha extends AbstractInjector
{
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();
@@ -245,7 +267,9 @@ public class RasterizerAlpha extends AbstractInjector
InstructionContext ic = resolveFieldThroughInvokes(array);
if (ic == null)
{
return false;
}
return ((GetFieldInstruction) ic.getInstruction()).getMyField() == f;
}

View File

@@ -64,6 +64,8 @@ public class RenderDraw extends AbstractInjector
}
if (replaced != EXPECTED)
{
throw new InjectException("Didn't replace the expected amount of method calls");
}
}
}

View File

@@ -50,7 +50,9 @@ public class InjectGetter
public static void inject(ClassFile targetClass, RSApiMethod apiMethod, Field field, Number getter)
{
if (targetClass.findMethod(apiMethod.getName(), apiMethod.getSignature()) != null)
{
throw new InjectException("Duplicate getter method " + apiMethod.getMethod().toString());
}
final String name = apiMethod.getName();
final Signature sig = apiMethod.getSignature();
@@ -75,7 +77,9 @@ public class InjectGetter
}
if (getter != null)
{
InjectUtil.injectObfuscatedGetter(getter, instructions, ins::add);
}
ins.add(InjectUtil.createReturnForType(instructions, field.getType()));

View File

@@ -53,7 +53,9 @@ public class InjectSetter
public static void inject(ClassFile targetClass, RSApiMethod apiMethod, Field field, Number getter)
{
if (targetClass.findMethod(apiMethod.getName(), apiMethod.getSignature()) != null)
{
throw new InjectException("Duplicate setter method " + apiMethod.getMethod().toString());
}
final String name = apiMethod.getName();
final Signature sig = apiMethod.getSignature();
@@ -69,7 +71,9 @@ public class InjectSetter
// load this
if (!field.isStatic())
{
ins.add(new ALoad(instructions, 0));
}
// load argument
final Type argumentType = sig.getTypeOfArg(0);
@@ -85,12 +89,18 @@ public class InjectSetter
}
if (getter != null)
{
InjectUtil.injectObfuscatedSetter(getter, instructions, ins::add);
}
if (field.isStatic())
{
ins.add(new PutStatic(instructions, field));
}
else
{
ins.add(new PutField(instructions, field));
}
ins.add(new VReturn(instructions));

View File

@@ -45,7 +45,9 @@ public class RSApi implements Iterable<RSApiClass>
for (File file : classes)
{
if (!file.getName().startsWith("RS"))
{
continue;
}
try (InputStream is = new FileInputStream(file))
{
@@ -72,7 +74,9 @@ public class RSApi implements Iterable<RSApiClass>
final ImmutableMap.Builder<String, RSApiClass> builder = ImmutableMap.builder();
for (RSApiClass clazz : this)
{
builder.put(clazz.getName(), clazz);
}
this.map = builder.build();
@@ -112,11 +116,17 @@ public class RSApi implements Iterable<RSApiClass>
{
RSApiClass clazz = findClass(interf.getName());
if (clazz != null)
{
return clazz;
}
for (RSApiClass apiC : this)
{
if (apiC.getInterfaces().contains(interf))
{
return apiC;
}
}
return null;
}

View File

@@ -46,7 +46,9 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
for (RSApiMethod method : this)
{
if (method.isSynthetic())
{
continue;
}
if (method.findAnnotation(CONSTRUCT) != null)
{
@@ -56,10 +58,12 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
final Annotation imported = method.findAnnotation(IMPORT);
if (imported != null)
{
imports.computeIfAbsent(
imported.getValueString(),
(str) -> new ArrayList<>()
).add(method);
}
}
}
@@ -79,7 +83,9 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
{
List<RSApiMethod> imported = imports.get(str);
if (imported == null)
{
return;
}
to.addAll(imported);
}
@@ -95,7 +101,9 @@ public class RSApiClass extends ClassVisitor implements Iterable<RSApiMethod>
clazz = new Class(name);
for (String s : interfaces)
{
this.interfaces.add(new Class(s));
}
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)

View File

@@ -9,9 +9,7 @@ package com.openosrs.injector.rsapi;
import java.util.HashMap;
import java.util.Map;
import lombok.Data;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.asm.Annotation;
import net.runelite.asm.Named;

View File

@@ -16,7 +16,7 @@ import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
@CacheableTask
abstract class Inject: DefaultTask() {
abstract class Inject : DefaultTask() {
@get:Nested
abstract val extension: InjectExtension

View File

@@ -15,10 +15,13 @@ import org.gradle.api.tasks.PathSensitivity
interface InjectExtension {
@get:[InputFile PathSensitive(PathSensitivity.NONE)]
val vanilla: RegularFileProperty
@get:[InputFile PathSensitive(PathSensitivity.NONE)]
val rsclient: RegularFileProperty
@get:[InputFile PathSensitive(PathSensitivity.NONE)]
val mixins: RegularFileProperty
@get:[InputFile PathSensitive(PathSensitivity.NONE)]
val rsapi: RegularFileProperty
}

View File

@@ -10,19 +10,21 @@ package com.openosrs.injector
import org.gradle.api.Plugin
import org.gradle.api.Project
class InjectPlugin: Plugin<Project> {
override fun apply(project: Project) { with(project) {
val task = tasks.create("inject", Inject::class.java)
task.output.convention { file("$buildDir/libs/$name-$version.jar") }
class InjectPlugin : Plugin<Project> {
override fun apply(project: Project) {
with(project) {
val task = tasks.create("inject", Inject::class.java)
task.output.convention { file("$buildDir/libs/$name-$version.jar") }
artifacts {
it.add("runtimeElements", task.output)
artifacts {
it.add("runtimeElements", task.output)
}
tasks.getByName("assemble") {
it.finalizedBy("inject")
}
extensions.add(InjectExtension::class.java, "injector", task.extension)
}
tasks.getByName("assemble") {
it.finalizedBy("inject")
}
extensions.add(InjectExtension::class.java, "injector", task.extension)
}}
}
}

View File

@@ -283,12 +283,15 @@ public class MixinInjectorTest
Instruction i;
while (it.hasNext() &&
!((i = it.next()) instanceof GetStatic &&
((GetStatic) i).getField().getName().equals(gottenField)));
((GetStatic) i).getField().getName().equals(gottenField)))
{
;
}
return
(i = it.next()) instanceof LDC &&
((LDC) i).getConstantAsInt() == 1157381415 &&
it.next() instanceof IMul;
((LDC) i).getConstantAsInt() == 1157381415 &&
it.next() instanceof IMul;
}
private static ClassFile loadClass(Class<?> clazz)

View File

@@ -1,83 +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 com.openosrs.injector.injectors.raw;
import com.google.common.io.ByteStreams;
import com.openosrs.injector.TestInjection;
import com.openosrs.injector.injection.InjectData;
import com.openosrs.injector.rsapi.RSApi;
import net.runelite.asm.ClassFile;
import net.runelite.asm.ClassGroup;
import net.runelite.deob.util.JarUtil;
import org.junit.Test;
public class DrawAfterWidgetsTest
{
@Test
public void testInjectDrawWidgetsRev160() throws Exception
{
// Rev 160 does not have the drawWidgets call inlined
ClassFile deobClient = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Client_deob160.class")));
ClassFile deobRasterizer = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_deob160.class")));
ClassGroup deob = new ClassGroup();
deob.addClass(deobClient);
deob.addClass(deobRasterizer);
ClassFile obClient = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Client_ob160.class")));
ClassFile obRasterizer = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_ob160.class")));
ClassGroup vanilla = new ClassGroup();
vanilla.addClass(obClient);
vanilla.addClass(obRasterizer);
InjectData inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi());
new DrawAfterWidgets(inject).inject();
}
@Test
public void testInjectDrawWidgetsRev180() throws Exception
{
// Rev 180 has the drawWidgets call inlined
ClassFile deobClient = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Client_deob180.class")));
ClassFile deobRasterizer = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_deob180.class")));
ClassGroup deob = new ClassGroup();
deob.addClass(deobClient);
deob.addClass(deobRasterizer);
ClassFile obClient = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Client_ob180.class")));
ClassFile obRasterizer = JarUtil.loadClass(ByteStreams.toByteArray(getClass().getResourceAsStream("/drawafterwidgets/Rasterizer2D_ob180.class")));
ClassGroup vanilla = new ClassGroup();
vanilla.addClass(obClient);
vanilla.addClass(obRasterizer);
InjectData inject = new TestInjection(vanilla, deob, new ClassGroup(), new RSApi());
new DrawAfterWidgets(inject).inject();
}
}

View File

@@ -36,9 +36,11 @@ public class SourceChangerTest
new ClassReader(PACKAGE + "OldName").accept(vann, ClassReader.SKIP_FRAMES);
new SourceChanger(
new InjectData(new ClassGroup(), new ClassGroup(), null, null) {
new InjectData(new ClassGroup(), new ClassGroup(), null, null)
{
public void runChildInjector(Injector injector) throws InjectException
{}
{
}
@Override
public void forEachPair(BiConsumer<ClassFile, ClassFile> consumer)
@@ -54,8 +56,8 @@ public class SourceChangerTest
vann.getClassFile().accept(cw);
OldName obj = (OldName) MethodHandles.privateLookupIn(
NewName.class,
MethodHandles.lookup())
NewName.class,
MethodHandles.lookup())
.defineClass(cw.toByteArray())
.getDeclaredConstructor()
.newInstance();