Esempio n. 1
0
    private void ProcessMethodDefinition(MethodDefinition method)
    {
        MethodBody body = method.Body;

        body.SimplifyMacros();
        ILProcessor ilProcessor = body.GetILProcessor();

        if (body.HasExceptionHandlers)
        {
            for (int i = 0; i < body.ExceptionHandlers.Count; i++)
            {
                //This doesn't rethrow per se, but it might be the path to more fine grained control
                //var exceptionType = typeof(Exception);
                //var exceptionCtor = exceptionType.GetConstructor(new Type[] {});
                //var constructorReference = ModuleDefinition.ImportReference(exceptionCtor);
                //ilProcessor.InsertBefore(body.ExceptionHandlers[i].HandlerEnd.Previous, Instruction.Create(OpCodes.Newobj, constructorReference));
                //ilProcessor.InsertBefore(body.ExceptionHandlers[i].HandlerEnd.Previous, Instruction.Create(OpCodes.Throw));

                ilProcessor.Replace(body.ExceptionHandlers[i].HandlerEnd.Previous, Instruction.Create(OpCodes.Rethrow));
            }
        }

        body.InitLocals = true;
        body.OptimizeMacros();
    }
Esempio n. 2
0
        private static void injectAssemblyLoad(MethodBody methodBody, string sourceAssemblyPath, Func <MethodDefinition, MethodReference> resolveMethod)
        {
            methodBody.SimplifyMacros();

            var returnInstructions = methodBody.Instructions
                                     .Where(x => x.OpCode == OpCodes.Ret)
                                     .ToList();

            using var sourceAssembly = AssemblyPathUtils.ReadAssemblyFromPath(sourceAssemblyPath, false);
            var sourceAssemblyMainModule = sourceAssembly.MainModule;

            var foundMethodInitializerMethods = ModuleDefinitionUtils.FindModuleInitializerMethods(sourceAssemblyMainModule)
                                                .Select(resolveMethod);

            foreach (var instruction in returnInstructions)
            {
                var instructions = new List <Instruction>(foundMethodInitializerMethods.Select(x => Instruction.Create(OpCodes.Call, x)))
                {
                    Instruction.Create(OpCodes.Ret)
                };

                methodBody.Instructions.Replace(instruction, instructions);
            }

            methodBody.OptimizeMacros();
        }
        // The idea behind this method is to change method call from Method(obj1, obj2, obj3) to Method(param Object[] objs)
        // To do that, we need to first pack all the objects to an array, then push the array onto the stack before calling the method.
        // Creating an array is achieved by pushing number of elements on the stack, and using newarr instruction.
        // Lastly, we need to pop the array back from the stack and store it in a local variable.
        //
        // Putting object to array is done by:
        // Loading array to the stack, loading index to the stack, loading the reference to value on the stack
        // and using stelem.ref to insert it to the array. stelem.ref instruction pops all three inserted values
        // from the stack
        //
        // For example, we need to convert something like this:
        //
        // IL_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
        // IL_0005: stloc.0
        // IL_0006: ldloc.0
        // IL_0007: ldstr "{0}, {1}"
        // IL_000c: ldstr "one"
        // IL_0011: ldstr "two"
        // IL_0016: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendFormat(string, object[])
        // IL_001b: pop
        //
        // To this:
        //
        // IL_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
        // IL_0005: stloc.0
        // IL_0006: ldloc.0
        // IL_0007: ldstr "{0}, {1}\r\n"
        // IL_000c: ldstr "one"
        // IL_0011: ldstr "two"
        // IL_0016: ldc.i4 2
        // IL_001b: newarr [mscorlib]System.Object
        // IL_0020: stloc 1
        // IL_0024: stloc 2
        // IL_0028: stloc 3
        // IL_002c: ldloc 1
        // IL_0030: ldc.i4 0
        // IL_0035: ldloc 2
        // IL_0039: stelem.ref
        // IL_003a: ldloc 1
        // IL_003e: ldc.i4 1
        // IL_0043: ldloc 3
        // IL_0047: stelem.ref
        // IL_0048: ldloc 1
        // IL_004c: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendFormat(string, object[])
        // IL_0051: pop
        //
        // Basically, just before the invalid function call we pack all the arguments (that are already on the stack,
        // ready to be passed to invalid function) to a newly created array and call a valid function instead passing
        // the array as argument
        //
        public void RewriteObjectListToParamsCall(MethodBody methodBody, int instructionIndex)
        {
            methodBody.SimplifyMacros();

            var parameterType = ParamsMethod.Parameters.Last().ParameterType;
            var arrayInfo = new VariableDefinition(parameterType);
            methodBody.InitLocals = true;
            methodBody.Variables.Add(arrayInfo);

            var instruction = methodBody.Instructions[instructionIndex];
            int numberOfObjects = (instruction.Operand as MethodReference).Parameters.Count - ParamsMethod.Parameters.Count + 1;

            // Firstly, let's create the object array

            // Push number of objects to the stack
            var instr = Instruction.Create(OpCodes.Ldc_I4, numberOfObjects);
            var firstInstruction = instr;
            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // Create a new array
            instr = Instruction.Create(OpCodes.Newarr, ParamsMethod.Parameters.Last().ParameterType.GetElementType());
            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // Store the newly created array to first variable slot
            instr = Instruction.Create(OpCodes.Stloc, arrayInfo);
            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // At this point, all the references to objects that need to be packed to the array are on the stack.
            var objectInfo = new VariableDefinition[numberOfObjects];
            for (int i = 0; i < numberOfObjects; i++)
            {
                objectInfo[i] = new VariableDefinition(parameterType.GetElementType());
                methodBody.Variables.Add(objectInfo[i]);

                instr = Instruction.Create(OpCodes.Stloc, objectInfo[i]);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;
            }

            // Now that we got the references to objects in local variables rather than the stack, it's high time we insert them to the array
            // We need to load them in backwards order, because the last argument was taken off the stack first.
            for (int i = 0; i < numberOfObjects; i++)
            {
                // Load reference to the array to the stack
                instr = Instruction.Create(OpCodes.Ldloc, arrayInfo);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Load object index to the stack
                instr = Instruction.Create(OpCodes.Ldc_I4, numberOfObjects - i - 1);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Load reference to the object to the stack
                instr = Instruction.Create(OpCodes.Ldloc, objectInfo[i]);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Insert the object to the array
                if (parameterType.GetElementType().IsValueType)
                {
                    instr = Instruction.Create(OpCodes.Stelem_Any, parameterType.GetElementType());
                    methodBody.Instructions.Insert(instructionIndex, instr);
                    instructionIndex++;
                }
                else
                {
                    instr = Instruction.Create(OpCodes.Stelem_Ref);
                    methodBody.Instructions.Insert(instructionIndex, instr);
                    instructionIndex++;
                }
            }

            // Finally, load reference to the array to the stack so it can be inserted to the array
            instr = Instruction.Create(OpCodes.Ldloc, arrayInfo);
            methodBody.Instructions.Insert(instructionIndex, instr);

            instruction.Operand = ParamsMethod;
            ParamsMethod = null;
            MethodChanged = false;
            methodBody.OptimizeMacros();		// This, together with SimplifyMacros() before touching IL code, recalculates IL instruction offsets

            // If any other instruction is referencing the illegal call, we need to rewrite it to reference beginning of object packing instead
            // For example, there's a branch jump to call the method. We need to pack the objects anyway before calling the method
            foreach (var changeableInstruction in methodBody.Instructions)
            {
                if (changeableInstruction.Operand is Instruction &&
                    (changeableInstruction as Instruction).Operand == instruction)
                {
                    changeableInstruction.Operand = firstInstruction;
                }
            }
        }
    private void Inject()
    {
        body = Method.Body;

        body.SimplifyMacros();

        var ilProcessor = body.GetILProcessor();

        var returnFixer = new ReturnFixer
        {
            Method = Method
        };

        returnFixer.MakeLastStatementReturn();

        var methodBodyFirstInstruction = GetMethodBodyFirstInstruction();

        var startInstructions = new List <Instruction>();

        startInstructions.AddRange(GetStartInstructions());

        foreach (var instruction in startInstructions)
        {
            ilProcessor.InsertBefore(methodBodyFirstInstruction, instruction);
        }

        var paramInstructions = GetParamInstructions();

        paramInstructions.Reverse();

        if (paramInstructions.Any())
        {
            foreach (var instruction in paramInstructions)
            {
                ilProcessor.InsertAfter(startInstructions.Last(), instruction);
            }
        }

        ilProcessor.InsertBefore(returnFixer.NopBeforeReturn, GetReturnValueInstructions(returnFixer.ReturnVariable));

        var tryCatchLeaveInstructions = Instruction.Create(OpCodes.Leave, returnFixer.NopBeforeReturn);
        var catchInstructions         = new List <Instruction>();

        catchInstructions.AddRange(GetCatchInstructions());
        ilProcessor.InsertBefore(returnFixer.NopBeforeReturn, tryCatchLeaveInstructions);
        ilProcessor.InsertBefore(returnFixer.NopBeforeReturn, catchInstructions);

        var endInstructions = new List <Instruction>();

        endInstructions.AddRange(GetEndInstructions());
        var finallyInstruction = Instruction.Create(OpCodes.Endfinally);

        endInstructions.Add(finallyInstruction);
        endInstructions.Reverse();

        foreach (var instruction in endInstructions)
        {
            ilProcessor.InsertAfter(catchInstructions.Last(), instruction);
        }

        var handler = new ExceptionHandler(ExceptionHandlerType.Catch)
        {
            CatchType    = ExceptionType,
            TryStart     = methodBodyFirstInstruction,
            TryEnd       = tryCatchLeaveInstructions.Next,
            HandlerStart = catchInstructions.First(),
            HandlerEnd   = catchInstructions.Last().Next
        };

        body.ExceptionHandlers.Add(handler);

        handler = new ExceptionHandler(ExceptionHandlerType.Finally)
        {
            TryStart     = methodBodyFirstInstruction,
            TryEnd       = catchInstructions.Last().Next,
            HandlerStart = catchInstructions.Last().Next,
            HandlerEnd   = finallyInstruction.Next
        };

        body.ExceptionHandlers.Add(handler);

        var         instructions         = body.Instructions;
        Instruction doubleDupInstruction = null;

        for (var index = 0; index < instructions.Count; index++)
        {
            var instruction = instructions[index];

            if (instruction.OpCode == OpCodes.Dup && instructions[index + 1].OpCode == OpCodes.Dup)
            {
                doubleDupInstruction = instructions[index + 1];
            }

            if (instruction.OpCode == OpCodes.Pop && doubleDupInstruction != null)
            {
                var extraPopInstruction = instructions[index];
                ilProcessor.Remove(extraPopInstruction);
                ilProcessor.InsertAfter(doubleDupInstruction, extraPopInstruction);
                doubleDupInstruction = null;
            }
        }

        body.InitLocals = true;
        body.OptimizeMacros();
    }
Esempio n. 5
0
        public DirectCallMethod(ModuleDefinition module, TypeDefinition type)
        {
            this.module = module;
            this.type = type;

            getLength = module.Import(typeof(string).GetMethod("get_Length", new Type[0]));
            getChars = module.Import(typeof(string).GetMethod("get_Chars", new[] { typeof(int) }));
            isNullOrEmpty = module.Import(typeof(string).GetMethod("IsNullOrEmpty", new[] { typeof(string) }));
            stringEquals = module.Import(typeof(string).GetMethod("Equals", new[] { typeof(string) }));

            // Copy method definition from base class
            var base_assembly = AssemblyDefinition.ReadAssembly(Path.Combine(Interface.Oxide.ExtensionDirectory, "Oxide.Ext.CSharp.dll"));
            var base_module = base_assembly.MainModule;
            var base_type = module.Import(base_assembly.MainModule.GetType("Oxide.Plugins.CSharpPlugin")).Resolve();
            var base_method = module.Import(base_type.Methods.First(method => method.Name == "DirectCallHook")).Resolve();

            // Create method override based on virtual method signature
            method = new MethodDefinition(base_method.Name, base_method.Attributes, base_module.Import(base_method.ReturnType)) { DeclaringType = type };
            foreach (var parameter in base_method.Parameters)
            {
                var new_param = new ParameterDefinition(parameter.Name, parameter.Attributes, base_module.Import(parameter.ParameterType))
                {
                    IsOut = parameter.IsOut,
                    Constant = parameter.Constant,
                    MarshalInfo = parameter.MarshalInfo,
                    IsReturnValue = parameter.IsReturnValue
                };
                foreach (var attribute in parameter.CustomAttributes)
                    new_param.CustomAttributes.Add(new CustomAttribute(module.Import(attribute.Constructor)));
                method.Parameters.Add(new_param);
            }

            foreach (var attribute in base_method.CustomAttributes)
                method.CustomAttributes.Add(new CustomAttribute(module.Import(attribute.Constructor)));

            method.ImplAttributes = base_method.ImplAttributes;
            method.SemanticsAttributes = base_method.SemanticsAttributes;

            // Replace the NewSlot attribute with ReuseSlot
            method.Attributes &= ~MethodAttributes.NewSlot;
            method.Attributes |= MethodAttributes.ReuseSlot;

            // Create new method body
            body = new MethodBody(method);
            body.SimplifyMacros();
            method.Body = body;
            type.Methods.Add(method);

            // Create variables
            body.Variables.Add(new VariableDefinition("name_size", module.TypeSystem.Int32));
            body.Variables.Add(new VariableDefinition("i", module.TypeSystem.Int32));

            // Initialize return value to null
            AddInstruction(OpCodes.Ldarg_2);
            AddInstruction(OpCodes.Ldnull);
            AddInstruction(OpCodes.Stind_Ref);

            // Check for name null or empty
            AddInstruction(OpCodes.Ldarg_1);
            AddInstruction(OpCodes.Call, isNullOrEmpty);
            var empty = AddInstruction(OpCodes.Brfalse, body.Instructions[0]);
            Return(false);

            // Get method name length
            empty.Operand = AddInstruction(OpCodes.Ldarg_1);
            AddInstruction(OpCodes.Callvirt, getLength);
            AddInstruction(OpCodes.Stloc_0);

            // Initialize i counter variable to 0
            AddInstruction(OpCodes.Ldc_I4_0);
            AddInstruction(OpCodes.Stloc_1);

            // Find all hook methods defined by the plugin
            foreach (var m in type.Methods.Where(m => !m.IsStatic && m.IsPrivate && !m.HasGenericParameters && !m.ReturnType.IsGenericParameter && m.DeclaringType == type && !m.IsSetter && !m.IsGetter))
            {
                //ignore compiler generated
                if (m.Name.Contains("<")) continue;
                if (!hookMethods.ContainsKey(m.Name)) hookMethods[m.Name] = m;
            }

            // Build a hook method name trie
            var root_node = new Node();
            foreach (var method_name in hookMethods.Keys)
            {
                var current_node = root_node;
                for (var i = 1; i <= method_name.Length; i++)
                {
                    var letter = method_name[i - 1];
                    Node next_node;
                    if (!current_node.Edges.TryGetValue(letter, out next_node))
                    {
                        next_node = new Node { Parent = current_node, Char = letter };
                        current_node.Edges[letter] = next_node;
                    }
                    if (i == method_name.Length) next_node.Name = method_name;
                    current_node = next_node;
                }
            }

            // Build conditional method call logic from trie nodes
            var n = 1;
            foreach (var edge in root_node.Edges.Keys)
                BuildNode(root_node.Edges[edge], n++);

            // No valid method was found
            endInstruction = Return(false);

            foreach (var instruction in jumpToEdgePlaceholderTargets.Keys)
            {
                instruction.Operand = jumpToEdgePlaceholderTargets[instruction].FirstInstruction;
            }

            foreach (var instruction in jumpToEndPlaceholders)
            {
                instruction.Operand = endInstruction;
            }

            body.OptimizeMacros();
        }
Esempio n. 6
0
            public DirectCallMethod(ModuleDefinition module, TypeDefinition type)
            {
                this.module = module;
                this.type = type;

                getLength = module.Import(typeof(string).GetMethod("get_Length", new Type[0]));
                getChars = module.Import(typeof(string).GetMethod("get_Chars", new[] { typeof(int) }));
                isNullOrEmpty = module.Import(typeof(string).GetMethod("IsNullOrEmpty"));

                // Copy method definition from base class
                var base_assembly = AssemblyDefinition.ReadAssembly("HookDispatch.exe");
                var base_module = base_assembly.MainModule;
                var base_type = base_assembly.MainModule.GetType("Oxide.Plugins.Plugin");
                var base_method = base_type.Methods.First(method => method.Name == "DirectCallHook");

                // Create method override based on virtual method signature
                method = new MethodDefinition(base_method.Name, base_method.Attributes, base_module.Import(base_method.ReturnType));
                foreach (var parameter in base_method.Parameters)
                    method.Parameters.Add(new ParameterDefinition(base_module.Import(parameter.ParameterType)) {Name = parameter.Name, IsOut = parameter.IsOut});

                method.ImplAttributes = base_method.ImplAttributes;
                method.SemanticsAttributes = base_method.SemanticsAttributes;

                // Replace the NewSlot attribute with ReuseSlot
                method.Attributes &= ~Mono.Cecil.MethodAttributes.NewSlot;
                method.Attributes |= Mono.Cecil.MethodAttributes.ReuseSlot;

                // Create new method body
                body = new Mono.Cecil.Cil.MethodBody(method);
                body.SimplifyMacros();
                method.Body = body;
                type.Methods.Add(method);

                // Create variables
                body.Variables.Add(new VariableDefinition("name_size", module.TypeSystem.Int32));
                body.Variables.Add(new VariableDefinition("i", module.TypeSystem.Int32));

                // Initialize return value to null
                AddInstruction(OpCodes.Ldarg_2);
                AddInstruction(OpCodes.Ldnull);
                AddInstruction(OpCodes.Stind_Ref);

                // Check for name null or empty
                AddInstruction(OpCodes.Ldarg_1);
                AddInstruction(OpCodes.Call, isNullOrEmpty);
                var empty = AddInstruction(OpCodes.Brfalse, body.Instructions[0]);
                Return(false);

                // Get method name length
                empty.Operand = AddInstruction(OpCodes.Ldarg_1);
                AddInstruction(OpCodes.Callvirt, getLength);
                AddInstruction(OpCodes.Stloc_0);

                // Initialize i counter variable to 0
                AddInstruction(OpCodes.Ldc_I4_0);
                AddInstruction(OpCodes.Stloc_1);

                // Find all hook methods defined by the plugin
                hookMethods = type.Methods.Where(m => !m.IsStatic && m.IsPrivate).ToDictionary(m => m.Name, m => m);

                // Build a hook method name trie
                var root_node = new Node();
                foreach (var method_name in hookMethods.Keys)
                {
                    var current_node = root_node;
                    for (var i = 1; i <= method_name.Length; i++)
                    {
                        var letter = method_name[i - 1];
                        Node next_node;
                        if (!current_node.Edges.TryGetValue(letter, out next_node))
                        {
                            next_node = new Node { Parent = current_node, Char = letter };
                            if (i == method_name.Length) next_node.Name = method_name;
                            current_node.Edges[letter] = next_node;
                        }
                        current_node = next_node;
                    }
                }

                // Build conditional method call logic from trie nodes
                var n = 1;
                foreach (var edge in root_node.Edges.Keys)
                    BuildNode(root_node.Edges[edge], n++);

                // No valid method was found
                endInstruction = Return(false);

                foreach (var instruction in jumpToEdgePlaceholderTargets.Keys)
                {
                    instruction.Operand = jumpToEdgePlaceholderTargets[instruction].FirstInstruction;
                }

                foreach (var instruction in jumpToEndPlaceholders)
                {
                    instruction.Operand = endInstruction;
                }

                //UpdateInstructions();

                body.OptimizeMacros();

                foreach (var instruction in jumpToEdgePlaceholderTargets.Keys)
                {
                    Puts($"Jump: {instruction}");
                }

                foreach (var instruction in jumpToEndPlaceholders)
                {
                    Puts($"Jump to end: {instruction}");
                }

                foreach (var instruction in body.Instructions)
                {
                    var line = instruction.ToString();
                    Puts(line);
                    if (line.Contains(" bne.un") || line.Contains(" ret")) Puts("");
                }
            }
Esempio n. 7
0
        // The idea behind this method is to change method call from Method(obj1, obj2, obj3) to Method(param Object[] objs)
        // To do that, we need to first pack all the objects to an array, then push the array onto the stack before calling the method.
        // Creating an array is achieved by pushing number of elements on the stack, and using newarr instruction.
        // Lastly, we need to pop the array back from the stack and store it in a local variable.
        //
        // Putting object to array is done by:
        // Loading array to the stack, loading index to the stack, loading the reference to value on the stack
        // and using stelem.ref to insert it to the array. stelem.ref instruction pops all three inserted values
        // from the stack
        //
        // For example, we need to convert something like this:
        //
        // IL_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
        // IL_0005: stloc.0
        // IL_0006: ldloc.0
        // IL_0007: ldstr "{0}, {1}"
        // IL_000c: ldstr "one"
        // IL_0011: ldstr "two"
        // IL_0016: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendFormat(string, object[])
        // IL_001b: pop
        //
        // To this:
        //
        // IL_0000: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
        // IL_0005: stloc.0
        // IL_0006: ldloc.0
        // IL_0007: ldstr "{0}, {1}\r\n"
        // IL_000c: ldstr "one"
        // IL_0011: ldstr "two"
        // IL_0016: ldc.i4 2
        // IL_001b: newarr [mscorlib]System.Object
        // IL_0020: stloc 1
        // IL_0024: stloc 2
        // IL_0028: stloc 3
        // IL_002c: ldloc 1
        // IL_0030: ldc.i4 0
        // IL_0035: ldloc 2
        // IL_0039: stelem.ref
        // IL_003a: ldloc 1
        // IL_003e: ldc.i4 1
        // IL_0043: ldloc 3
        // IL_0047: stelem.ref
        // IL_0048: ldloc 1
        // IL_004c: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendFormat(string, object[])
        // IL_0051: pop
        //
        // Basically, just before the invalid function call we pack all the arguments (that are already on the stack,
        // ready to be passed to invalid function) to a newly created array and call a valid function instead passing
        // the array as argument
        //
        public void RewriteObjectListToParamsCall(MethodBody methodBody, int instructionIndex)
        {
            methodBody.SimplifyMacros();

            var parameterType = ParamsMethod.Parameters.Last().ParameterType;
            var arrayInfo     = new VariableDefinition(parameterType);

            methodBody.InitLocals = true;
            methodBody.Variables.Add(arrayInfo);

            var instruction     = methodBody.Instructions[instructionIndex];
            int numberOfObjects = (instruction.Operand as MethodReference).Parameters.Count - ParamsMethod.Parameters.Count + 1;

            // Firstly, let's create the object array

            // Push number of objects to the stack
            var instr            = Instruction.Create(OpCodes.Ldc_I4, numberOfObjects);
            var firstInstruction = instr;

            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // Create a new array
            instr = Instruction.Create(OpCodes.Newarr, ParamsMethod.Parameters.Last().ParameterType.GetElementType());
            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // Store the newly created array to first variable slot
            instr = Instruction.Create(OpCodes.Stloc, arrayInfo);
            methodBody.Instructions.Insert(instructionIndex, instr);
            instructionIndex++;

            // At this point, all the references to objects that need to be packed to the array are on the stack.
            var objectInfo = new VariableDefinition[numberOfObjects];

            for (int i = 0; i < numberOfObjects; i++)
            {
                objectInfo[i] = new VariableDefinition(parameterType.GetElementType());
                methodBody.Variables.Add(objectInfo[i]);

                instr = Instruction.Create(OpCodes.Stloc, objectInfo[i]);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;
            }

            // Now that we got the references to objects in local variables rather than the stack, it's high time we insert them to the array
            // We need to load them in backwards order, because the last argument was taken off the stack first.
            for (int i = 0; i < numberOfObjects; i++)
            {
                // Load reference to the array to the stack
                instr = Instruction.Create(OpCodes.Ldloc, arrayInfo);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Load object index to the stack
                instr = Instruction.Create(OpCodes.Ldc_I4, numberOfObjects - i - 1);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Load reference to the object to the stack
                instr = Instruction.Create(OpCodes.Ldloc, objectInfo[i]);
                methodBody.Instructions.Insert(instructionIndex, instr);
                instructionIndex++;

                // Insert the object to the array
                if (parameterType.GetElementType().IsValueType)
                {
                    instr = Instruction.Create(OpCodes.Stelem_Any, parameterType.GetElementType());
                    methodBody.Instructions.Insert(instructionIndex, instr);
                    instructionIndex++;
                }
                else
                {
                    instr = Instruction.Create(OpCodes.Stelem_Ref);
                    methodBody.Instructions.Insert(instructionIndex, instr);
                    instructionIndex++;
                }
            }

            // Finally, load reference to the array to the stack so it can be inserted to the array
            instr = Instruction.Create(OpCodes.Ldloc, arrayInfo);
            methodBody.Instructions.Insert(instructionIndex, instr);

            instruction.Operand = ParamsMethod;
            ParamsMethod        = null;
            MethodChanged       = false;
            methodBody.OptimizeMacros();                        // This, together with SimplifyMacros() before touching IL code, recalculates IL instruction offsets

            // If any other instruction is referencing the illegal call, we need to rewrite it to reference beginning of object packing instead
            // For example, there's a branch jump to call the method. We need to pack the objects anyway before calling the method
            foreach (var changeableInstruction in methodBody.Instructions)
            {
                if (changeableInstruction.Operand is Instruction &&
                    (changeableInstruction as Instruction).Operand == instruction)
                {
                    changeableInstruction.Operand = firstInstruction;
                }
            }
        }
Esempio n. 8
0
            public DirectCallMethod(ModuleDefinition module, TypeDefinition type)
            {
                this.module = module;
                this.type   = type;

                getLength = module.Import(typeof(string).GetMethod("get_Length", new Type[0]));
                getChars  = module.Import(typeof(string).GetMethod("get_Chars", new[] { typeof(int) }));

                // Copy method definition from base class
                var base_assembly = AssemblyDefinition.ReadAssembly("HookDispatch.exe");
                var base_module   = base_assembly.MainModule;
                var base_type     = base_assembly.MainModule.GetType("Oxide.Plugins.Plugin");
                var base_method   = base_type.Methods.First(method => method.Name == "DirectCallHook");

                // Create method override based on virtual method signature
                method = new MethodDefinition(base_method.Name, base_method.Attributes, base_module.Import(base_method.ReturnType));
                foreach (var parameter in base_method.Parameters)
                {
                    method.Parameters.Add(new ParameterDefinition(base_module.Import(parameter.ParameterType)));
                }

                method.ImplAttributes      = base_method.ImplAttributes;
                method.SemanticsAttributes = base_method.SemanticsAttributes;

                // Replace the NewSlot attribute with ReuseSlot
                method.Attributes &= ~Mono.Cecil.MethodAttributes.NewSlot;
                method.Attributes |= Mono.Cecil.MethodAttributes.ReuseSlot;

                // Create new method body
                body = new Mono.Cecil.Cil.MethodBody(method);
                body.SimplifyMacros();
                method.Body = body;
                type.Methods.Add(method);

                // Create variables
                body.Variables.Add(new VariableDefinition("name_size", module.TypeSystem.Int32));
                body.Variables.Add(new VariableDefinition("i", module.TypeSystem.Int32));

                // Initialize return value to null
                AddInstruction(OpCodes.Ldarg_2);
                AddInstruction(OpCodes.Ldnull);
                AddInstruction(OpCodes.Stind_Ref);

                // Get method name length
                AddInstruction(OpCodes.Ldarg_1);
                AddInstruction(OpCodes.Callvirt, getLength);
                AddInstruction(OpCodes.Stloc_0);

                // Initialize i counter variable to 0
                AddInstruction(OpCodes.Ldc_I4_0);
                AddInstruction(OpCodes.Stloc_1);

                // Find all hook methods defined by the plugin
                hookMethods = type.Methods.Where(m => !m.IsStatic && m.IsPrivate).ToDictionary(m => m.Name, m => m);

                // Build a hook method name trie
                var root_node = new Node();

                foreach (var method_name in hookMethods.Keys)
                {
                    var current_node = root_node;
                    for (var i = 1; i <= method_name.Length; i++)
                    {
                        var  letter = method_name[i - 1];
                        Node next_node;
                        if (!current_node.Edges.TryGetValue(letter, out next_node))
                        {
                            next_node = new Node {
                                Parent = current_node, Char = letter
                            };
                            if (i == method_name.Length)
                            {
                                next_node.Name = method_name;
                            }
                            current_node.Edges[letter] = next_node;
                        }
                        current_node = next_node;
                    }
                }

                // Build conditional method call logic from trie nodes
                var n = 1;

                foreach (var edge in root_node.Edges.Keys)
                {
                    BuildNode(root_node.Edges[edge], n++);
                }

                // No valid method was found
                endInstruction = Return(false);

                foreach (var instruction in jumpToEdgePlaceholderTargets.Keys)
                {
                    instruction.Operand = jumpToEdgePlaceholderTargets[instruction].FirstInstruction;
                }

                foreach (var instruction in jumpToEndPlaceholders)
                {
                    instruction.Operand = endInstruction;
                }

                //UpdateInstructions();

                body.OptimizeMacros();

                foreach (var instruction in jumpToEdgePlaceholderTargets.Keys)
                {
                    Puts($"Jump: {instruction}");
                }

                foreach (var instruction in jumpToEndPlaceholders)
                {
                    Puts($"Jump to end: {instruction}");
                }

                foreach (var instruction in body.Instructions)
                {
                    var line = instruction.ToString();
                    Puts(line);
                    if (line.Contains(" bne.un.s") || line.Contains(" ret"))
                    {
                        Puts("");
                    }
                }
            }
Esempio n. 9
0
        private void InnerProcess(MethodDefinition method, bool returnVoid, ReferenceContainer references)
        {
            body = method.Body;
            body.SimplifyMacros();

            if (body.Instructions.Count <= 3)
                return; // nothing to do (empty method)

            HandleReturnType(method, returnVoid, references);

            var ilProcessor = body.GetILProcessor();

            var firstInstruction = FirstInstructionSkipCtor(method);

            firstInstruction = RejigFirstInstruction(ilProcessor, firstInstruction);

            InjectContext(ilProcessor, firstInstruction, method, references);
            var returnInstruction = FixReturns();

            var beforeReturn = Instruction.Create(OpCodes.Nop);
            ilProcessor.InsertBefore(returnInstruction, beforeReturn);

            // exclude try-catch from constructors (lot's of pain otherwise)
            if (!method.IsConstructor)
            {
                var beginCatch = InjectIlForCatch(ilProcessor, beforeReturn, references);
                var beginFinally = InjectIlForFinaly(ilProcessor, beforeReturn, references);

                var catchHandler = new ExceptionHandler(ExceptionHandlerType.Catch)
                    {
                        TryStart = firstInstruction,
                        TryEnd = beginCatch,
                        CatchType = references.ExceptionType,
                        HandlerStart = beginCatch,
                        HandlerEnd = beginFinally,
                    };

                body.ExceptionHandlers.Add(catchHandler);

                var finallyHandler = new ExceptionHandler(ExceptionHandlerType.Finally)
                    {
                        TryStart = firstInstruction,
                        TryEnd = beginFinally,
                        HandlerStart = beginFinally,
                        HandlerEnd = beforeReturn,
                    };

                body.ExceptionHandlers.Add(finallyHandler);
            }
            else
            {
                InjectIlForDispose(ilProcessor, returnInstruction, references);
            }

            body.InitLocals = true;
            body.OptimizeMacros();
        }
        private void PatchAssembly(Action <byte[]> callback)
        {
            Action action1 = null;

            if (this.isPatching)
            {
                Interface.Oxide.LogWarning("Already patching plugin assembly: {0} (ignoring)", new object[] { this.PluginNames.ToSentence <string>() });
                return;
            }
            float now = Interface.Oxide.Now;

            this.isPatching = true;
            ThreadPool.QueueUserWorkItem((object _) => {
                AssemblyDefinition assemblyDefinition;
                try
                {
                    using (MemoryStream memoryStream = new MemoryStream(this.RawAssembly))
                    {
                        assemblyDefinition = AssemblyDefinition.ReadAssembly(memoryStream);
                    }
                    ConstructorInfo constructor      = typeof(UnauthorizedAccessException).GetConstructor(new Type[] { typeof(string) });
                    MethodReference methodReference1 = assemblyDefinition.MainModule.Import(constructor);
                    Action <TypeDefinition> methods  = null;
                    methods = (TypeDefinition type) => {
                        string fullName;
                        string str;
                        foreach (MethodDefinition method in type.Methods)
                        {
                            bool flag = false;
                            if (method.Body != null)
                            {
                                bool flag1 = false;
                                foreach (VariableDefinition variable in method.Body.Variables)
                                {
                                    if (!CompiledAssembly.IsNamespaceBlacklisted(variable.VariableType.FullName))
                                    {
                                        continue;
                                    }
                                    Mono.Cecil.Cil.MethodBody methodBody = new Mono.Cecil.Cil.MethodBody(method);
                                    methodBody.Instructions.Add(Instruction.Create(OpCodes.Ldstr, string.Concat("System access is restricted, you are not allowed to use ", variable.VariableType.FullName)));
                                    methodBody.Instructions.Add(Instruction.Create(OpCodes.Newobj, methodReference1));
                                    methodBody.Instructions.Add(Instruction.Create(OpCodes.Throw));
                                    method.Body = methodBody;
                                    flag1       = true;
                                    break;
                                }
                                if (flag1)
                                {
                                    continue;
                                }
                                Collection <Instruction> instructions = method.Body.Instructions;
                                ILProcessor lProcessor  = method.Body.GetILProcessor();
                                Instruction instruction = instructions.First <Instruction>();
                                for (int i = 0; i < instructions.Count && !flag; i++)
                                {
                                    Instruction item = instructions[i];
                                    if (item.OpCode == OpCodes.Ldtoken)
                                    {
                                        IMetadataTokenProvider operand = item.Operand as IMetadataTokenProvider;
                                        str         = (operand != null ? operand.ToString() : null);
                                        string str1 = str;
                                        if (CompiledAssembly.IsNamespaceBlacklisted(str1))
                                        {
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Ldstr, string.Concat("System access is restricted, you are not allowed to use ", str1)));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Newobj, methodReference1));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Throw));
                                            flag = true;
                                        }
                                    }
                                    else if (item.OpCode == OpCodes.Call || item.OpCode == OpCodes.Calli || item.OpCode == OpCodes.Callvirt || item.OpCode == OpCodes.Ldftn)
                                    {
                                        MethodReference methodReference = item.Operand as MethodReference;
                                        string str2 = (methodReference != null ? methodReference.DeclaringType.FullName : null);
                                        if (str2 == "System.Type" && methodReference.Name == "GetType" || CompiledAssembly.IsNamespaceBlacklisted(str2))
                                        {
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Ldstr, string.Concat("System access is restricted, you are not allowed to use ", str2)));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Newobj, methodReference1));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Throw));
                                            flag = true;
                                        }
                                    }
                                    else if (item.OpCode == OpCodes.Ldfld)
                                    {
                                        FieldReference fieldReference = item.Operand as FieldReference;
                                        fullName    = (fieldReference != null ? fieldReference.FieldType.FullName : null);
                                        string str3 = fullName;
                                        if (CompiledAssembly.IsNamespaceBlacklisted(str3))
                                        {
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Ldstr, string.Concat("System access is restricted, you are not allowed to use ", str3)));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Newobj, methodReference1));
                                            lProcessor.InsertBefore(instruction, Instruction.Create(OpCodes.Throw));
                                            flag = true;
                                        }
                                    }
                                }
                            }
                            else if (method.HasPInvokeInfo)
                            {
                                MethodDefinition attributes           = method;
                                attributes.Attributes                 = attributes.Attributes & (Mono.Cecil.MethodAttributes.MemberAccessMask | Mono.Cecil.MethodAttributes.Private | Mono.Cecil.MethodAttributes.FamANDAssem | Mono.Cecil.MethodAttributes.Assembly | Mono.Cecil.MethodAttributes.Family | Mono.Cecil.MethodAttributes.FamORAssem | Mono.Cecil.MethodAttributes.Public | Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.Final | Mono.Cecil.MethodAttributes.Virtual | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.VtableLayoutMask | Mono.Cecil.MethodAttributes.NewSlot | Mono.Cecil.MethodAttributes.CheckAccessOnOverride | Mono.Cecil.MethodAttributes.Abstract | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.UnmanagedExport | Mono.Cecil.MethodAttributes.RTSpecialName | Mono.Cecil.MethodAttributes.HasSecurity | Mono.Cecil.MethodAttributes.RequireSecObject);
                                Mono.Cecil.Cil.MethodBody methodBody1 = new Mono.Cecil.Cil.MethodBody(method);
                                methodBody1.Instructions.Add(Instruction.Create(OpCodes.Ldstr, "PInvoke access is restricted, you are not allowed to use PInvoke"));
                                methodBody1.Instructions.Add(Instruction.Create(OpCodes.Newobj, methodReference1));
                                methodBody1.Instructions.Add(Instruction.Create(OpCodes.Throw));
                                method.Body = methodBody1;
                            }
                            if (!flag)
                            {
                                continue;
                            }
                            Mono.Cecil.Cil.MethodBody body = method.Body;
                            if (body != null)
                            {
                                body.OptimizeMacros();
                            }
                            else
                            {
                            }
                        }
                        foreach (TypeDefinition nestedType in type.NestedTypes)
                        {
                            methods(nestedType);
                        }
                    };
                    foreach (TypeDefinition typeDefinition in assemblyDefinition.MainModule.Types)
                    {
                        methods(typeDefinition);
                        if (this.IsCompilerGenerated(typeDefinition))
                        {
                            continue;
                        }
                        if (typeDefinition.Namespace != "Oxide.Plugins")
                        {
                            if (!(typeDefinition.FullName != "<Module>") || this.PluginNames.Any <string>((string plugin) => typeDefinition.FullName.StartsWith(string.Concat("Oxide.Plugins.", plugin))))
                            {
                                continue;
                            }
                            Interface.Oxide.LogWarning(((int)this.PluginNames.Length == 1 ? string.Concat(this.PluginNames[0], " has polluted the global namespace by defining ", typeDefinition.FullName) : string.Concat("A plugin has polluted the global namespace by defining ", typeDefinition.FullName)), Array.Empty <object>());
                        }
                        else if (!this.PluginNames.Contains <string>(typeDefinition.Name))
                        {
                            Interface.Oxide.LogWarning(((int)this.PluginNames.Length == 1 ? string.Concat(this.PluginNames[0], " has polluted the global namespace by defining ", typeDefinition.Name) : string.Concat("A plugin has polluted the global namespace by defining ", typeDefinition.Name)), Array.Empty <object>());
                        }
                        else
                        {
                            Collection <MethodDefinition> methodDefinitions = typeDefinition.Methods;
                            Func <MethodDefinition, bool> u003cu003e9_204   = CompiledAssembly.< > c.< > 9__20_4;
                            if (u003cu003e9_204 == null)
                            {
                                u003cu003e9_204 = (MethodDefinition m) => {
                                    if (m.IsStatic || !m.IsConstructor || m.HasParameters)
                                    {
                                        return(false);
                                    }
                                    return(!m.IsPublic);
                                };
                                CompiledAssembly.< > c.< > 9__20_4 = u003cu003e9_204;
                            }
                            if (methodDefinitions.FirstOrDefault <MethodDefinition>(u003cu003e9_204) == null)
                            {
                                DirectCallMethod directCallMethod = new DirectCallMethod(assemblyDefinition.MainModule, typeDefinition);
                            }
                            else
                            {
                                CompilablePlugin compilablePlugin = this.CompilablePlugins.SingleOrDefault <CompilablePlugin>((CompilablePlugin p) => p.Name == typeDefinition.Name);
                                if (compilablePlugin == null)
                                {
                                    continue;
                                }
                                compilablePlugin.CompilerErrors = "Primary constructor in main class must be public";
                            }
                        }
                    }
                    foreach (TypeDefinition typeDefinition1 in assemblyDefinition.MainModule.Types)
                    {
                        if (typeDefinition1.Namespace != "Oxide.Plugins" || !this.PluginNames.Contains <string>(typeDefinition1.Name))
                        {
                            continue;
                        }
                        Collection <MethodDefinition> methods1        = typeDefinition1.Methods;
                        Func <MethodDefinition, bool> u003cu003e9_206 = CompiledAssembly.< > c.< > 9__20_6;
                        if (u003cu003e9_206 == null)
                        {
                            u003cu003e9_206 = (MethodDefinition m) => {
                                if (m.IsStatic || m.HasGenericParameters || m.ReturnType.IsGenericParameter || m.IsSetter)
                                {
                                    return(false);
                                }
                                return(!m.IsGetter);
                            };
                            CompiledAssembly.< > c.< > 9__20_6 = u003cu003e9_206;
                        }
                        foreach (MethodDefinition methodDefinition in methods1.Where <MethodDefinition>(u003cu003e9_206))
                        {
                            foreach (ParameterDefinition parameter in methodDefinition.Parameters)
                            {
                                foreach (CustomAttribute customAttribute in parameter.CustomAttributes)
                                {
                                }
                            }
                        }
                    }
                    using (MemoryStream memoryStream1 = new MemoryStream())
                    {
                        assemblyDefinition.Write(memoryStream1);
                        this.PatchedAssembly = memoryStream1.ToArray();
                    }
                    OxideMod oxide       = Interface.Oxide;
                    Action u003cu003e9_2 = action1;
                    if (u003cu003e9_2 == null)
                    {
                        Action u003cu003e4_this = () => {
                            this.isPatching = false;
                            callback(this.PatchedAssembly);
                        };
                        Action action = u003cu003e4_this;
                        action1       = u003cu003e4_this;
                        u003cu003e9_2 = action;
                    }
                    oxide.NextTick(u003cu003e9_2);
                }
                catch (Exception exception1)
                {
                    Exception exception = exception1;
                    Interface.Oxide.NextTick(() => {
                        this.isPatching = false;
                        Interface.Oxide.LogException(string.Concat("Exception while patching: ", this.PluginNames.ToSentence <string>()), exception);
                        callback(null);
                    });
                }
            });
        }