コード例 #1
0
 public static void InjectWith(this MethodDefinition method,
                               MethodDefinition injectionMethod,
                               int codeOffset,
                               int tag,
                               InjectFlags flags,
                               InjectDirection dir,
                               int[] localsID,
                               FieldDefinition[] typeFields)
 {
     method.InjectWith(injectionMethod, codeOffset, (object)tag, flags, dir, localsID, typeFields);
 }
コード例 #2
0
 /// <summary>
 ///     Inject a hook call into this method.
 /// </summary>
 /// <param name="method">This method that is used as a target.</param>
 /// <param name="injectionMethod">The method the call of which to inject.</param>
 /// <param name="codeOffset">
 ///     The index of the instruction from which to start injecting. If positive, will count from the
 ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
 ///     and 0 is the first.
 /// </param>
 /// <param name="tag">
 ///     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="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
 /// <param name="dir">The direction in which to insert the call: either above the start code or below it.</param>
 /// <param name="localsID">
 ///     An array of indicies of local variables to pass to the injection method. Used only if
 ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
 /// </param>
 /// <param name="typeFields">
 ///     An array of class fields from the type the target lies in to pass to the injection method.
 ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
 /// </param>
 public static void InjectWith(this MethodDefinition method,
                               MethodDefinition injectionMethod,
                               int codeOffset = 0,
                               int tag = 0,
                               InjectFlags flags = InjectFlags.None,
                               InjectDirection dir = InjectDirection.Before,
                               int[] localsID = null,
                               FieldDefinition[] typeFields = null)
 {
     InjectionDefinition id = new InjectionDefinition(method, injectionMethod, flags, localsID, typeFields);
     id.Inject(codeOffset, tag, dir);
 }
コード例 #3
0
        /// <summary>
        ///     Inject a hook call into this method.
        /// </summary>
        /// <param name="method">This method that is used as a target.</param>
        /// <param name="injectionMethod">The method the call of which to inject.</param>
        /// <param name="codeOffset">
        ///     The index of the instruction from which to start injecting. If positive, will count from the
        ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
        ///     and 0 is the first.
        /// </param>
        /// <param name="tag">
        ///     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="flags">Injection flags that specify what values to pass to the injection method and how to inject it.</param>
        /// <param name="dir">The direction in which to insert the call: either above the start code or below it.</param>
        /// <param name="localsID">
        ///     An array of indicies of local variables to pass to the injection method. Used only if
        ///     <see cref="InjectFlags.PassLocals" /> is specified, otherwise ignored.
        /// </param>
        /// <param name="typeFields">
        ///     An array of class fields from the type the target lies in to pass to the injection method.
        ///     Used only if <see cref="InjectFlags.PassFields" /> is specified, otherwise ignored.
        /// </param>
        public static void InjectWith(this MethodDefinition method,
                                      MethodDefinition injectionMethod,
                                      int codeOffset               = 0,
                                      object tag                   = null,
                                      InjectFlags flags            = InjectFlags.None,
                                      InjectDirection dir          = InjectDirection.Before,
                                      int[] localsID               = null,
                                      FieldDefinition[] typeFields = null)
        {
            InjectionDefinition id = new InjectionDefinition(method, injectionMethod, flags, localsID, typeFields);

            id.Inject(codeOffset, tag, dir);
        }
コード例 #4
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);

            // 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(
                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 #####");
        }
コード例 #5
0
 /// <summary>
 ///     Inject the call of the injection method into the target.
 /// </summary>
 /// <param name="startCode">
 ///     The index of the instruction from which to start injecting. If positive, will count from the
 ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
 ///     and 0 is the first.
 /// </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(int startCode = 0, int token = 0, InjectDirection direction = InjectDirection.Before)
 {
     startCode = startCode < 0 ? InjectTarget.Body.Instructions.Count + startCode : startCode;
     Inject(InjectTarget.Body.Instructions[startCode], token, direction);
 }
コード例 #6
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,
                           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 #####");
        }
コード例 #7
0
 public void Inject(Instruction startCode, int token, InjectDirection direction)
 {
     Inject(startCode, (object)token, direction);
 }
コード例 #8
0
 /// <summary>
 ///     Inject the call of the injection method into the target.
 /// </summary>
 /// <param name="startCode">
 ///     The index of the instruction from which to start injecting. If positive, will count from the
 ///     beginning of the method. If negative, will count from the end. For instance, -1 is the method's last instruction
 ///     and 0 is the first.
 /// </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(int startCode = 0, object token = null, InjectDirection direction = InjectDirection.Before)
 {
     startCode = startCode < 0 ? InjectTarget.Body.Instructions.Count + startCode : startCode;
     Inject(InjectTarget.Body.Instructions[startCode], token, direction);
 }
コード例 #9
0
        /// <summary>
        /// Injects like <see cref="Mono.Cecil.Inject.InjectionDefinition"/>, with less safety measures
        /// and injects a value from a local compiler class' field
        /// </summary>
        /// <returns>The modified self parameter.</returns>
        /// <param name="self">Self.</param>
        /// <param name="injectionMethod">The method to inject.</param>
        /// <param name="callLoc">The instruction position to inject at.</param>
        /// <param name="localClass">The Local class value whose field is to be injecting from.</param>
        /// <param name="fieldInClass">The field reference in local class.</param>
        /// <param name="f">The injection flags.</param>
        /// <param name="localsID">The other local value ids.</param>
        /// <param name="typeFields">The type fields.</param>
        public static MethodDefinition InjectWithLocalFieldParameter(
            this MethodDefinition self,
            MethodDefinition injectionMethod,
            int callLoc,
            int localClass,
            FieldDefinition fieldInClass,
            bool fieldByRef              = false,
            InjectDirection direction    = InjectDirection.Before,
            InjectFlags f                = InjectFlags.None,
            int[] localsID               = null,
            FieldDefinition[] typeFields = null,
            int token = 0
            )
        {
            var flags = f.ToValues();

            if (callLoc == -1)
            {
                throw new ArgumentOutOfRangeException(nameof(callLoc));
            }
            if (flags.PassLocals && localsID == null)
            {
                throw new ArgumentNullException(nameof(localsID));
            }
            if (flags.PassFields && typeFields == null)
            {
                throw new ArgumentNullException(nameof(typeFields));
            }

            var body   = self.Body;
            var il     = body.GetILProcessor();
            var isVoid = self.ReturnType.FullName == "System.Void";
            var inst   = body.Instructions[callLoc];
            var inst2  = inst;

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

            VariableDefinition returnDef = null;

            if (flags.ModifyReturn && !isVoid)
            {
                body.InitLocals = true;
                returnDef       = new VariableDefinition(self.ReturnType);
                body.Variables.Add(returnDef);
            }
            if (flags.PassTag)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldc_I4, token));
            }
            if (flags.PassInvokingInstance)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldarg_0));
            }
            if (flags.ModifyReturn && !isVoid)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Ldloca_S, returnDef));
            }
            if (flags.PassLocals)
            {
                foreach (int i in localsID)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldloca_S, (byte)i));
                }
            }
            if (flags.PassFields)
            {
                var memberRefs = typeFields.Select(t => t.Module.ImportReference(t));
                foreach (FieldReference t in memberRefs)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldarg_0));
                    il.InsertBefore(inst, il.Create(OpCodes.Ldflda, t));
                }
            }
            if (flags.PassParameters)
            {
                int prefixCount = Convert.ToInt32(flags.PassTag) +
                                  Convert.ToInt32(flags.PassInvokingInstance) +
                                  Convert.ToInt32(flags.ModifyReturn && !isVoid);
                int localsCount    = flags.PassLocals ? localsID.Length : 0;
                int memberRefCount = flags.PassFields ? typeFields.Length : 0;
                int paramCount     = flags.PassParameters ? self.Parameters.Count : 0;
                int parameters     = injectionMethod.Parameters.Count - prefixCount - localsCount - memberRefCount - 1;
                int icr            = Convert.ToInt32(!self.IsStatic);
                for (int i = 0; i < parameters; i++)
                {
                    il.InsertBefore(
                        inst,
                        il.Create(flags.PassParametersByRef ? OpCodes.Ldarga_S : OpCodes.Ldarg_S, (byte)(i + icr))
                        );
                }
            }
            il.InsertBefore(inst, il.Create(OpCodes.Ldloc_S, (byte)localClass));
            il.InsertBefore(inst, il.Create(fieldByRef ? OpCodes.Ldflda : OpCodes.Ldfld, fieldInClass));
            il.InsertBefore(inst, il.Create(OpCodes.Call, self.Module.ImportReference(injectionMethod)));
            if (flags.ModifyReturn)
            {
                il.InsertBefore(inst, il.Create(OpCodes.Brfalse_S, inst));
                if (!isVoid)
                {
                    il.InsertBefore(inst, il.Create(OpCodes.Ldloc_S, returnDef));
                }
                il.InsertBefore(inst, il.Create(OpCodes.Ret));
            }
            // If we don't use the return value of InjectMethod, pop it from the ES
            else if (injectionMethod.ReturnType.FullName != "System.Void")
            {
                il.InsertBefore(inst, il.Create(OpCodes.Pop));
            }
            if (direction == InjectDirection.After)
            {
                il.Remove(inst2);
            }
            return(self);
        }
コード例 #10
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 #####");
        }