async internal Task <Dictionary <string, object> > ApplyPatch(JObject data, ProgressDelegate progressDelegate, CoreDelegates coreDelegates) { AssemblyDefinition assembly = null; EntryPointDefinitions sourceDefs = null; string tempFile = string.Empty; try { PatchConfig config = new PatchConfig((JObject)data["patchConfig"]); EntryPoint sourcePoint = config.SourceEntryPoint as EntryPoint; if (!File.Exists(sourcePoint.AssemblyPath)) { throw new FileNotFoundException($"{sourcePoint.AssemblyPath} does not exist"); } sourceDefs = sourcePoint.GetDefinitions() as EntryPointDefinitions; if (!sourceDefs.IsEntryPointValid) { throw new EntryPointNotFoundException($"Unable to find {sourcePoint.AssemblyPath}_{sourcePoint.ToString ()} source entry point. Expected format is Namespace.className::methodName"); } string dataPath = await coreDelegates.context.GetDataPath(); string VMLPath = await coreDelegates.context.GetModLoaderPath(); foreach (EntryPoint point in config.TargetEntryPoints) { if (!File.Exists(point.AssemblyPath)) { throw new FileNotFoundException($"{point.AssemblyPath} does not exist"); } // TODO: Might have to change this to only use the entry point's dependency path. // More testing is needed. string [] assemblyResolverPaths = (dataPath != VMLPath) ? new string [] { point.DependencyPath, dataPath, VMLPath } : new string [] { point.DependencyPath, dataPath }; m_resolver = new MissingAssemblyResolver(assemblyResolverPaths); tempFile = Util.GetTempFile(point.AssemblyPath); using (assembly = AssemblyDefinition.ReadAssembly(tempFile, new ReaderParameters { AssemblyResolver = m_resolver })) { if (IsInjected(assembly, config.SourceEntryPoint, point)) { throw new EntryPointInjectedException($"{point.AssemblyPath}_{point.ToString ()} is already injected"); } TypeDefinition typeDef = assembly.MainModule.GetType(point.TypeName); MethodDefinition methDef = typeDef.Methods.First(x => x.Name == point.MethodName); ILProcessor ilProcessor = methDef.Body.GetILProcessor(); if (sourcePoint.ExpandoObjectData != string.Empty) { ilProcessor.InsertBefore(methDef.Body.Instructions [0], Instruction.Create(OpCodes.Ldstr, sourcePoint.ExpandoObjectData)); ilProcessor.InsertBefore(methDef.Body.Instructions [1], Instruction.Create(OpCodes.Call, methDef.Module.ImportReference(sourceDefs.MethodDef))); } else { ilProcessor.InsertBefore(methDef.Body.Instructions [0], Instruction.Create(OpCodes.Call, methDef.Module.ImportReference(sourceDefs.MethodDef))); } assembly.Write(point.AssemblyPath); } Util.Dispose(ref assembly); Util.DeleteTemp(tempFile); tempFile = string.Empty; } Util.Dispose(ref sourceDefs); } catch (Exception exc) { Util.Dispose(ref assembly); Util.Dispose(ref sourceDefs); if (tempFile != string.Empty) { Util.DeleteTemp(tempFile); tempFile = string.Empty; } return(PatchHelper.CreatePatchResult(false, exc.Message)); } return(PatchHelper.CreatePatchResult(true, "Patch Applied")); }
async internal Task <Dictionary <string, object> > RemovePatch(JObject data, ProgressDelegate progressDelegate, CoreDelegates coreDelegates) { AssemblyDefinition assembly = null; EntryPointDefinitions sourceDefs = null; string tempFile = string.Empty; // This should rarely be used when deploying VML as Vortex itself would remove the // the assemblies when the user decides to purge. try { PatchConfig config = new PatchConfig((JObject)data ["patchConfig"]); EntryPoint sourcePoint = config.SourceEntryPoint as EntryPoint; if (!File.Exists(sourcePoint.AssemblyPath)) { throw new FileNotFoundException($"{sourcePoint.AssemblyPath} does not exist"); } sourceDefs = sourcePoint.GetDefinitions() as EntryPointDefinitions; if (!sourceDefs.IsEntryPointValid) { throw new EntryPointNotFoundException($"Unable to find {sourcePoint.AssemblyPath}_{sourcePoint.ToString ()} source entry point. Expected format is Namespace.className::methodName"); } string dataPath = await coreDelegates.context.GetDataPath(); string VMLPath = await coreDelegates.context.GetModLoaderPath(); foreach (EntryPoint point in config.TargetEntryPoints) { if (!File.Exists(point.AssemblyPath)) { throw new FileNotFoundException($"{point.AssemblyPath} does not exist"); } // TODO: Might have to change this to only use the entry point's dependency path. // More testing is needed. string [] assemblyResolverPaths = (dataPath != VMLPath) ? new string [] { point.DependencyPath, dataPath, VMLPath } : new string [] { point.DependencyPath, dataPath }; m_resolver = new MissingAssemblyResolver(assemblyResolverPaths); tempFile = Util.GetTempFile(point.AssemblyPath); using (assembly = AssemblyDefinition.ReadAssembly(tempFile, new ReaderParameters { ReadWrite = true, AssemblyResolver = m_resolver })) { // If the assembly is not injected - there's nothing to remove. // This shouldn't be treated as an error although we do break // out using an exception. if (!IsInjected(assembly, sourcePoint, point)) { throw new EntryPointInjectedException($"{point.AssemblyPath}_{point.ToString()} is not injected."); } TypeDefinition typeDef = assembly.MainModule.GetType(point.TypeName); MethodDefinition methDef = typeDef.Methods.First(x => x.Name == point.MethodName); var instructions = methDef.Body.Instructions .Where(instr => instr.OpCode == OpCodes.Call) .ToArray(); Instruction patchInstr = instructions.FirstOrDefault(instr => instr.Operand.ToString().Contains(point.ToString())); methDef.Body.Instructions.Remove(patchInstr); assembly.Write(point.AssemblyPath); } Util.Dispose(ref assembly); Util.DeleteTemp(tempFile); tempFile = string.Empty; } Util.Dispose(ref sourceDefs); } catch (Exception exc) { Util.Dispose(ref assembly); Util.Dispose(ref sourceDefs); if (tempFile != string.Empty) { Util.DeleteTemp(tempFile); tempFile = string.Empty; } return(PatchHelper.CreatePatchResult(false, exc.Message)); } return(PatchHelper.CreatePatchResult(true, "Patch removed successfully")); }
async internal Task <Dictionary <string, object> > IsPatchApplicable(JObject data, ProgressDelegate progressDelegate, CoreDelegates coreDelegates) { AssemblyDefinition assembly = null; EntryPointDefinitions sourceDefs = null; EntryPointDefinitions targetDefs = null; string tempFile = string.Empty; try { PatchConfig config = new PatchConfig((JObject)data["patchConfig"]); EntryPoint sourcePoint = config.SourceEntryPoint as EntryPoint; if (!File.Exists(sourcePoint.AssemblyPath)) { throw new FileNotFoundException($"{sourcePoint.AssemblyPath} does not exist"); } sourceDefs = sourcePoint.GetDefinitions() as EntryPointDefinitions; if (!sourceDefs.IsEntryPointValid) { throw new EntryPointNotFoundException($"Unable to find {sourcePoint.AssemblyPath}_{sourcePoint.ToString ()} target entry point. Expected format is Namespace.className::methodName"); } string dataPath = await coreDelegates.context.GetDataPath(); string VMLPath = await coreDelegates.context.GetModLoaderPath(); foreach (EntryPoint point in config.TargetEntryPoints) { if (!File.Exists(point.AssemblyPath)) { throw new FileNotFoundException($"{point.AssemblyPath} does not exist"); } // TODO: Might have to change this to only use the entry point's dependency path. // More testing is needed. string [] assemblyResolverPaths = (dataPath != VMLPath) ? new string [] { point.DependencyPath, dataPath, VMLPath } : new string [] { point.DependencyPath, dataPath }; targetDefs = point.GetDefinitions() as EntryPointDefinitions; if (!targetDefs.IsEntryPointValid) { throw new EntryPointNotFoundException($"Unable to find {point.AssemblyPath}{point.ToString ()} target entry point. Expected format is Namespace.className::methodName"); } m_resolver = new MissingAssemblyResolver(assemblyResolverPaths); tempFile = Util.GetTempFile(point.AssemblyPath); using (assembly = AssemblyDefinition.ReadAssembly(tempFile, new ReaderParameters { AssemblyResolver = m_resolver })) { if (IsInjected(assembly, config.SourceEntryPoint, point)) { throw new EntryPointInjectedException($"{point.AssemblyPath}_{point.ToString()} is already injected"); } } Util.Dispose(ref assembly); Util.Dispose(ref targetDefs); Util.DeleteTemp(tempFile); tempFile = string.Empty; } } catch (Exception exc) { Util.Dispose(ref assembly); Util.Dispose(ref sourceDefs); Util.Dispose(ref targetDefs); if (tempFile != string.Empty) { Util.DeleteTemp(tempFile); tempFile = string.Empty; } return(PatchHelper.CreatePatchResult(false, exc.Message)); } return(PatchHelper.CreatePatchResult(true, "Patch is applicable")); }