Search in sources :

Example 1 with PutField

use of net.runelite.asm.attributes.code.instructions.PutField in project runelite by runelite.

the class InjectHook method run.

public 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;
        Instruction objectInstruction = new AConstNull(ins);
        StackContext objectStackContext = null;
        if (sfi instanceof PutField) {
            // Object being set on
            StackContext objectStack = ic.getPops().get(1);
            objectStackContext = objectStack;
        }
        int idx = ins.getInstructions().indexOf(sfi);
        assert idx != -1;
        try {
            // 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;
        // assume this is always at index 1
        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) {
            StackContext objectReference = arrayReferencePushed.getPops().get(0);
            objectStackContext = objectReference;
        }
        // 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 {
            injectCallback(ins, idx + 1, hookInfo, index, objectStackContext);
        } catch (InjectionException ex) {
            throw new RuntimeException(ex);
        }
    });
    e.run();
}
Also used : InstructionContext(net.runelite.asm.execution.InstructionContext) SetFieldInstruction(net.runelite.asm.attributes.code.instruction.types.SetFieldInstruction) Instructions(net.runelite.asm.attributes.code.Instructions) AConstNull(net.runelite.asm.attributes.code.instructions.AConstNull) Method(net.runelite.asm.Method) DupInstruction(net.runelite.asm.attributes.code.instruction.types.DupInstruction) SetFieldInstruction(net.runelite.asm.attributes.code.instruction.types.SetFieldInstruction) Instruction(net.runelite.asm.attributes.code.Instruction) ArrayStore(net.runelite.asm.attributes.code.instructions.ArrayStore) Code(net.runelite.asm.attributes.Code) Field(net.runelite.asm.Field) PutField(net.runelite.asm.attributes.code.instructions.PutField) Execution(net.runelite.asm.execution.Execution) StackContext(net.runelite.asm.execution.StackContext) HashSet(java.util.HashSet) PutField(net.runelite.asm.attributes.code.instructions.PutField)

Example 2 with PutField

use of net.runelite.asm.attributes.code.instructions.PutField in project runelite by runelite.

the class InjectSetter method injectSetter.

/**
 * 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
 * @param setter
 */
public 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));
}
Also used : Instructions(net.runelite.asm.attributes.code.Instructions) LDC(net.runelite.asm.attributes.code.instructions.LDC) Method(net.runelite.asm.Method) CheckCast(net.runelite.asm.attributes.code.instructions.CheckCast) Instruction(net.runelite.asm.attributes.code.Instruction) PutStatic(net.runelite.asm.attributes.code.instructions.PutStatic) Code(net.runelite.asm.attributes.Code) VReturn(net.runelite.asm.attributes.code.instructions.VReturn) Type(net.runelite.asm.Type) Signature(net.runelite.asm.signature.Signature) IMul(net.runelite.asm.attributes.code.instructions.IMul) LMul(net.runelite.asm.attributes.code.instructions.LMul) ALoad(net.runelite.asm.attributes.code.instructions.ALoad) PutField(net.runelite.asm.attributes.code.instructions.PutField)

Example 3 with PutField

use of net.runelite.asm.attributes.code.instructions.PutField in project runelite by runelite.

the class ScriptVM method injectScriptVMHooks.

private void injectScriptVMHooks() throws InjectionException {
    /*
			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 ScriptState::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

		 */
    String scriptObName = DeobAnnotations.getObfuscatedName(inject.getDeobfuscated().findClass("Script").getAnnotations());
    Method runScript = findObMethod("copy$runScript");
    Method vmExecuteOpcode = findObMethod("vmExecuteOpcode");
    Field scriptInstructions = findDeobField("instructions");
    Field scriptStatePC = findDeobField("invokedFromPc");
    Field currentScriptField = findObField("currentScript");
    Field currentScriptPCField = findObField("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);
            }
        }
    }
    // 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));
}
Also used : ListIterator(java.util.ListIterator) GetField(net.runelite.asm.attributes.code.instructions.GetField) IMul(net.runelite.asm.attributes.code.instructions.IMul) LoggerFactory(org.slf4j.LoggerFactory) Dup(net.runelite.asm.attributes.code.instructions.Dup) PutStatic(net.runelite.asm.attributes.code.instructions.PutStatic) ALoad(net.runelite.asm.attributes.code.instructions.ALoad) Inject(net.runelite.injector.Inject) AtomicReference(java.util.concurrent.atomic.AtomicReference) StackContext(net.runelite.asm.execution.StackContext) HashSet(java.util.HashSet) Method(net.runelite.asm.Method) ILoad(net.runelite.asm.attributes.code.instructions.ILoad) IStore(net.runelite.asm.attributes.code.instructions.IStore) IALoad(net.runelite.asm.attributes.code.instructions.IALoad) DeobAnnotations(net.runelite.deob.DeobAnnotations) Logger(org.slf4j.Logger) AStore(net.runelite.asm.attributes.code.instructions.AStore) Field(net.runelite.asm.Field) Set(java.util.Set) Type(net.runelite.asm.Type) InstructionContext(net.runelite.asm.execution.InstructionContext) InvokeStatic(net.runelite.asm.attributes.code.instructions.InvokeStatic) PutField(net.runelite.asm.attributes.code.instructions.PutField) Execution(net.runelite.asm.execution.Execution) ClassFile(net.runelite.asm.ClassFile) Label(net.runelite.asm.attributes.code.Label) MethodContext(net.runelite.asm.execution.MethodContext) Instructions(net.runelite.asm.attributes.code.Instructions) IInc(net.runelite.asm.attributes.code.instructions.IInc) InjectionException(net.runelite.injector.InjectionException) Instruction(net.runelite.asm.attributes.code.Instruction) IfNe(net.runelite.asm.attributes.code.instructions.IfNe) IStore(net.runelite.asm.attributes.code.instructions.IStore) Label(net.runelite.asm.attributes.code.Label) Instruction(net.runelite.asm.attributes.code.Instruction) InjectionException(net.runelite.injector.InjectionException) GetField(net.runelite.asm.attributes.code.instructions.GetField) Field(net.runelite.asm.Field) PutField(net.runelite.asm.attributes.code.instructions.PutField) Execution(net.runelite.asm.execution.Execution) AStore(net.runelite.asm.attributes.code.instructions.AStore) IALoad(net.runelite.asm.attributes.code.instructions.IALoad) IMul(net.runelite.asm.attributes.code.instructions.IMul) Dup(net.runelite.asm.attributes.code.instructions.Dup) HashSet(java.util.HashSet) PutField(net.runelite.asm.attributes.code.instructions.PutField) InstructionContext(net.runelite.asm.execution.InstructionContext) GetField(net.runelite.asm.attributes.code.instructions.GetField) ILoad(net.runelite.asm.attributes.code.instructions.ILoad) MethodContext(net.runelite.asm.execution.MethodContext) AtomicReference(java.util.concurrent.atomic.AtomicReference) Instructions(net.runelite.asm.attributes.code.Instructions) Method(net.runelite.asm.Method) ListIterator(java.util.ListIterator) PutStatic(net.runelite.asm.attributes.code.instructions.PutStatic) StackContext(net.runelite.asm.execution.StackContext) IfNe(net.runelite.asm.attributes.code.instructions.IfNe) IInc(net.runelite.asm.attributes.code.instructions.IInc) ALoad(net.runelite.asm.attributes.code.instructions.ALoad) IALoad(net.runelite.asm.attributes.code.instructions.IALoad) InvokeStatic(net.runelite.asm.attributes.code.instructions.InvokeStatic)

Aggregations

Method (net.runelite.asm.Method)3 Instruction (net.runelite.asm.attributes.code.Instruction)3 Instructions (net.runelite.asm.attributes.code.Instructions)3 PutField (net.runelite.asm.attributes.code.instructions.PutField)3 HashSet (java.util.HashSet)2 Field (net.runelite.asm.Field)2 Type (net.runelite.asm.Type)2 Code (net.runelite.asm.attributes.Code)2 ALoad (net.runelite.asm.attributes.code.instructions.ALoad)2 IMul (net.runelite.asm.attributes.code.instructions.IMul)2 PutStatic (net.runelite.asm.attributes.code.instructions.PutStatic)2 Execution (net.runelite.asm.execution.Execution)2 InstructionContext (net.runelite.asm.execution.InstructionContext)2 StackContext (net.runelite.asm.execution.StackContext)2 ListIterator (java.util.ListIterator)1 Set (java.util.Set)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1 ClassFile (net.runelite.asm.ClassFile)1 Label (net.runelite.asm.attributes.code.Label)1 DupInstruction (net.runelite.asm.attributes.code.instruction.types.DupInstruction)1