use of net.runelite.asm.signature.Signature in project runelite by runelite.
the class InjectHookMethod method injectHookMethod.
private void injectHookMethod(Annotation hook, Method deobMethod, Method vanillaMethod) throws InjectionException {
String hookName = hook.getElement().getString();
Instructions instructions = vanillaMethod.getCode().getInstructions();
Signature.Builder builder = new Signature.Builder().setReturnType(// Hooks always return void
Type.VOID);
for (Type type : deobMethod.getDescriptor().getArguments()) {
builder.addArgument(inject.deobfuscatedTypeToApiType(type));
}
assert deobMethod.isStatic() == vanillaMethod.isStatic();
if (!deobMethod.isStatic()) {
// Add variable to signature
builder.addArgument(0, inject.deobfuscatedTypeToApiType(new Type(deobMethod.getClassFile().getName())));
}
Signature signature = builder.build();
List<Integer> insertIndexes = findHookLocations(hook, vanillaMethod);
insertIndexes.sort((a, b) -> Integer.compare(b, a));
for (int insertPos : insertIndexes) {
if (!deobMethod.isStatic()) {
instructions.addInstruction(insertPos++, new ALoad(instructions, 0));
}
// current variable index
int index = deobMethod.isStatic() ? 0 : 1;
for (int i = index; i < signature.size(); ++i) {
Type type = signature.getTypeOfArg(i);
Instruction load = inject.createLoadForTypeIndex(instructions, type, index);
instructions.addInstruction(insertPos++, load);
index += type.getSize();
}
// Invoke callback
InvokeStatic invoke = new InvokeStatic(instructions, new net.runelite.asm.pool.Method(new net.runelite.asm.pool.Class(HOOKS), hookName, signature));
instructions.addInstruction(insertPos++, invoke);
}
logger.info("Injected method hook {} in {} with {} args: {}", hookName, vanillaMethod, signature.size(), signature.getArguments());
}
use of net.runelite.asm.signature.Signature in project runelite by runelite.
the class Renamer method renameClass.
private void renameClass(ClassGroup group, ClassFile cf, String name) {
for (ClassFile c : group.getClasses()) {
// rename on child interfaces and classes
renameClass(c, cf, name);
for (Method method : c.getMethods()) {
// rename on instructions. this includes method calls and field accesses.
if (method.getCode() != null) {
Code code = method.getCode();
// rename on instructions
for (Instruction i : code.getInstructions().getInstructions()) {
i.renameClass(cf.getName(), name);
}
// rename on exception handlers
Exceptions exceptions = code.getExceptions();
exceptions.renameClass(cf, name);
}
// rename on parameters
Signature.Builder builder = new Signature.Builder();
Signature signature = method.getDescriptor();
for (int i = 0; i < signature.size(); ++i) {
Type type = signature.getTypeOfArg(i);
if (type.getInternalName().equals(cf.getName())) {
builder.addArgument(Type.getType("L" + name + ";", type.getDimensions()));
} else {
builder.addArgument(type);
}
}
// rename return type
if (signature.getReturnValue().getInternalName().equals(cf.getName())) {
builder.setReturnType(Type.getType("L" + name + ";", signature.getReturnValue().getDimensions()));
} else {
builder.setReturnType(signature.getReturnValue());
}
Signature newSignature = builder.build();
if (!method.getDescriptor().equals(newSignature)) {
// Signature was updated. Annotate it
if (method.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE) == null) {
// Signature was not previously renamed
method.getAnnotations().addAnnotation(DeobAnnotations.OBFUSCATED_SIGNATURE, "signature", method.getDescriptor().toString());
}
}
method.setDescriptor(newSignature);
// rename on exceptions thrown
if (method.getExceptions() != null) {
method.getExceptions().renameClass(cf, name);
}
}
// rename on fields
for (Field field : c.getFields()) {
if (field.getType().getInternalName().equals(cf.getName())) {
if (field.getAnnotations().find(DeobAnnotations.OBFUSCATED_SIGNATURE) == null) {
// Signature was updated. Annotate it
field.getAnnotations().addAnnotation(DeobAnnotations.OBFUSCATED_SIGNATURE, "signature", field.getType().toString());
}
field.setType(Type.getType("L" + name + ";", field.getType().getDimensions()));
}
}
}
if (cf.getAnnotations().find(DeobAnnotations.OBFUSCATED_NAME) == null) {
cf.getAnnotations().addAnnotation(DeobAnnotations.OBFUSCATED_NAME, "value", cf.getName());
}
group.renameClass(cf, name);
}
use of net.runelite.asm.signature.Signature in project runelite by runelite.
the class UnusedParameters method removeParameter.
public void removeParameter(ClassGroup group, List<Method> methods, Signature signature, Execution execution, int paramIndex, int lvtIndex) {
int slots = signature.getTypeOfArg(paramIndex).getSize();
for (ClassFile cf : group.getClasses()) {
for (Method m : cf.getMethods()) {
Code c = m.getCode();
if (c == null) {
continue;
}
for (Instruction i : new ArrayList<>(c.getInstructions().getInstructions())) {
if (!(i instanceof InvokeInstruction)) {
continue;
}
InvokeInstruction ii = (InvokeInstruction) i;
if (!ii.getMethods().stream().anyMatch(me -> methods.contains(me))) {
continue;
}
// remove parameter from instruction
ii.removeParameter(paramIndex);
Collection<InstructionContext> ics = invokes.get(i);
assert ics != null;
if (ics != null) {
for (InstructionContext ins : ics) {
// index from top of stack of parameter. 0 is the last parameter
int pops = signature.size() - paramIndex - 1;
StackContext sctx = ins.getPops().get(pops);
if (sctx.getPushed().getInstruction().getInstructions() == null) {
continue;
}
// remove parameter from stack
ins.removeStack(pops);
}
}
}
}
}
for (Method method : methods) {
if (method.getCode() != null) // adjust lvt indexes to get rid of idx in the method
{
for (Instruction ins : method.getCode().getInstructions().getInstructions()) {
if (ins instanceof LVTInstruction) {
LVTInstruction lins = (LVTInstruction) ins;
int i = lins.getVariableIndex();
// current unused variable detection just looks for no accesses
assert i != lvtIndex;
// reassign
if (i > lvtIndex) {
assert i > 0;
assert i >= lvtIndex + slots;
Instruction newIns = lins.setVariableIndex(i - slots);
assert ins == newIns;
}
}
}
}
}
for (Method method : methods) {
method.getDescriptor().remove(paramIndex);
}
}
use of net.runelite.asm.signature.Signature in project runelite by runelite.
the class ExprArgOrder method hash.
private static void hash(Method method, MessageDigest sha256, InstructionContext ic) {
Instruction i = ic.getInstruction();
// this relies on all push constants are converted to ldc..
sha256.update((byte) i.getType().getCode());
if (i instanceof PushConstantInstruction) {
PushConstantInstruction pci = (PushConstantInstruction) i;
Object constant = pci.getConstant();
if (constant instanceof Number) {
long l = ((Number) constant).longValue();
sha256.update(Longs.toByteArray(l));
}
} else if (i instanceof LVTInstruction) {
int idx = ((LVTInstruction) i).getVariableIndex();
Signature signature = method.getDescriptor();
int lvt = method.isStatic() ? 0 : 1;
for (Type type : signature.getArguments()) {
if (idx == lvt) {
// Accessing a method parameter
sha256.update(Ints.toByteArray(idx));
break;
}
lvt += type.getSize();
}
}
for (StackContext sctx : ic.getPops()) {
hash(method, sha256, sctx.getPushed());
}
}
use of net.runelite.asm.signature.Signature in project runelite by runelite.
the class MixinInjector method injectMethods.
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();
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"));
}
// 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 = inject.toObClass(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);
}
}
}
Aggregations