public override bool ApplyPatch(MethodDefinition original, ILWeaver weaver, AssemblyDefinition oxideassembly, Patcher patcher = null) { // Get the call hook method MethodDefinition callhookmethod = oxideassembly.MainModule.Types .Single((t) => t.FullName == "Oxide.Core.Interface") .Methods.Single((m) => m.IsStatic && m.Name == "CallHook"); // Start injecting where requested weaver.Pointer = InjectionIndex; // Get the existing instruction we're going to inject behind Instruction existing; try { existing = weaver.Instructions[weaver.Pointer]; } catch (ArgumentOutOfRangeException) { ShowMsg(string.Format("The injection index specified for {0} is invalid!", Name), "Invalid Index", patcher); return false; } // Load the hook name // Push the arguments array to the stack and make the call VariableDefinition argsvar; var firstinjected = PushArgsArray(original, weaver, out argsvar, patcher); var hookname = weaver.Add(Instruction.Create(OpCodes.Ldstr, HookName)); if (firstinjected == null) firstinjected = hookname; if (argsvar != null) weaver.Ldloc(argsvar); else weaver.Add(Instruction.Create(OpCodes.Ldnull)); weaver.Add(Instruction.Create(OpCodes.Call, original.Module.Import(callhookmethod))); // Deal with the return value DealWithReturnValue(original, argsvar, weaver); // Find all instructions which pointed to the existing and redirect them for (int i = 0; i < weaver.Instructions.Count; i++) { Instruction ins = weaver.Instructions[i]; if (ins.Operand != null && ins.Operand.Equals(existing)) { // Check if the instruction lies within our injection range // If it does, it's an instruction we just injected so we don't want to edit it if (i < InjectionIndex || i > weaver.Pointer) ins.Operand = firstinjected; } } return true; }
public override bool ApplyPatch(MethodDefinition original, ILWeaver weaver, AssemblyDefinition oxidemodule, Patcher patcher = null) { var insts = new List<Instruction>(); foreach (var instructionData in Instructions) { var instruction = CreateInstruction(original, weaver, instructionData, insts, patcher); if (instruction == null) return false; insts.Add(instruction); } // Start injecting where requested weaver.Pointer = InjectionIndex; if (!weaver.RemoveAfter(RemoveCount)) { ShowMsg(string.Format("The remove count specified for {0} is invalid!", Name), "Invalid Remove Count", patcher); return false; } if (Instructions.Count == 0) return true; // Get the existing instruction we're going to inject behind Instruction existing; try { existing = weaver.Instructions[weaver.Pointer]; } catch (ArgumentOutOfRangeException) { ShowMsg(string.Format("The injection index specified for {0} is invalid!", Name), "Invalid Index", patcher); return false; } foreach (var inst in insts) weaver.Add(inst); // Find all instructions which pointed to the existing and redirect them for (int i = 0; i < weaver.Instructions.Count; i++) { Instruction ins = weaver.Instructions[i]; if (ins.Operand != null && ins.Operand.Equals(existing)) { // Check if the instruction lies within our injection range // If it does, it's an instruction we just injected so we don't want to edit it if (i < InjectionIndex || i > weaver.Pointer) ins.Operand = insts[0]; } } return true; }
public override bool ApplyPatch(MethodDefinition original, ILWeaver weaver, AssemblyDefinition oxideassembly, Patcher patcher = null) { MethodDefinition initoxidemethod = oxideassembly.MainModule.Types .Single((t) => t.FullName == "Oxide.Core.Interface") .Methods.Single((m) => m.IsStatic && m.Name == "Initialize"); // Start injecting where requested weaver.Pointer = InjectionIndex; // Get the existing instruction we're going to inject behind Instruction existing; try { existing = weaver.Instructions[weaver.Pointer]; } catch (ArgumentOutOfRangeException) { ShowMsg(string.Format("The injection index specified for {0} is invalid!", Name), "Invalid Index", patcher); return false; } // Load the hook name Instruction firstinjected = weaver.Add(Instruction.Create(OpCodes.Call, weaver.Module.Import(initoxidemethod))); // Find all instructions which pointed to the existing and redirect them for (int i = 0; i < weaver.Instructions.Count; i++) { Instruction ins = weaver.Instructions[i]; if (ins.Operand != null && ins.Operand.Equals(existing)) { // Check if the instruction lies within our injection range // If it does, it's an instruction we just injected so we don't want to edit it if (i < InjectionIndex || i > weaver.Pointer) ins.Operand = firstinjected; } } return true; }
static void Main(string[] args) { Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; AppDomain.CurrentDomain.AssemblyResolve += (sender, args1) => { String resourceName = "OxidePatcher.Dependencies." + new AssemblyName(args1.Name).Name + ".dll"; if (resourceName.Contains("resources.dll")) { return null; } using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)) { Byte[] assemblyData = new Byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } }; if (args.Length == 0) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new PatcherForm()); } else { bool console = false; string filename = "RustExperimental.opj"; bool unflagAll = false; string targetOverride = ""; string error = ""; int n = 0; while (n < args.Length) { if (args[n].Contains("-unflag")) { unflagAll = true; } else if (!args[n].StartsWith("-") && args[n].EndsWith(".opj")) { filename = args[n]; } else if (args[n].Contains("-c")) { console = true; } else if (args[n].Contains("-p")) { try { if (!args[n + 1].StartsWith("-") && !(args[n + 1].EndsWith(".opj"))) { targetOverride = args[n + 1]; n++; } else if (args[n + 1].StartsWith("-")) { error = "-p requires a file path."; } } catch (IndexOutOfRangeException) { error = "-p requires a file path."; } } else { error = "Unknown or invalid option: " + args[n]; } n++; } if (console) { // redirect console output to parent process; // must be before any calls to Console.WriteLine() AttachConsole(ATTACH_PARENT_PROCESS); } if (error != "" && console) { Console.WriteLine("ERROR: " + error); return; } else if (error != "") { MessageBox.Show(error, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } if (console && !Directory.Exists(targetOverride) && targetOverride != "") { Console.WriteLine(targetOverride + " does not exist!"); return; } else if (!Directory.Exists(targetOverride) && targetOverride != "") { MessageBox.Show(targetOverride + " does not exist!", "File Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (console && !System.IO.File.Exists(filename)) { Console.WriteLine(filename + " does not exist!"); return; } else if (!System.IO.File.Exists(filename)) { MessageBox.Show(filename + " does not exist!", "File Not Found", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Project PatchProject = null; if (targetOverride == "") { PatchProject = Project.Load(filename); } else { PatchProject = Project.Load(filename, targetOverride); } if (unflagAll) { unflag(PatchProject, filename, console); } if (!console) { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new PatcherForm(filename)); } else { Patcher patcher = new Patcher(PatchProject); patcher.Patch(true); Console.WriteLine("Press Enter to continue..."); } } }
/// <summary> /// Patches this hook into the target weaver /// </summary> /// <param name="original"></param> /// <param name="weaver"></param> /// <param name="oxidemodule"></param> /// <param name="patcher"></param> public abstract bool ApplyPatch(MethodDefinition original, ILWeaver weaver, AssemblyDefinition oxidemodule, Patcher patcher = null);
/// <summary> /// PrePatches this hook into the target weaver /// </summary> /// <param name="weaver"></param> /// <param name="oxidemodule"></param> /// <param name="original"></param> /// <param name="patcher"></param> public bool PreparePatch(MethodDefinition original, ILWeaver weaver, AssemblyDefinition oxidemodule, Patcher patcher = null) { if (BaseHook != null) { return BaseHook.PreparePatch(original, weaver, oxidemodule, patcher) && BaseHook.ApplyPatch(original, weaver, oxidemodule, patcher); } return true; }
protected void ShowMsg(string msg, string header, Patcher patcher) { if (patcher != null) patcher.Log(msg); else MessageBox.Show(msg, header, MessageBoxButtons.OK, MessageBoxIcon.Error); }
private Instruction CreateInstruction(MethodDefinition method, ILWeaver weaver, InstructionData instructionData, List<Instruction> insts, Patcher patcher) { var opcode = opCodes[instructionData.OpCode]; var optype = instructionData.OpType; Instruction Instruction = null; switch (optype) { case OpType.None: Instruction = Instruction.Create(opcode); break; case OpType.Byte: Instruction = Instruction.Create(opcode, Convert.ToByte(instructionData.Operand)); break; case OpType.SByte: Instruction = Instruction.Create(opcode, Convert.ToSByte(instructionData.Operand)); break; case OpType.Int32: Instruction = Instruction.Create(opcode, Convert.ToInt32(instructionData.Operand)); break; case OpType.Int64: Instruction = Instruction.Create(opcode, Convert.ToInt64(instructionData.Operand)); break; case OpType.Single: Instruction = Instruction.Create(opcode, Convert.ToSingle(instructionData.Operand)); break; case OpType.Double: Instruction = Instruction.Create(opcode, Convert.ToDouble(instructionData.Operand)); break; case OpType.String: Instruction = Instruction.Create(opcode, Convert.ToString(instructionData.Operand)); break; case OpType.VerbatimString: Instruction = Instruction.Create(opcode, Regex.Unescape(Convert.ToString(instructionData.Operand))); break; case OpType.Instruction: var index = Convert.ToInt32(instructionData.Operand); Instruction = Instruction.Create(opcode, index < 1024 ? weaver.Instructions[index] : insts[index - 1024]); break; case OpType.Variable: Instruction = Instruction.Create(opcode, method.Body.Variables[Convert.ToInt32(instructionData.Operand)]); break; case OpType.Parameter: Instruction = Instruction.Create(opcode, method.Parameters[Convert.ToInt32(instructionData.Operand)]); break; case OpType.Field: var fieldData = Convert.ToString(instructionData.Operand).Split('|'); var fieldType = GetType(fieldData[0], fieldData[1], patcher); if (fieldType == null) return null; var fieldField = fieldType.Fields.FirstOrDefault(f => f.Name.Equals(fieldData[2])); if (fieldField == null) { ShowMsg(string.Format("The Field '{0}' for '{1}' could not be found!", fieldData[2], Name), "Missing Field", patcher); return null; } Instruction = Instruction.Create(opcode, method.Module.Import(fieldField)); break; case OpType.Method: var methodData = Convert.ToString(instructionData.Operand).Split('|'); var methodType = GetType(methodData[0], methodData[1], patcher); if (methodType == null) return null; var methodMethod = methodType.Methods.FirstOrDefault(f => f.Name.Equals(methodData[2])); if (methodMethod == null) { ShowMsg(string.Format("The Method '{0}' for '{1}' could not be found!", methodData[2], Name), "Missing Method", patcher); return null; } Instruction = Instruction.Create(opcode, method.Module.Import(methodMethod)); break; case OpType.Generic: break; case OpType.Type: var typeData = Convert.ToString(instructionData.Operand).Split('|'); var typeType = GetType(typeData[0], typeData[1], patcher); if (typeType == null) return null; Instruction = Instruction.Create(opcode, method.Module.Import(typeType)); break; default: throw new ArgumentOutOfRangeException(); } return Instruction; }
private TypeDefinition GetType(string assemblyName, string typeName, Patcher patcher) { var targetDir = patcher != null ? patcher.PatchProject.TargetDirectory : PatcherForm.MainForm.CurrentProject.TargetDirectory; var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(targetDir); string filename = Path.Combine(targetDir, assemblyName.Replace(".dll", "") + ".dll"); var assem = AssemblyDefinition.ReadAssembly(filename, new ReaderParameters { AssemblyResolver = resolver }); if (assem == null) { ShowMsg(string.Format("The Assembly '{0}' for '{1}' could not be found!", assemblyName, Name), "Missing Assembly", patcher); return null; } var type = assem.MainModule.GetType(typeName); if (type == null) { ShowMsg(string.Format("The Type '{0}' for '{1}' could not be found!", typeName, Name), "Missing Type", patcher); return null; } return type; }
private void Worker() { WorkerWriteLog("Started patching."); try { Patcher patcher = new Patcher(PatchProject); patcher.OnLogMessage += (msg) => WorkerWriteLog(msg); patcher.Patch(); } catch (Exception ex) { WorkerWriteLog("ERROR: {0}", ex.Message); } WorkerWriteLog("Patch complete."); WorkerCompleteWork(); }
private bool GetFieldOrProperty(ILWeaver weaver, MethodDefinition originalMethod, TypeDefinition currentArg, string[] target, Patcher patcher) { if (resolver == null) { resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(patcher != null ? patcher.PatchProject.TargetDirectory : PatcherForm.MainForm.CurrentProject.TargetDirectory); } if (currentArg == null || target == null || target.Length == 0) return false; int i; var arg = currentArg; for (i = 0; i < target.Length; i++) { if (GetFieldOrProperty(weaver, originalMethod, ref arg, target[i])) continue; ShowMsg($"Could not find the field or property `{target[i]}` in any of the base classes or interfaces of `{currentArg.Name}`.", "Invalid field or property", patcher); break; } if (arg.IsValueType || arg.IsByReference) weaver.Add(arg.Module == originalMethod.Module ? Instruction.Create(OpCodes.Box, arg.Resolve()) : Instruction.Create(OpCodes.Box, originalMethod.Module.Import(arg.Resolve()))); return i >= 1; }
private Instruction PushArgsArray(MethodDefinition method, ILWeaver weaver, out VariableDefinition argsvar, Patcher patcher) { // Are we going to use arguments? if (ArgumentBehavior == Hooks.ArgumentBehavior.None) { // Push null and we're done argsvar = null; return null; } // Create array variable Instruction firstInstruction; // Are we using the argument string? if (ArgumentBehavior == Hooks.ArgumentBehavior.UseArgumentString) { string retvalue; string[] args = ParseArgumentString(out retvalue); if (args == null) { // Silently fail, but at least produce valid IL argsvar = null; return null; } // Create the array argsvar = weaver.AddVariable(new ArrayType(method.Module.TypeSystem.Object), "args"); firstInstruction = weaver.Add(ILWeaver.Ldc_I4_n(args.Length)); weaver.Add(Instruction.Create(OpCodes.Newarr, method.Module.TypeSystem.Object)); weaver.Stloc(argsvar); // Populate it for (int i = 0; i < args.Length; i++) { string arg = args[i].ToLowerInvariant(); string[] target = null; if (!string.IsNullOrEmpty(arg) && args[i].Contains(".")) { string[] split = args[i].Split('.'); arg = split[0]; target = split.Skip(1).ToArray(); } weaver.Ldloc(argsvar); weaver.Add(ILWeaver.Ldc_I4_n(i)); if (string.IsNullOrEmpty(arg)) weaver.Add(Instruction.Create(OpCodes.Ldnull)); else if (arg == "this") { if (method.IsStatic) weaver.Add(Instruction.Create(OpCodes.Ldnull)); else weaver.Add(ILWeaver.Ldarg(null)); GetFieldOrProperty(weaver, method, method.DeclaringType.Resolve(), target, patcher); } else if (arg[0] == 'p' || arg[0] == 'a') { int index; if (int.TryParse(arg.Substring(1), out index)) { ParameterDefinition pdef; /*if (method.IsStatic) pdef = method.Parameters[index]; else pdef = method.Parameters[index + 1];*/ pdef = method.Parameters[index]; weaver.Add(ILWeaver.Ldarg(pdef)); if (pdef.ParameterType.IsByReference) { weaver.Add(Instruction.Create(OpCodes.Ldobj, pdef.ParameterType)); weaver.Add(Instruction.Create(OpCodes.Box, pdef.ParameterType)); } if (!GetFieldOrProperty(weaver, method, pdef.ParameterType.Resolve(), target, patcher) && pdef.ParameterType.IsValueType) weaver.Add(Instruction.Create(OpCodes.Box, pdef.ParameterType)); } else weaver.Add(Instruction.Create(OpCodes.Ldnull)); } else if (arg[0] == 'l' || arg[0] == 'v') { int index; if (int.TryParse(arg.Substring(1), out index)) { VariableDefinition vdef = weaver.Variables[index]; weaver.Ldloc(vdef); if (vdef.VariableType.IsByReference) { weaver.Add(Instruction.Create(OpCodes.Ldobj, vdef.VariableType)); weaver.Add(Instruction.Create(OpCodes.Box, vdef.VariableType)); } if (!GetFieldOrProperty(weaver, method, vdef.VariableType.Resolve(), target, patcher) && vdef.VariableType.IsValueType) weaver.Add(Instruction.Create(OpCodes.Box, vdef.VariableType)); } else weaver.Add(Instruction.Create(OpCodes.Ldnull)); } else weaver.Add(Instruction.Create(OpCodes.Ldnull)); weaver.Add(Instruction.Create(OpCodes.Stelem_Ref)); } } else { // Figure out what we're doing bool includeargs = ArgumentBehavior == Hooks.ArgumentBehavior.All || ArgumentBehavior == Hooks.ArgumentBehavior.JustParams; bool includethis = ArgumentBehavior == Hooks.ArgumentBehavior.All || ArgumentBehavior == Hooks.ArgumentBehavior.JustThis; if (method.IsStatic) includethis = false; // Work out what arguments we're going to transmit List<ParameterDefinition> args = new List<ParameterDefinition>(); if (includeargs) { for (int i = 0; i < method.Parameters.Count; i++) { ParameterDefinition arg = method.Parameters[i]; if (!arg.IsOut) args.Add(arg); } } argsvar = weaver.AddVariable(new ArrayType(method.Module.TypeSystem.Object), "args"); // Load arg count, create array, store if (includethis) firstInstruction = weaver.Add(ILWeaver.Ldc_I4_n(args.Count + 1)); else firstInstruction = weaver.Add(ILWeaver.Ldc_I4_n(args.Count)); weaver.Add(Instruction.Create(OpCodes.Newarr, method.Module.TypeSystem.Object)); weaver.Stloc(argsvar); // Include this if (includethis) { weaver.Ldloc(argsvar); weaver.Add(ILWeaver.Ldc_I4_n(0)); weaver.Add(ILWeaver.Ldarg(null)); weaver.Add(Instruction.Create(OpCodes.Stelem_Ref)); } // Loop each argument for (int i = 0; i < args.Count; i++) { // Load array, load index load arg, store in array ParameterDefinition arg = args[i]; weaver.Ldloc(argsvar); if (includethis) weaver.Add(ILWeaver.Ldc_I4_n(i + 1)); else weaver.Add(ILWeaver.Ldc_I4_n(i)); weaver.Add(ILWeaver.Ldarg(args[i])); if (arg.ParameterType.IsByReference) { weaver.Add(Instruction.Create(OpCodes.Ldobj, arg.ParameterType)); weaver.Add(Instruction.Create(OpCodes.Box, arg.ParameterType)); } else if (arg.ParameterType.IsValueType) weaver.Add(Instruction.Create(OpCodes.Box, arg.ParameterType)); weaver.Add(Instruction.Create(OpCodes.Stelem_Ref)); } } return firstInstruction; }