using Mono.Cecil; using Mono.Cecil.Cil; using ReCodeIt.Utils; namespace ReCodeItLib.Dumper; public class Dumper { public static void CreateDumper(string managedPath) { var resolver = new DefaultAssemblyResolver(); resolver.AddSearchDirectory(managedPath); // remove these dirs so it resolves to managed folder only resolver.RemoveSearchDirectory("."); resolver.RemoveSearchDirectory("bin"); var readerParameters = new ReaderParameters { AssemblyResolver = resolver }; // Assembly-CSharp section var assemblyPath = Path.Combine(managedPath, "Assembly-Csharp.dll"); var dumperOutputPath = Path.Combine(managedPath, "dumper"); var dumperBackupPath = Path.Combine(dumperOutputPath, "backup"); var dumperDataFolder = Path.Combine(dumperOutputPath, "DUMPDATA"); var newAssemblyPath = Path.Combine(dumperOutputPath, "Assembly-CSharp.dll"); Directory.CreateDirectory(dumperOutputPath); Directory.CreateDirectory(dumperBackupPath); Directory.CreateDirectory(dumperDataFolder); File.Copy(Path.Combine(managedPath, "Assembly-CSharp.dll"), Path.Combine(dumperBackupPath, "Assembly-CSharp.dll"), true); File.Copy(Path.Combine(managedPath, "FilesChecker.dll"), Path.Combine(dumperBackupPath, "FilesChecker.dll"), true); File.Copy(".\\Assets\\Dumper\\DumpLib.dll", Path.Combine(dumperOutputPath, "DumpLib.dll"), true); File.Copy(".\\Assets\\Dumper\\DUMPDATA\\botReqData.json", dumperDataFolder + "\\botreqData.json", true); File.Copy(".\\Assets\\Dumper\\DUMPDATA\\config.json", dumperDataFolder + "\\config.json", true); File.Copy(".\\Assets\\Dumper\\DUMPDATA\\raidSettings.json", dumperDataFolder + "\\raidSettings.json", true); // loads assembly var oldAssembly = AssemblyDefinition.ReadAssembly(assemblyPath, readerParameters); // gets all types var types = oldAssembly.MainModule.GetTypes().ToList(); // finds and checks for type with backRequest var backRequestType = types.Where(DumpyTypeHelper.GetBackRequestType).ToList(); CheckNullOrMulti(backRequestType, "BackRequest"); // finds and checks for type with ValidateCertificate var validateCertificateType = types.Where(DumpyTypeHelper.GetValidateCertificateType).ToList(); CheckNullOrMulti(validateCertificateType, "ValidateCertificate"); // finds and checks for type with RunValidation var runValidationType = types.Where(DumpyTypeHelper.GetRunValidationType).ToList(); CheckNullOrMulti(runValidationType, "RunValidation"); var dumpyTaskType = types.Where(DumpyTypeHelper.GetMenuScreenType).ToList(); CheckNullOrMulti(dumpyTaskType, "DumpyTask"); // apply code changes SetBackRequestCode(oldAssembly, backRequestType[0]); SetValidateCertificateCode(validateCertificateType[0]); SetRunValidationCode(oldAssembly, runValidationType[0]); SetDumpyTaskCode(oldAssembly, dumpyTaskType[0]); // write modified assembly to file oldAssembly.Write(newAssemblyPath); // FilesChecker section var oldFilesCheckerPath = Path.Combine(managedPath, "FilesChecker.dll"); var oldFilesChecker = AssemblyDefinition.ReadAssembly(oldFilesCheckerPath, readerParameters); var newFilesCheckerPath = Path.Combine(dumperOutputPath, Path.GetFileName(oldFilesCheckerPath)); // gets all types types = oldFilesChecker.MainModule.GetTypes().ToList(); // finds and checks for type called EnsureConsistency var ensureConsistencyType = types.Where(DumpyTypeHelper.GetEnsureConsistencyType).ToList(); CheckNullOrMulti(ensureConsistencyType, "EnsureConsistency"); // apply code changes SetEnsureConsistencyCode(oldFilesChecker, ensureConsistencyType[0]); SetEnsureConsistencySingleCode(oldFilesChecker, ensureConsistencyType[0]); // Write modified assembly to file oldFilesChecker.Write(newFilesCheckerPath); } /// /// Finds the method with backRequest and bResponse as params. /// Checks the method instructions before modification has a count of 269, /// if this is not the case, this needs to be checked. /// This type passed in is the only type with this method. /// /// /// private static void SetBackRequestCode(AssemblyDefinition oldAssembly, TypeDefinition type) { // find method var method = type.Methods.First(x => x.Parameters.Any(p => p.Name is "backRequest") && x.Parameters.Any(p => p.Name == "bResponse")); if (method == null || method.Body.Instructions.Count != 269) { Logger.Log($"BackRequest Instructions count has changed from 269 to {method.Body.Instructions.Count}", ConsoleColor.Red); } // where we insert the new instructions var startOfInstructions = 252; var processor = method.Body.GetILProcessor(); var liList = DumpyInstructionsHelper.GetBackRequestInstructions(oldAssembly, method); var index = method.Body.Instructions[startOfInstructions]; foreach (var item in liList) { processor.InsertBefore(index, item); } var ins = Instruction.Create(OpCodes.Brfalse_S, method.Body.Instructions[startOfInstructions]); // create instruction to jump to index 252 method.Body.Instructions[220] = ins; // instruction to jump from 202 to 252 } /// /// Finds the method called ValidateCertificate. /// Checks that we found two of these methods, /// if this is not the case, this needs to be checked. /// This type passed in is the only type with this method. /// /// /// private static void SetValidateCertificateCode(TypeDefinition type) { var methods = type.Methods.Where(x => x.Name == "ValidateCertificate"); // should be 2 // check make sure nothing has changed var firstMethod = methods.FirstOrDefault(m => m.Parameters.Any(p => p.Name == "certificate")); var secondMethod = methods.FirstOrDefault(m => m.Parameters.Any(p => p.Name == "certificateData")); if (firstMethod?.Body.Instructions.Count != 55 || secondMethod?.Body.Instructions.Count != 14) { var errorMessage = $"Instruction count has changed, method with 'certificate' as a param - before: 51, now: {firstMethod.Body.Instructions.Count}, " + $"method with 'certificateData' as a param - before: 14, now: {secondMethod.Body.Instructions.Count}"; Logger.Log(errorMessage, ConsoleColor.Red); } if (methods.Count() != 2) { Logger.Log($"ValidateCertificate should be found twice, count was: {methods.Count()}", ConsoleColor.Red); } foreach (var method in methods) { // clear these from the body. method.Body.Instructions.Clear(); method.Body.Variables.Clear(); method.Body.ExceptionHandlers.Clear(); // return true; var ins = Instruction.Create(OpCodes.Ldc_I4_1); var ins1 = Instruction.Create(OpCodes.Ret); // add instructions method.Body.Instructions.Add(ins); method.Body.Instructions.Add(ins1); } } /// /// Finds the method called RunValidation and MoveNext. /// Checks that we found two of these methods, /// if this is not the case, this needs to be checked. /// This type passed in is the only type with this method. /// /// /// private static void SetRunValidationCode(AssemblyDefinition oldAssembly, TypeDefinition type) { var method = type.Methods.First(x => x.Name == "RunValidation"); var method2 = type.NestedTypes[0].Methods.First(x => x.Name == "MoveNext"); if (method == null || method.Body.Instructions.Count != 25) { Logger.Log($"RunValidation Instructions count has changed from 25 to {method.Body.Instructions.Count}", ConsoleColor.Red); } if (method2 == null || method2.Body.Instructions.Count != 171) { Logger.Log($"RunValidation's MoveNext Instructions count has changed from 171 to {method2.Body.Instructions.Count}", ConsoleColor.Red); } // Clear these from the body of each method respectively method.Body.Instructions.Clear(); method2.Body.Instructions.Clear(); method2.Body.Variables.Clear(); method2.Body.ExceptionHandlers.Clear(); var processor = method.Body.GetILProcessor(); var processor2 = method2.Body.GetILProcessor(); var liList = DumpyInstructionsHelper.GetRunValidationInstructions(oldAssembly, method); var liList2 = DumpyInstructionsHelper.GetRunValidationInstructionsMoveNext(oldAssembly, method2); foreach (var instruction in liList) { processor.Append(instruction); } foreach (var instruction in liList2) { processor2.Append(instruction); } var ins = Instruction.Create(OpCodes.Leave_S, method2.Body.Instructions[14]); // Create instruction to jump to index 14 var ins1 = Instruction.Create(OpCodes.Leave_S, method2.Body.Instructions[method2.Body.Instructions.IndexOf(method2.Body.Instructions.Last())]); // Create instruction to jump to last index processor2.InsertAfter(method2.Body.Instructions[5], ins); // Instruction to jump from 5 to 14 processor2.InsertAfter(method2.Body.Instructions[14], ins1); // Instruction to jump from 14 to last index // Create exception handler with defined indexes var handler = new ExceptionHandler(ExceptionHandlerType.Catch) { TryStart = method2.Body.Instructions[3], TryEnd = method2.Body.Instructions[7], HandlerStart = method2.Body.Instructions[7], HandlerEnd = method2.Body.Instructions[16], CatchType = method2.Module.ImportReference(typeof(Exception)), }; // Add exception handler to method body method2.Body.ExceptionHandlers.Add(handler); } /// /// Finds the method called EnsureConsistency. /// if this is not the case, this needs to be checked. /// This type passed in is the only type with this method. /// /// /// private static void SetEnsureConsistencyCode(AssemblyDefinition oldFileChecker, TypeDefinition type) { var method = type.Methods.First(x => x.Name == "EnsureConsistency"); if (method == null || method.Body.Instructions.Count != 152) { Logger.Log($"EnsureConsistency is null or Instructions count has changed from 152 to {method.Body.Instructions.Count}", ConsoleColor.Red); } var processor = method.Body.GetILProcessor(); // clear these from the method body method.Body.Instructions.Clear(); method.Body.Variables.Clear(); method.Body.ExceptionHandlers.Clear(); var liList = DumpyInstructionsHelper.GetEnsureConsistencyInstructions(oldFileChecker, method); foreach (var li in liList) { processor.Append(li); } } /// /// Finds the method called EnsureConsistencySingle. /// if this is not the case, this needs to be checked. /// This type passed in is the only type with this method. /// /// /// private static void SetEnsureConsistencySingleCode(AssemblyDefinition oldFileChecker, TypeDefinition type) { var method = type.Methods.First(x => x.Name == "EnsureConsistencySingle"); if (method == null || method.Body.Instructions.Count != 101) { Logger.Log($"EnsureConsistencySingle is null or Instructions count has changed from 101 to {method.Body.Instructions.Count}", ConsoleColor.Red); } // clear these from the method body method.Body.Instructions.Clear(); method.Body.Variables.Clear(); method.Body.ExceptionHandlers.Clear(); var processor = method.Body.GetILProcessor(); var liList = DumpyInstructionsHelper.GetEnsureConsistencyInstructions(oldFileChecker, method); foreach (var li in liList) { processor.Append(li); } } private static void SetDumpyTaskCode(AssemblyDefinition oldAssembly, TypeDefinition type) { var method = type.Methods.First(x => x.Name == "Awake"); if (method == null || method.Body.Instructions.Count != 62) { Logger.Log($"MainMenu is null or instructions have changed from 62 to {method.Body.Instructions.Count}", ConsoleColor.Red); } var processor = method.Body.GetILProcessor(); var liList = DumpyInstructionsHelper.GetDumpyTaskInstructions(oldAssembly, method); var index = method.Body.Instructions.First(x => x.OpCode == OpCodes.Ret); foreach (var item in liList) { processor.InsertBefore(index, item); } } /// /// Checks for null or multiple types /// /// ICollection /// string public static void CheckNullOrMulti(List types, string name = "") { if (types == null) { Logger.Log($"{name} was null", ConsoleColor.Red); } if (types.Count > 1) { Logger.Log($"{name} count was more than 1", ConsoleColor.Red); } } }