ReplaceInstruction() public static method

Replaces the insturction with another by replacing the opcode and the operand. Unlike ILProcessor.Replace, preserves the references to the original instruction (which branches might use, for instance).
public static ReplaceInstruction ( Instruction original, Instruction newIns ) : void
original Mono.Cecil.Cil.Instruction The instruction to replace.
newIns Mono.Cecil.Cil.Instruction The instruction to replace with.
return void
        /// <summary>
        ///     Inject the call of the injection method into the target.
        /// </summary>
        /// <param name="startCode">The instruction from which to start injecting.</param>
        /// <param name="token">
        ///     If <see cref="InjectFlags.PassTag" /> is specified, the value of this parameter will be passed as a
        ///     parameter to the injection method.
        /// </param>
        /// <param name="direction">The direction in which to insert the call: either above the start code or below it.</param>
        public void Inject(Instruction startCode,
                           object token = null,
                           InjectDirection direction = InjectDirection.Before)
        {
            InjectValues flags = Flags.ToValues();

#if DEBUG
            Logger.LogLine(LogMask.Inject, "##### INJECTION START #####");
            Logger.LogLine(
                LogMask.Inject,
                $"Injecting a call to {InjectMethod.Module.Name}.{InjectMethod.Name} into {InjectTarget.Module.Name}.{InjectTarget.Name}.");
            Logger.LogLine(LogMask.Inject, "Patch parameters:");
            Logger.LogLine(LogMask.Inject, $"Pass tag: {flags.PassTag}");
            Logger.LogLine(LogMask.Inject, $"Modify return value: {flags.ModifyReturn}");
            Logger.LogLine(LogMask.Inject, $"Pass THIS: {flags.PassInvokingInstance}");
            Logger.LogLine(LogMask.Inject, $"Pass method locals: {flags.PassLocals}");
            Logger.LogLine(LogMask.Inject, $"Pass member fields: {flags.PassFields}");
            Logger.LogLine(LogMask.Inject, $"Pass member parameters: {flags.PassParameters}");
            if (flags.PassParameters)
            {
                Logger.LogLine(
                    LogMask.Inject,
                    $"Member parameters are passed by {(flags.PassParametersByRef ? "reference" : "value")}");
            }
#endif

            bool isVoid = InjectTarget.ReturnType.FullName == "System.Void";

            MethodReference hookRef = InjectTarget.Module.Import(InjectMethod);

            // If the hook is generic but not instantiated fully, attempt to fill in the generic arguments with the ones specified in the target method/class
            if (hookRef.HasGenericParameters &&
                (!hookRef.IsGenericInstance ||
                 hookRef.IsGenericInstance &&
                 ((GenericInstanceMethod)hookRef).GenericArguments.Count < hookRef.GenericParameters.Count))
            {
                GenericInstanceMethod genericInjectMethod = new GenericInstanceMethod(hookRef);
                foreach (GenericParameter genericParameter in InjectMethod.GenericParameters)
                {
                    List <GenericParameter> @params = new List <GenericParameter>();
                    @params.AddRange(InjectTarget.GenericParameters);
                    @params.AddRange(InjectTarget.DeclaringType.GenericParameters);
                    GenericParameter param = @params.FirstOrDefault(p => p.Name == genericParameter.Name);
                    if (param == null)
                    {
                        throw new
                              Exception("Could not find a suitable type to bind to the generic injection method. Try to manually instantiate the generic injection method before injecting.");
                    }
                    genericInjectMethod.GenericArguments.Add(param);
                }

                hookRef = genericInjectMethod;
            }

            MethodBody  targetBody = InjectTarget.Body;
            ILProcessor il         = targetBody.GetILProcessor();
            int         startIndex = targetBody.Instructions.IndexOf(startCode);
            if (startIndex == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(startCode));
            }
            Instruction startInstruction = startCode;

            if (direction == InjectDirection.Before && startIndex != 0)
            {
                Instruction oldIns = ILUtils.CopyInstruction(startCode);
                ILUtils.ReplaceInstruction(startCode, il.Create(OpCodes.Nop));
                Instruction ins = targetBody.Instructions[startIndex];
                il.InsertAfter(ins, oldIns);
                startInstruction = targetBody.Instructions[startIndex + 1];
            }
            else if (direction == InjectDirection.After)
            {
                il.InsertAfter(startCode, il.Create(OpCodes.Nop));
                startInstruction = targetBody.Instructions[startIndex + 1];
            }

            VariableDefinition returnDef = null;
            if (flags.ModifyReturn && !isVoid)
            {
                targetBody.InitLocals = true;
                returnDef             = new VariableDefinition(InjectTarget.ReturnType);
                targetBody.Variables.Add(returnDef);
            }

            if (flags.PassTag)
            {
                Logger.LogLine(LogMask.Inject, $"Passing custom token value: {token}");

                switch (flags.TagType)
                {
                case InjectValues.PassTagType.Int32:
                {
                    int tag = token as int? ?? 0;
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldc_I4, tag));
                }
                break;

                case InjectValues.PassTagType.String:
                {
                    string tag = token as string;
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldstr, tag));
                }
                break;

                case InjectValues.PassTagType.None: break;

                case InjectValues.PassTagType.Max: break;

                default: throw new ArgumentOutOfRangeException();
                }
            }

            if (flags.PassInvokingInstance)
            {
                Logger.LogLine(LogMask.Inject, "Passing THIS argument");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0));
            }

            if (flags.ModifyReturn && !isVoid)
            {
                Logger.LogLine(LogMask.Inject, "Passing return reference");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, returnDef));
            }

            if (flags.PassLocals)
            {
                Logger.LogLine(LogMask.Inject, "Passing local variable references");
                foreach (int i in LocalVarIDs)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing local variable index: {i}");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, (byte)i));
                }
            }

            if (flags.PassFields)
            {
                Logger.LogLine(LogMask.Inject, "Passing member field references");
                IEnumerable <FieldReference> memberRefs = MemberReferences.Select(t => t.Module.Import(t));
                foreach (FieldReference t in memberRefs)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing member field {t.FullName}");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0));
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldflda, t));
                }
            }

            if (flags.PassParameters)
            {
                Logger.LogLine(LogMask.Inject,
                               $"Passing member parameters by {(flags.PassParametersByRef ? "reference" : "value")}");
                int icr = Convert.ToInt32(!InjectTarget.IsStatic);
                for (int i = 0; i < ParameterCount; i++)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing parameter of index {i + icr}");
                    il.InsertBefore(startInstruction,
                                    flags.PassParametersByRef
                                            ? il.Create(OpCodes.Ldarga_S, (byte)(i + icr))
                                            : il.Create(OpCodes.Ldarg_S, (byte)(i + icr)));
                }
            }

            Logger.LogLine(LogMask.Inject, "Injecting the call to the method");
            il.InsertBefore(startInstruction, il.Create(OpCodes.Call, hookRef));
            if (flags.ModifyReturn)
            {
                Logger.LogLine(LogMask.Inject, "Inserting return check");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Brfalse_S, startInstruction));
                if (!isVoid)
                {
                    Logger.LogLine(LogMask.Inject, "Inserting return value");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloc_S, returnDef));
                }

                Logger.LogLine(LogMask.Inject, "Inserting return command");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ret));
            }
            // If we don't use the return value of InjectMethod, pop it from the ES
            else if (InjectMethod.ReturnType.FullName != "System.Void")
            {
                il.InsertBefore(startInstruction, il.Create(OpCodes.Pop));
            }

            if (direction == InjectDirection.After)
            {
                il.Remove(startInstruction);
            }
            Logger.LogLine(LogMask.Inject, "Injection complete");
            Logger.LogLine(LogMask.Inject, "##### INJECTION END #####");
        }
Esempio n. 2
0
        /// <summary>
        ///     Inject the call of the injection method into the target.
        /// </summary>
        /// <param name="startCode">The instruction from which to start injecting.</param>
        /// <param name="token">
        ///     If <see cref="InjectFlags.PassTag" /> is specified, the value of this parameter will be passed as a
        ///     parameter to the injection method.
        /// </param>
        /// <param name="direction">The direction in which to insert the call: either above the start code or below it.</param>
        public void Inject(Instruction startCode, int token = 0, InjectDirection direction = InjectDirection.Before)
        {
            InjectValues flags = Flags.ToValues();

#if DEBUG
            Logger.LogLine(LogMask.Inject, "##### INJECTION START #####");
            Logger.LogLine(
                LogMask.Inject,
                $"Injecting a call to {InjectMethod.Module.Name}.{InjectMethod.Name} into {InjectTarget.Module.Name}.{InjectTarget.Name}.");
            Logger.LogLine(LogMask.Inject, "Patch parameters:");
            Logger.LogLine(LogMask.Inject, $"Pass tag: {flags.PassTag}");
            Logger.LogLine(LogMask.Inject, $"Modify return value: {flags.ModifyReturn}");
            Logger.LogLine(LogMask.Inject, $"Pass THIS: {flags.PassInvokingInstance}");
            Logger.LogLine(LogMask.Inject, $"Pass method locals: {flags.PassLocals}");
            Logger.LogLine(LogMask.Inject, $"Pass member fields: {flags.PassFields}");
            Logger.LogLine(LogMask.Inject, $"Pass member parameters: {flags.PassParameters}");
            if (flags.PassParameters)
            {
                Logger.LogLine(
                    LogMask.Inject,
                    $"Member parameters are passed by {(flags.PassParametersByRef ? "reference" : "value")}");
            }
#endif

            bool isVoid = InjectTarget.ReturnType.FullName == "System.Void";

            MethodReference hookRef = InjectTarget.Module.Import(InjectMethod);

            MethodBody  targetBody = InjectTarget.Body;
            ILProcessor il         = targetBody.GetILProcessor();
            int         startIndex = targetBody.Instructions.IndexOf(startCode);
            if (startIndex == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(startCode));
            }
            Instruction startInstruction = startCode;

            if (direction == InjectDirection.Before && startIndex != 0)
            {
                Instruction oldIns = ILUtils.CopyInstruction(startCode);
                ILUtils.ReplaceInstruction(startCode, il.Create(OpCodes.Nop));
                Instruction ins = targetBody.Instructions[startIndex];
                il.InsertAfter(ins, oldIns);
                startInstruction = targetBody.Instructions[startIndex + 1];
            }
            else if (direction == InjectDirection.After)
            {
                il.InsertAfter(startCode, il.Create(OpCodes.Nop));
                startInstruction = targetBody.Instructions[startIndex + 1];
            }

            VariableDefinition returnDef = null;
            if (flags.ModifyReturn && !isVoid)
            {
                targetBody.InitLocals = true;
                returnDef             = new VariableDefinition(
                    InjectMethod.Name + "_return",
                    InjectTarget.ReturnType);
                targetBody.Variables.Add(returnDef);
            }


            if (flags.PassTag)
            {
                Logger.LogLine(LogMask.Inject, $"Passing custom token value: {token}");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ldc_I4, token));
            }
            if (flags.PassInvokingInstance)
            {
                Logger.LogLine(LogMask.Inject, "Passing THIS argument");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0));
            }
            if (flags.ModifyReturn && !isVoid)
            {
                Logger.LogLine(LogMask.Inject, "Passing return reference");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, returnDef));
            }
            if (flags.PassLocals)
            {
                Logger.LogLine(LogMask.Inject, "Passing local variable references");
                foreach (int i in LocalVarIDs)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing local variable index: {i}");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloca_S, (byte)i));
                }
            }
            if (flags.PassFields)
            {
                Logger.LogLine(LogMask.Inject, "Passing member field references");
                IEnumerable <FieldReference> memberRefs = MemberReferences.Select(t => t.Module.Import(t));
                foreach (FieldReference t in memberRefs)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing member field {t.FullName}");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldarg_0));
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldflda, t));
                }
            }
            if (flags.PassParameters)
            {
                Logger.LogLine(
                    LogMask.Inject,
                    $"Passing member parameters by {(flags.PassParametersByRef ? "reference" : "value")}");
                int icr = Convert.ToInt32(!InjectTarget.IsStatic);
                for (int i = 0; i < _ParameterCount; i++)
                {
                    Logger.LogLine(LogMask.Inject, $"Passing parameter of index {(i + icr)}");
                    il.InsertBefore(
                        startInstruction,
                        flags.PassParametersByRef
                    ? il.Create(OpCodes.Ldarga_S, (byte)(i + icr)) : il.Create(OpCodes.Ldarg_S, (byte)(i + icr)));
                }
            }
            Logger.LogLine(LogMask.Inject, "Injecting the call to the method");
            il.InsertBefore(startInstruction, il.Create(OpCodes.Call, hookRef));
            if (flags.ModifyReturn)
            {
                Logger.LogLine(LogMask.Inject, "Inserting return check");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Brfalse_S, startInstruction));
                if (!isVoid)
                {
                    Logger.LogLine(LogMask.Inject, "Inserting return value");
                    il.InsertBefore(startInstruction, il.Create(OpCodes.Ldloc_S, returnDef));
                }
                Logger.LogLine(LogMask.Inject, "Inserting return command");
                il.InsertBefore(startInstruction, il.Create(OpCodes.Ret));
            }
            // If we don't use the return value of InjectMethod, pop it from the ES
            else if (InjectMethod.ReturnType.FullName != "System.Void")
            {
                il.InsertBefore(startInstruction, il.Create(OpCodes.Pop));
            }
            if (direction == InjectDirection.After)
            {
                il.Remove(startInstruction);
            }
            Logger.LogLine(LogMask.Inject, "Injection complete");
            Logger.LogLine(LogMask.Inject, "##### INJECTION END #####");
        }