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(); }
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(); }
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(); }
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--; } }
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); }
// 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; } } }
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(""); } } }
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(); }