Esempio n. 1
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();
        }
Esempio n. 2
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();
    }
        // 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 void Mutate(Random rand, MethodBody body)
        {
            body.SimplifyMacros();
            foreach (var i in body.Instructions)
            {
                FieldReference field = i.Operand as FieldReference;
                if (field != null && field.DeclaringType.FullName == "Mutation")
                {
                    switch (field.Name)
                    {
                        case "Key0I":
                            i.Operand = IntKeys[0]; goto case "I";
                        case "Key1I":
                            i.Operand = IntKeys[1]; goto case "I";
                        case "Key2I":
                            i.Operand = IntKeys[2]; goto case "I";
                        case "Key3I":
                            i.Operand = IntKeys[3]; goto case "I";
                        case "Key4I":
                            i.Operand = IntKeys[4]; goto case "I";
                        case "Key5I":
                            i.Operand = IntKeys[5]; goto case "I";
                        case "Key6I":
                            i.Operand = IntKeys[6]; goto case "I";
                        case "Key7I":
                            i.Operand = IntKeys[7]; goto case "I";

                        case "Key0L":
                            i.Operand = LongKeys[0]; goto case "L";
                        case "Key1L":
                            i.Operand = LongKeys[1]; goto case "L";
                        case "Key2L":
                            i.Operand = LongKeys[2]; goto case "L";
                        case "Key3L":
                            i.Operand = LongKeys[3]; goto case "L";
                        case "Key4L":
                            i.Operand = LongKeys[4]; goto case "L";
                        case "Key5L":
                            i.Operand = LongKeys[5]; goto case "L";
                        case "Key6L":
                            i.Operand = LongKeys[6]; goto case "L";
                        case "Key7L":
                            i.Operand = LongKeys[7]; goto case "L";

                        case "Key0S":
                            i.Operand = StringKeys[0]; goto case "S";
                        case "Key1S":
                            i.Operand = StringKeys[1]; goto case "S";
                        case "Key2S":
                            i.Operand = StringKeys[2]; goto case "S";
                        case "Key3S":
                            i.Operand = StringKeys[3]; goto case "S";

                        case "Key0Delayed":
                            if (IsDelayed)
                            {
                                i.Operand = DelayedKeys[0];
                                goto case "I";
                            }
                            else
                                Delayed0 = i;
                            break;
                        case "Key1Delayed":
                            if (IsDelayed)
                            {
                                i.Operand = DelayedKeys[1];
                                goto case "I";
                            }
                            else
                                Delayed1 = i;
                            break;

                        case "I":
                            i.OpCode = OpCodes.Ldc_I4; break;
                        case "L":
                            i.OpCode = OpCodes.Ldc_I8; break;
                        case "S":
                            i.OpCode = OpCodes.Ldstr; break;
                    }
                }
                MethodReference method = i.Operand as MethodReference;
                if (method != null && method.DeclaringType.FullName == "Mutation")
                {
                    if (method.Name == "Placeholder")
                        Placeholder = i;
                    else if (method.Name == "DeclaringType")
                    {
                        i.OpCode = OpCodes.Ldtoken;
                        i.Operand = body.Method.DeclaringType;
                    }
                    else if (method.Name == "Method")
                    {
                        var str = (string)i.Previous.Operand;
                        i.OpCode = OpCodes.Ldtoken;
                        i.Operand = body.Method.DeclaringType.Methods.Single(m => m.Name == str);
                        i.Previous.OpCode = OpCodes.Nop;
                        i.Previous.Operand = null;
                    }
                    else if (method.Name == "Break")
                    {
                        i.OpCode = OpCodes.Break;
                        i.Operand = null;
                    }
                }
            }

            for (int i = 0; i < body.Variables.Count; i++)
            {
                int x = rand.Next(0, body.Variables.Count);
                var tmp = body.Variables[i];
                body.Variables[i] = body.Variables[x];
                body.Variables[x] = tmp;
            }

            int iteration = rand.Next(20, 35);
            while (iteration > 0)
            {
                MutateCode(rand, body);
                iteration--;
            }
        }
Esempio n. 7
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("");
                }
            }
            /// <summary>
            /// Convert all synchronized methods.
            /// </summary>
            private static void Convert(MethodBody body)
            {
                var typeSystem = body.Method.Module.TypeSystem;
                var monitorType = typeSystem.LookupType("System.Threading", "Monitor");
                var enterMethod = new MethodReference("Enter", typeSystem.Void, monitorType);
                enterMethod.Parameters.Add(new ParameterDefinition(typeSystem.Object));
                var exitMethod = new MethodReference("Exit", typeSystem.Void, monitorType);
                exitMethod.Parameters.Add(new ParameterDefinition(typeSystem.Object));
                var firstInstr = body.Instructions[0];

                // Expand macro's
                body.SimplifyMacros();

                // Prepare new return
                var retSeq = new ILSequence();
                retSeq.Emit(OpCodes.Nop);
                retSeq.Emit(OpCodes.Ret);

                // Monitor.Enter(this)
                var initSeq = new ILSequence();
                initSeq.Emit(OpCodes.Ldarg_0); // ld this
                initSeq.Emit(OpCodes.Call, enterMethod);
                initSeq.InsertTo(0, body);

                // Leave sequence
                var leaveSeq = new ILSequence();
                leaveSeq.Emit(OpCodes.Nop);
                leaveSeq.Emit(OpCodes.Leave, retSeq.First);
                leaveSeq.AppendTo(body);

                // Finally: Monitor.Exit(this)
                var finallySeq = new ILSequence();
                finallySeq.Emit(OpCodes.Ldarg_0); // ld this
                finallySeq.Emit(OpCodes.Call, exitMethod);
                finallySeq.Emit(OpCodes.Endfinally);
                finallySeq.AppendTo(body);

                // Replace Ret instructions
                foreach (var instr in body.Instructions.Where(x => x.OpCode.Code == Mono.Cecil.Cil.Code.Ret))
                {
                    if (instr.Next == leaveSeq.First)
                    {
                        instr.ChangeToNop();
                    }
                    else
                    {
                        instr.OpCode = OpCodes.Br;
                        instr.Operand = leaveSeq.First;
                    }
                }

                // Append ret sequence
                retSeq.AppendTo(body);

                // Update offsets
                body.ComputeOffsets();

                // Add try/finally block
                var handler = new ExceptionHandler(ExceptionHandlerType.Finally);
                handler.TryStart = firstInstr;
                handler.TryEnd = finallySeq.First; // leaveSeq.Last;
                handler.HandlerStart = finallySeq.First;
                handler.HandlerEnd = retSeq.First; // finallySeq.Last;
                body.ExceptionHandlers.Insert(0, handler);
            }
Esempio n. 9
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. 10
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. 11
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();
        }