private static void PatchTest() { var targetPath = @"..\..\..\Patchwork.Tests.Target\bin\debug\Patchwork.Tests.Target.dll";; var dir = Path.GetDirectoryName(targetPath); var fn = Path.GetFileNameWithoutExtension(targetPath); var ext = Path.GetExtension(targetPath); var newFileName = string.Format("{0}.patched{1}", fn, ext); var newTarget = Path.Combine(dir, newFileName); File.Copy(targetPath, newTarget, true); var patcher = new AssemblyPatcher(newTarget, log: Log); var patchPath = typeof(Patchwork.Tests.Patch.TestClass).Assembly.Location; var maker = new ManifestCreator(); var manifest = maker.CreateManifest(patchPath); patcher.PatchManifest(manifest, null); patcher.WriteTo(newTarget); Log.Information("Loading assembly into memory..."); var loaded = Assembly.LoadFrom(newTarget); Log.Information("Invoking method"); var module = loaded.GetModules()[0]; var types = module.FindTypes((typ, o) => typ.Name.Contains("EntryPoint"), null); var foundType = types.Single(); var method = foundType.GetMethod("StandardTests"); try { var ret = method.Invoke(null, null); Log.Information("Result: {@Result}", ret); } catch (TargetInvocationException ex) { throw ex.InnerException; } Console.ReadKey(); }
public static int Main(string[] args) { bool interactive = args.Length > 0; gendiffs = args.Any(arg => arg == "--generatediffs" || arg == "-gd"); string logfile = (from arg in args where arg.StartsWith("--logFile:", StringComparison.Ordinal) || arg.StartsWith("-l:", StringComparison.Ordinal) select arg.Split(':').Last()).FirstOrDefault(); LogFile = !String.IsNullOrEmpty(logfile) ? logfile : LogFile; LogToFile = !String.IsNullOrEmpty(logfile); if (gendiffs) MethodDB.GetInstance(); newAssCS = true; Log($"[( Pluton Patcher v{Version} )]"); rustAssembly = AssemblyPatcher.GetPatcher("Assembly-CSharp.dll"); if (rustAssembly.GetType("PlutonPatched") != null) { LogError("Assembly-CSharp.dll is already patched!"); if (interactive) System.Threading.Thread.Sleep(250); return (int)ExitCode.ACDLL_ALREADY_PATCHED; } foreach (var json in Directory.GetFiles("./", "*.json")) { JSON.Array jsonArr = JSON.Array.Parse(File.ReadAllText(json)); foreach (JSON.Value jsonElmnt in jsonArr) { JSON.Object jsonObj = jsonElmnt.Obj; var assemblyPatch = AssemblyPatch.ParseFromJSON(jsonObj); if (!assemblyPatch.Patch()) { LogError("Failed to patch!"); if (interactive) System.Threading.Thread.Sleep(250); return (int)ExitCode.ACDLL_GENERIC_PATCH_ERR; } } } if (gendiffs) { MethodDB.GetInstance().Save(); string diffs = MethodDB.GetDifferences(); if (!String.IsNullOrEmpty(diffs)) File.WriteAllText($"diffs-{DateTime.Now.ToShortDateString()}{DateTime.Now.ToShortTimeString()}.html".Replace('\\', '_').Replace('/', '_'), "<html><head><style>del,ins{text-decoration:none}ins{background-color:#0F0}del{color:#999;background-color:#F00}</style></head><body>" + diffs + "</body></html>"); } Log("Completed!"); if (interactive) System.Threading.Thread.Sleep(250); return (int)ExitCode.SUCCESS; }
private static void PatchTest() { var targetPath = @"..\..\..\Patchwork.Tests.Target\bin\debug\Patchwork.Tests.Target.dll";; var dir = Path.GetDirectoryName(targetPath); var fn = Path.GetFileNameWithoutExtension(targetPath); var ext = Path.GetExtension(targetPath); var newFileName = string.Format("{0}.patched{1}", fn, ext); var newTarget = Path.Combine(dir, newFileName); File.Copy(targetPath, newTarget, true); var patcher = new AssemblyPatcher(newTarget, log: Log); var patchPath = typeof(Patchwork.Tests.Patch.TestClass).Assembly.Location; patcher.DebugOptions = DebugFlags.CreationOverwrites; patcher.PatchAssembly(patchPath); patcher.WriteTo(newTarget); Log.Information("Loading assembly into memory..."); var loaded = Assembly.LoadFrom(newTarget); Log.Information("Invoking method"); var module = loaded.GetModules()[0]; var types = module.FindTypes((typ, o) => typ.Name.Contains("EntryPoint"), null); var foundType = types.Single(); var method = foundType.GetMethod("StandardTests"); Process.Start(@"C:\Users\lifeg_000\AppData\Local\Microsoft\VisualStudio\12.0\Extensions\bicaz2ty.1dn\ILSpy.exe", string.Format("\"{0}\"", newTarget)); try { var ret = method.Invoke(null, null); Log.Information("Result: {@Result}", ret); } catch (TargetInvocationException ex) { throw ex.InnerException; } Console.ReadKey(); }
public static void Run() { try { AllocateConsole(); if (ConfigApplyRuntimePatches.Value) { UnityPatches.Apply(); } Logger.Sources.Add(TraceLogSource.CreateSource()); PreloaderLog = new PreloaderConsoleListener(ConfigPreloaderCOutLogging.Value); Logger.Listeners.Add(PreloaderLog); string consoleTile = $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}"; ConsoleWindow.Title = consoleTile; Logger.LogMessage(consoleTile); //See BuildInfoAttribute for more information about this section. object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false); if (attributes.Length > 0) { var attribute = (BuildInfoAttribute)attributes[0]; Logger.LogMessage(attribute.Info); } #if UNITY_2018 Logger.LogMessage("Compiled in Unity v2018 mode"); #else Logger.LogMessage("Compiled in Legacy Unity mode"); #endif Logger.LogInfo($"Running under Unity v{Process.GetCurrentProcess().MainModule.FileVersionInfo.FileVersion}"); Logger.LogMessage("Preloader started"); AssemblyPatcher.AddPatcher(new PatcherPlugin { TargetDLLs = new[] { ConfigEntrypointAssembly.Value }, Patcher = PatchEntrypoint, Name = "BepInEx.Chainloader" }); AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath, GetPatcherMethods); Logger.LogInfo($"{AssemblyPatcher.PatcherPlugins.Count} patcher plugin(s) loaded"); AssemblyPatcher.PatchAndLoad(Paths.ManagedPath); AssemblyPatcher.DisposePatchers(); Logger.LogMessage("Preloader finished"); Logger.Listeners.Remove(PreloaderLog); Logger.Listeners.Add(new ConsoleLogListener()); PreloaderLog.Dispose(); } catch (Exception ex) { try { Logger.LogFatal("Could not run preloader!"); Logger.LogFatal(ex); PreloaderLog?.Dispose(); if (!ConsoleWindow.IsAttached) { //if we've already attached the console, then the log will already be written to the console AllocateConsole(); Console.Write(PreloaderLog); } PreloaderLog = null; } finally { File.WriteAllText( Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"), PreloaderLog + "\r\n" + ex); PreloaderLog?.Dispose(); PreloaderLog = null; } } }
public static void Run() { try { ConsoleManager.Initialize(false); PreloaderLog = new PreloaderConsoleListener(); Logger.Listeners.Add(PreloaderLog); if (ConsoleManager.ConfigConsoleEnabled.Value) { ConsoleManager.CreateConsole(); Logger.Listeners.Add(new ConsoleLogListener()); } ChainloaderLogHelper.PrintLogInfo(Log); Log.LogInfo($"Running under Unity v{FileVersionInfo.GetVersionInfo(Paths.ExecutablePath).FileVersion}"); Log.LogDebug($"Game executable path: {Paths.ExecutablePath}"); Log.LogDebug($"Unhollowed assembly directory: {IL2CPPUnhollowedPath}"); Log.LogDebug($"BepInEx root path: {Paths.BepInExRootPath}"); UnhollowerLog = Logger.CreateLogSource("Unhollower"); LogSupport.InfoHandler += UnhollowerLog.LogInfo; LogSupport.WarningHandler += UnhollowerLog.LogWarning; LogSupport.TraceHandler += UnhollowerLog.LogDebug; LogSupport.ErrorHandler += UnhollowerLog.LogError; if (ProxyAssemblyGenerator.CheckIfGenerationRequired()) { ProxyAssemblyGenerator.GenerateAssemblies(); } using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); assemblyPatcher.LoadAssemblyDirectories(IL2CPPUnhollowedPath); Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); } Logger.Listeners.Remove(PreloaderLog); Chainloader = new IL2CPPChainloader(); Chainloader.Initialize(); } catch (Exception ex) { Log.LogFatal(ex); throw; } }
private void ApplyInstructions(IEnumerable <PatchGroup> patchGroups, ProgressObject po) { //TODO: Use a different progress tracking system and make the entire patching operation more recoverable and fault-tolerant. //TODO: Refactor this method. patchGroups = patchGroups.ToList(); var appInfo = AppInfo; var logger = Logger; var fileProgress = new ProgressObject(); po.Child.Value = fileProgress; var patchProgress = new ProgressObject(); fileProgress.Child.Value = patchProgress; var myAttributesAssembly = typeof(AppInfo).Assembly; var attributesAssemblyName = Path.GetFileName(myAttributesAssembly.Location); var history = new List <XmlFileHistory>(); po.TaskTitle.Value = "Patching Game"; po.TaskText.Value = appInfo.AppName; po.Total.Value = patchGroups.Count(); foreach (var patchGroup in patchGroups) { var patchCount = patchGroup.Instructions.Count; po.TaskTitle.Value = $"Patching {appInfo.AppName}"; var targetFile = patchGroup.TargetPath; po.TaskText.Value = Path.GetFileName(targetFile); //Note that Path.Combine(FILENAME, "..", OTHER_FILENAME) doesn't work on Mono but does work on .NET. var dir = Path.GetDirectoryName(targetFile); var localAssemblyName = Path.Combine(dir, attributesAssemblyName); var copy = true; fileProgress.TaskTitle.Value = "Patching File"; fileProgress.Total.Value = 2 + patchCount; fileProgress.Current.Value++; var backupModified = PatchingHelper.GetBackupForModified(targetFile); var backupOrig = PatchingHelper.GetBackupForOriginal(targetFile); fileProgress.TaskText.Value = "Applying Patch"; if (!PatchingHelper.DoesFileMatchPatchList(backupModified, targetFile, patchGroup.Instructions) || Preferences.AlwaysPatch) { if (File.Exists(localAssemblyName)) { try { var localAssembly = AssemblyCache.Default.ReadAssembly(localAssemblyName); if (localAssembly.GetAssemblyMetadataString() == myAttributesAssembly.GetAssemblyMetadataString()) { copy = false; } } catch (Exception ex) { Logger.Warning(ex, $"Failed to read local attributes assembly so it will be overwritten."); //if reading the assembly failed for any reason, just ignore... } } if (copy) { File.Copy(myAttributesAssembly.Location, localAssemblyName, true); } var patcher = new AssemblyPatcher(targetFile, logger) { EmbedHistory = true }; foreach (var patch in patchGroup.Instructions) { try { patcher.PatchManifest(patch.Patch, patchProgress.ToMonitor()); } catch (PatchException ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = patch, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.ApplyingSpecificPatch }; } fileProgress.Current.Value++; } patchProgress.TaskText.Value = ""; patchProgress.TaskTitle.Value = ""; fileProgress.Current.Value++; fileProgress.TaskText.Value = "Writing Assembly"; if (Environment.OSVersion.Platform == PlatformID.Win32NT) { fileProgress.TaskText.Value = "Running PEVerify..."; var targetFolder = Path.GetDirectoryName(targetFile); try { var peOutput = patcher.RunPeVerify(new PEVerifyInput { AssemblyResolutionFolder = targetFolder, IgnoreErrors = AppInfo.IgnorePEVerifyErrors.ToList() }); logger.Information(peOutput.Output); } catch (Exception ex) { logger.Error(ex, "Failed to run PEVerify on the assembly."); } } try { patcher.WriteTo(backupModified); } catch (Exception ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = null, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.WritingToFile }; } } else { fileProgress.Current.Value += patchCount; } try { PatchingHelper.SwitchFilesSafely(backupModified, targetFile, backupOrig); } catch (Exception ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = null, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.PerformingSwitch }; } AssemblyCache.Default.ClearCache(); po.Current.Value++; } }
public static void Run() { try { AllocateConsole(); bool bridgeInitialized = Utility.TryDo(() => { if (ConfigShimHarmony.Value) { HarmonyDetourBridge.Init(); } }, out var harmonyBridgeException); Exception runtimePatchException = null; if (bridgeInitialized) { Utility.TryDo(() => { if (ConfigApplyRuntimePatches.Value) { UnityPatches.Apply(); } }, out runtimePatchException); } Logger.Sources.Add(TraceLogSource.CreateSource()); PreloaderLog = new PreloaderConsoleListener(ConfigPreloaderCOutLogging.Value); Logger.Listeners.Add(PreloaderLog); string consoleTile = $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Process.GetCurrentProcess().ProcessName}"; ConsoleWindow.Title = consoleTile; Logger.LogMessage(consoleTile); //See BuildInfoAttribute for more information about this section. object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false); if (attributes.Length > 0) { var attribute = (BuildInfoAttribute)attributes[0]; Logger.LogMessage(attribute.Info); } Logger.LogInfo($"Running under Unity v{FileVersionInfo.GetVersionInfo(Paths.ExecutablePath).FileVersion}"); Logger.LogInfo($"CLR runtime version: {Environment.Version}"); Logger.LogInfo($"Supports SRE: {Utility.CLRSupportsDynamicAssemblies}"); if (harmonyBridgeException != null) { Logger.LogWarning($"Failed to enable fix for Harmony for .NET Standard API. Error message: {harmonyBridgeException.Message}"); } if (runtimePatchException != null) { Logger.LogWarning($"Failed to apply runtime patches for Mono. See more info in the output log. Error message: {runtimePatchException.Message}"); } Logger.LogMessage("Preloader started"); AssemblyPatcher.AddPatcher(new PatcherPlugin { TargetDLLs = () => new[] { ConfigEntrypointAssembly.Value }, Patcher = PatchEntrypoint, TypeName = "BepInEx.Chainloader" }); AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Logger.LogInfo($"{AssemblyPatcher.PatcherPlugins.Count} patcher plugin(s) loaded"); AssemblyPatcher.PatchAndLoad(Paths.ManagedPath); AssemblyPatcher.DisposePatchers(); Logger.LogMessage("Preloader finished"); Logger.Listeners.Remove(PreloaderLog); Logger.Listeners.Add(new ConsoleLogListener()); PreloaderLog.Dispose(); } catch (Exception ex) { File.WriteAllText("err.log", ex.ToString()); try { Logger.LogFatal("Could not run preloader!"); Logger.LogFatal(ex); PreloaderLog?.Dispose(); if (!ConsoleWindow.IsAttached) { //if we've already attached the console, then the log will already be written to the console AllocateConsole(); Console.Write(PreloaderLog); } PreloaderLog = null; } finally { File.WriteAllText( Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"), PreloaderLog + "\r\n" + ex); PreloaderLog?.Dispose(); PreloaderLog = null; } } }
public static void Run() { try { InitializeHarmony(); ConsoleManager.Initialize(false); AllocateConsole(); Utility.TryDo(() => { if (ConfigApplyRuntimePatches.Value) { UnityPatches.Apply(); } }, out var runtimePatchException); Logger.InitializeInternalLoggers(); Logger.Sources.Add(TraceLogSource.CreateSource()); PreloaderLog = new PreloaderConsoleListener(); Logger.Listeners.Add(PreloaderLog); string consoleTile = $"BepInEx {typeof(Paths).Assembly.GetName().Version} - {Paths.ProcessName}"; if (ConsoleManager.ConsoleActive) { ConsoleManager.SetConsoleTitle(consoleTile); } Logger.LogMessage(consoleTile); //See BuildInfoAttribute for more information about this section. object[] attributes = typeof(BuildInfoAttribute).Assembly.GetCustomAttributes(typeof(BuildInfoAttribute), false); if (attributes.Length > 0) { var attribute = (BuildInfoAttribute)attributes[0]; Logger.LogMessage(attribute.Info); } Logger.LogInfo($"Running under Unity v{GetUnityVersion()}"); Logger.LogInfo($"CLR runtime version: {Environment.Version}"); Logger.LogInfo($"Supports SRE: {Utility.CLRSupportsDynamicAssemblies}"); if (runtimePatchException != null) { Logger.LogWarning($"Failed to apply runtime patches for Mono. See more info in the output log. Error message: {runtimePatchException.Message}"); } Logger.LogMessage("Preloader started"); AssemblyPatcher.AddPatcher(new PatcherPlugin { TargetDLLs = () => new[] { ConfigEntrypointAssembly.Value }, Patcher = PatchEntrypoint, TypeName = "BepInEx.Chainloader" }); AssemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Logger.LogInfo($"{AssemblyPatcher.PatcherPlugins.Count} patcher plugin{(AssemblyPatcher.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); AssemblyPatcher.PatchAndLoad(Paths.ManagedPath); AssemblyPatcher.DisposePatchers(); Logger.LogMessage("Preloader finished"); Logger.Listeners.Remove(PreloaderLog); PreloaderLog.Dispose(); } catch (Exception ex) { try { Logger.LogFatal("Could not run preloader!"); Logger.LogFatal(ex); if (!ConsoleManager.ConsoleActive) { //if we've already attached the console, then the log will already be written to the console AllocateConsole(); Console.Write(PreloaderLog); } } catch { } string log = string.Empty; try { // We could use platform-dependent newlines, however the developers use Windows so this will be easier to read :) log = string.Join("\r\n", PreloaderConsoleListener.LogEvents.Select(x => x.ToString()).ToArray()); log += "\r\n"; PreloaderLog?.Dispose(); PreloaderLog = null; } catch { } File.WriteAllText( Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"), log + ex); } }
public static void Run() { try { InitializeHarmony(); ConsoleManager.Initialize(false); AllocateConsole(); Utility.TryDo(() => { if (ConfigApplyRuntimePatches.Value) { UnityPatches.Apply(); } }, out var runtimePatchException); Logger.Sources.Add(new HarmonyLogSource()); Logger.Sources.Add(TraceLogSource.CreateSource()); Logger.Listeners.Add(new ConsoleLogListener()); PreloaderLog = new PreloaderConsoleListener(); Logger.Listeners.Add(PreloaderLog); ChainloaderLogHelper.PrintLogInfo(Log); Log.LogInfo($"Running under Unity v{GetUnityVersion()}"); Log.LogInfo($"CLR runtime version: {Environment.Version}"); Log.LogInfo($"Supports SRE: {Utility.CLRSupportsDynamicAssemblies}"); Log.LogDebug($"Game executable path: {Paths.ExecutablePath}"); Log.LogDebug($"Unity Managed directory: {Paths.ManagedPath}"); Log.LogDebug($"BepInEx root path: {Paths.BepInExRootPath}"); if (runtimePatchException != null) { Log.LogWarning($"Failed to apply runtime patches for Mono. See more info in the output log. Error message: {runtimePatchException.Message}"); } Log.LogMessage("Preloader started"); TypeLoader.SearchDirectories.UnionWith(Paths.DllSearchPaths); using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.PatcherPlugins.Add(new PatcherPlugin { TargetDLLs = () => new[] { ConfigEntrypointAssembly.Value }, Patcher = PatchEntrypoint, TypeName = "BepInEx.Chainloader" }); assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); assemblyPatcher.LoadAssemblyDirectories(Paths.DllSearchPaths); Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); } Log.LogMessage("Preloader finished"); Logger.Listeners.Remove(PreloaderLog); PreloaderLog.Dispose(); } catch (Exception ex) { try { Log.LogFatal("Could not run preloader!"); Log.LogFatal(ex); if (!ConsoleManager.ConsoleActive) { //if we've already attached the console, then the log will already be written to the console AllocateConsole(); Console.Write(PreloaderLog); } } catch { } var log = string.Empty; try { // We could use platform-dependent newlines, however the developers use Windows so this will be easier to read :) log = string.Join("\r\n", PreloaderConsoleListener.LogEvents.Select(x => x.ToString()).ToArray()); log += "\r\n"; PreloaderLog?.Dispose(); PreloaderLog = null; } catch { } File.WriteAllText( Path.Combine(Paths.GameRootPath, $"preloader_{DateTime.Now:yyyyMMdd_HHmmss_fff}.log"), log + ex); } }
public static void ApplyInstructions(LaunchType launchType, List <PatchGroup> patchGroups, ProgressObject totalProgress) { //TODO: Use a different progress tracking system and make the entire patching operation more recoverable and fault-tolerant. //TODO: Refactor this method. AppInfo appInfo = AppContextManager.Context.Value; Assembly myAttributesAssembly = typeof(AppInfo).Assembly; string myAttributesAssemblyName = Path.GetFileName(myAttributesAssembly.Location); var fileProgress = new ProgressObject(); var patchProgress = new ProgressObject(); totalProgress.Child.Value = fileProgress; fileProgress.Child.Value = patchProgress; totalProgress.SetTaskData("Patching Game", appInfo.AppName, patchGroups.Count); foreach (PatchGroup patchGroup in patchGroups) { int patchCount = patchGroup.Instructions.Count; string destinationPath = patchGroup.TargetPath; var patcher = new AssemblyPatcher(destinationPath, Logger); patcher.EmbedHistory = true; string sourcePath = PatchingHelper.GetBackupForModified(destinationPath); string backupPath = PatchingHelper.GetBackupForOriginal(destinationPath); // note that Path.Combine(FILENAME, "..", OTHER_FILENAME) doesn't work on Mono but does work on .NET. string targetDirectory = Path.GetDirectoryName(destinationPath); string localAssemblyName = Path.Combine(targetDirectory, myAttributesAssemblyName); totalProgress.SetTaskData(string.Format("Patching {0}", appInfo.AppName), Path.GetFileName(destinationPath)); fileProgress.SetTaskData("Patching File", total: 2 + patchCount, increment: true); fileProgress.SetTaskData(taskText: "Applying Patch"); if (!PatchingHelper.DoesFileMatchPatchList(sourcePath, destinationPath, patchGroup.Instructions) || PreferencesManager.Preferences.AlwaysPatch) { try { myAttributesAssembly.TryCopyAttributesAssembly(localAssemblyName); } catch (Exception exception) { Logger.Warning(exception, "Failed to read local attributes assembly so it will be overwritten."); } foreach (PatchInstruction patch in patchGroup.Instructions) { try { patcher.TryPatchManifest(patch, patchGroup, patchProgress); } catch (PatchingProcessException exception) { Logger.Show(exception); } fileProgress.SetTaskData(increment: true); } patchProgress.SetTaskData(string.Empty, string.Empty); fileProgress.SetTaskData(taskText: "Writing Assembly", increment: true); if (launchType == LaunchType.Test && Environment.OSVersion.Platform == PlatformID.Win32NT) { fileProgress.SetTaskData(taskText: "Running PEVerify"); string peOutput = patcher.TryRunPeVerify(appInfo, destinationPath); Logger.Information(peOutput); } try { patcher.TryBackup(sourcePath, patchGroup); } catch (PatchingProcessException exception) { Logger.Show(exception); } } else { fileProgress.Current.Value += patchCount; } try { LaunchManager.TrySwitchFilesSafely(sourcePath, destinationPath, backupPath, patchGroup); } catch (PatchingProcessException exception) { Logger.Show(exception); } AssemblyCache.Default.ClearCache(); totalProgress.SetTaskData(increment: true); } }
public static void Start(string[] args) { var preloaderListener = new PreloaderConsoleListener(); Logger.Listeners.Add(preloaderListener); if (string.IsNullOrEmpty(ConfigEntrypointExecutable.Value)) { Log.Log(LogLevel.Fatal, $"Entry executable was not set. Please set this in your config before launching the application"); Program.ReadExit(); return; } var executablePath = Path.GetFullPath(ConfigEntrypointExecutable.Value); if (!File.Exists(executablePath)) { Log.Log(LogLevel.Fatal, $"Unable to locate executable: {ConfigEntrypointExecutable.Value}"); Program.ReadExit(); return; } Paths.SetExecutablePath(executablePath); Program.ResolveDirectories.Add(Paths.GameRootPath); foreach (var searchDir in Program.ResolveDirectories) { TypeLoader.SearchDirectories.Add(searchDir); } if (PlatformHelper.Is(Platform.Windows)) { AddDllDirectory(Paths.GameRootPath); SetDllDirectory(Paths.GameRootPath); } Logger.Sources.Add(TraceLogSource.CreateSource()); ChainloaderLogHelper.PrintLogInfo(Log); Log.Log(LogLevel.Info, $"CLR runtime version: {Environment.Version}"); AssemblyBuildInfo executableInfo, launcherInfo; using (var executableAssembly = AssemblyDefinition.ReadAssembly(executablePath)) executableInfo = AssemblyBuildInfo.DetermineInfo(executableAssembly); using (var launcherAssembly = AssemblyDefinition.ReadAssembly(typeof(NetPreloader).Assembly.Location)) launcherInfo = AssemblyBuildInfo.DetermineInfo(launcherAssembly); // we don't particularly care about AnyCPU here since the fallback bitness is almost never the case if (executableInfo.Is64Bit != launcherInfo.Is64Bit) { Log.LogError($"Game executable is {(executableInfo.Is64Bit ? "64" : "32")}-bit while BepInEx has been compiled as {(launcherInfo.Is64Bit ? "64" : "32")}-bit. Expect crashes"); } if (executableInfo.NetFrameworkVersion != launcherInfo.NetFrameworkVersion || executableInfo.AssemblyFrameworkType != launcherInfo.AssemblyFrameworkType) { Log.LogWarning($"Game executable has been compiled as {executableInfo}, while BepInEx has been compiled as {launcherInfo}. There may be issues within the game caused by this"); } Log.LogInfo($"Game executable build architecture: {executableInfo}"); Log.LogInfo($"BepInEx launcher build architecture: {launcherInfo}"); Log.Log(LogLevel.Message, "Preloader started"); Assembly entrypointAssembly; using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.PatchDefinitions.Count} patcher definition(s) loaded"); assemblyPatcher.LoadAssemblyDirectories(new[] { Paths.GameRootPath }, new[] { "dll", "exe" }); Log.Log(LogLevel.Info, $"{assemblyPatcher.PatcherContext.AvailableAssemblies.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); var assemblyName = AssemblyName.GetAssemblyName(executablePath); entrypointAssembly = assemblyPatcher.PatcherContext.LoadedAssemblies.Values.FirstOrDefault(x => x.FullName == assemblyName.FullName); foreach (var loadedAssembly in assemblyPatcher.PatcherContext.LoadedAssemblies) { // TODO: Need full paths for loaded assemblies var assemblyPath = Path.Combine(Paths.GameRootPath, loadedAssembly.Key); Log.LogDebug($"Registering '{assemblyPath}' as a loaded assembly"); AssemblyFixes.AssemblyLocations[loadedAssembly.Value.FullName] = assemblyPath; } if (entrypointAssembly != null) { Log.LogDebug("Found patched entrypoint assembly! Using it"); } else { Log.LogDebug("Using entrypoint assembly from disk"); entrypointAssembly = Assembly.LoadFrom(executablePath); } } Log.LogMessage("Preloader finished"); Logger.Listeners.Remove(preloaderListener); var chainloader = new NetChainloader(); chainloader.Initialize(); chainloader.Execute(); AssemblyFixes.Execute(entrypointAssembly); try { var argList = new List <object>(); var paramTypes = entrypointAssembly.EntryPoint.GetParameters(); if (paramTypes.Length == 1 && paramTypes[0].ParameterType == typeof(string[])) { argList.Add(args); } else if (paramTypes.Length == 1 && paramTypes[0].ParameterType == typeof(string)) { argList.Add(string.Join(" ", args)); } else if (paramTypes.Length != 0) { // Only other entrypoint signatures I can think of that .NET supports is Task / Task<int> // async entrypoints. That's a can of worms for another time though Log.LogFatal($"Could not figure out how to handle entrypoint method with this signature: {entrypointAssembly.EntryPoint.FullDescription()}"); return; } entrypointAssembly.EntryPoint.Invoke(null, argList.ToArray()); } catch (Exception ex) { Log.LogFatal($"Unhandled exception: {ex}"); } }
public static int Main(string[] args) { bool interactive = true; if (args.Length > 0) { interactive = false; } foreach (string arg in args) { if (arg.Contains("--generatediffs")) { gendiffs = true; } } newAssCS = true; Console.WriteLine(string.Format("[( Pluton Patcher v{0} )]", Version)); try { plutonAssembly = AssemblyPatcher.FromFile("Pluton.dll"); rustAssembly = AssemblyPatcher.FromFile("Assembly-CSharp.dll"); } catch (FileNotFoundException ex) { Console.WriteLine("You are missing " + ex.FileName + " did you move the patcher to the managed folder ?"); if (interactive) { Console.WriteLine("Press any key to continue..."); } return((int)ExitCode.DLL_MISSING); } catch (Exception ex) { Console.WriteLine("An error occured while reading the assemblies :"); Console.WriteLine(ex.ToString()); if (interactive) { Console.WriteLine("Press any key to continue..."); } return((int)ExitCode.DLL_READ_ERROR); } bNPC = rustAssembly.GetType("BaseNPC"); bPlayer = rustAssembly.GetType("BasePlayer"); codeLock = rustAssembly.GetType("CodeLock"); hooksClass = plutonAssembly.GetType("Pluton.Hooks"); itemCrafter = rustAssembly.GetType("ItemCrafter"); pLoot = rustAssembly.GetType("PlayerLoot"); //Check if patching is required TypePatcher plutonClass = rustAssembly.GetType("PlutonPatched"); if (plutonClass == null) { try { if (gendiffs) { string hash = string.Empty; using (var sha512 = new System.Security.Cryptography.SHA512Managed()) hash = BitConverter.ToString(sha512.ComputeHash(File.ReadAllBytes("Assembly-CSharp.dll"))).Replace("-", "").ToLower(); Directory.CreateDirectory("diffs"); string hashpath = "diffs" + Path.DirectorySeparatorChar + "lastHash"; if (File.Exists(hashpath)) { newAssCS = hash != File.ReadAllText(hashpath); } if (newAssCS) { foreach (var difffile in Directory.GetFiles("diffs")) { if (difffile.Contains(".htm")) { string filename = Path.GetFileName(difffile); string dirname = Path.GetDirectoryName(difffile); Directory.CreateDirectory(Path.Combine(dirname, "old")); File.Move(difffile, difffile.Replace(Path.Combine(dirname, filename), Path.Combine(dirname, "old", filename))); } } } if (gendiffs && newAssCS) { File.WriteAllText(hashpath, hash); } } PatchASMCSharp(); Console.WriteLine("Patched Assembly-CSharp !"); } catch (Exception ex) { interactive = true; Console.WriteLine("An error occured while patching Assembly-CSharp :"); Console.WriteLine(); Console.WriteLine(ex.Message.ToString()); //Normal handle for the others Console.WriteLine(); Console.WriteLine(ex.StackTrace.ToString()); Console.WriteLine(); if (interactive) { Console.WriteLine("Press any key to continue..."); } return((int)ExitCode.ACDLL_GENERIC_PATCH_ERR); } } else { Console.WriteLine("Assembly-CSharp.dll is already patched!"); return((int)ExitCode.ACDLL_ALREADY_PATCHED); } try { rustAssembly.Write("Assembly-CSharp.dll"); } catch (Exception ex) { Console.WriteLine("An error occured while writing the assembly :"); Console.WriteLine("Error at: " + ex.TargetSite.Name); Console.WriteLine("Error msg: " + ex.Message); if (interactive) { Console.WriteLine("Press any key to continue..."); } return((int)ExitCode.DLL_WRITE_ERROR); } //Successfully patched the server Console.WriteLine("Completed !"); if (interactive) { System.Threading.Thread.Sleep(250); } return((int)ExitCode.SUCCESS); }
private void ApplyInstructions(IEnumerable<PatchGroup> patchGroups, ProgressObject po) { //TODO: Use a different progress tracking system and make the entire patching operation more recoverable and fault-tolerant. //TODO: Refactor this method. patchGroups = patchGroups.ToList(); var appInfo = AppInfo; var logger = Logger; var fileProgress = new ProgressObject(); po.Child.Value = fileProgress; var patchProgress = new ProgressObject(); fileProgress.Child.Value = patchProgress; var myAttributesAssembly = typeof (AppInfo).Assembly; var attributesAssemblyName = Path.GetFileName(myAttributesAssembly.Location); var history = new List<XmlFileHistory>(); po.TaskTitle.Value = "Patching Game"; po.TaskText.Value = appInfo.AppName; po.Total.Value = patchGroups.Count(); foreach (var patchGroup in patchGroups) { var patchCount = patchGroup.Instructions.Count; po.TaskTitle.Value = $"Patching {appInfo.AppName}"; var targetFile = patchGroup.TargetPath; po.TaskText.Value = Path.GetFileName(targetFile); //Note that Path.Combine(FILENAME, "..", OTHER_FILENAME) doesn't work on Mono but does work on .NET. var dir = Path.GetDirectoryName(targetFile); var localAssemblyName = Path.Combine(dir, attributesAssemblyName); var copy = true; fileProgress.TaskTitle.Value = "Patching File"; fileProgress.Total.Value = 2 + patchCount; fileProgress.Current.Value++; var backupModified = PatchingHelper.GetBackupForModified(targetFile); var backupOrig = PatchingHelper.GetBackupForOriginal(targetFile); fileProgress.TaskText.Value = "Applying Patch"; if (!PatchingHelper.DoesFileMatchPatchList(backupModified, targetFile, patchGroup.Instructions) || Preferences.AlwaysPatch) { if (File.Exists(localAssemblyName)) { try { var localAssembly = AssemblyCache.Default.ReadAssembly(localAssemblyName); if (localAssembly.GetAssemblyMetadataString() == myAttributesAssembly.GetAssemblyMetadataString()) { copy = false; } } catch (Exception ex) { Logger.Warning(ex, $"Failed to read local attributes assembly so it will be overwritten."); //if reading the assembly failed for any reason, just ignore... } } if (copy) { File.Copy(myAttributesAssembly.Location, localAssemblyName, true); } var patcher = new AssemblyPatcher(targetFile, logger) { EmbedHistory = true }; foreach (var patch in patchGroup.Instructions) { try { patcher.PatchManifest(patch.Patch, patchProgress.ToMonitor()); } catch (PatchException ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = patch, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.ApplyingSpecificPatch }; } fileProgress.Current.Value++; } patchProgress.TaskText.Value = ""; patchProgress.TaskTitle.Value = ""; fileProgress.Current.Value++; fileProgress.TaskText.Value = "Writing Assembly"; if (Environment.OSVersion.Platform == PlatformID.Win32NT) { fileProgress.TaskText.Value = "Running PEVerify..."; var targetFolder = Path.GetDirectoryName(targetFile); try { var peOutput = patcher.RunPeVerify(new PEVerifyInput { AssemblyResolutionFolder = targetFolder, IgnoreErrors = AppInfo.IgnorePEVerifyErrors.ToList() }); logger.Information(peOutput.Output); } catch (Exception ex) { logger.Error(ex, "Failed to run PEVerify on the assembly."); } } try { patcher.WriteTo(backupModified); } catch (Exception ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = null, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.WritingToFile }; } } else { fileProgress.Current.Value += patchCount; } try { PatchingHelper.SwitchFilesSafely(backupModified, targetFile, backupOrig); } catch (Exception ex) { throw new PatchingProcessException(ex) { AssociatedInstruction = null, AssociatedPatchGroup = patchGroup, Step = PatchProcessingStep.PerformingSwitch }; } AssemblyCache.Default.ClearCache(); po.Current.Value++; } }
public static void Start(string[] args) { if (string.IsNullOrEmpty(ConfigEntrypointExecutable.Value)) { Log.LogFatal($"Entry executable was not set. Please set this in your config before launching the application"); Program.ReadExit(); return; } string executablePath = Path.GetFullPath(ConfigEntrypointExecutable.Value); if (!File.Exists(executablePath)) { Log.LogFatal($"Unable to locate executable: {ConfigEntrypointExecutable.Value}"); Program.ReadExit(); return; } Paths.SetExecutablePath(executablePath); Program.ResolveDirectories.Add(Paths.GameRootPath); TypeLoader.SearchDirectories.Add(Paths.GameRootPath); Logger.Sources.Add(TraceLogSource.CreateSource()); ChainloaderLogHelper.PrintLogInfo(Log); Log.LogInfo($"CLR runtime version: {Environment.Version}"); Log.LogMessage("Preloader started"); Assembly entrypointAssembly; using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherPlugins.Count} patcher plugin(s) loaded"); assemblyPatcher.LoadAssemblyDirectory(Paths.GameRootPath, "dll", "exe"); Log.LogInfo($"{assemblyPatcher.AssembliesToPatch.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); var assemblyName = AssemblyName.GetAssemblyName(executablePath); entrypointAssembly = assemblyPatcher.LoadedAssemblies.Values.FirstOrDefault(x => x.FullName == assemblyName.FullName); if (entrypointAssembly != null) { Log.LogDebug("Found patched entrypoint assembly! Using it"); } else { Log.LogDebug("Using entrypoint assembly from disk"); entrypointAssembly = Assembly.LoadFrom(executablePath); } } Log.LogMessage("Preloader finished"); var chainloader = new NetChainloader(); chainloader.Initialize(); chainloader.Execute(); AssemblyFix.Execute(entrypointAssembly); entrypointAssembly.EntryPoint.Invoke(null, new [] { args }); }
public static void Run() { try { HarmonyBackendFix.Initialize(); ConsoleManager.Initialize(false, false); PreloaderLog = new PreloaderConsoleListener(); Logger.Listeners.Add(PreloaderLog); if (ConsoleManager.ConsoleEnabled) { ConsoleManager.CreateConsole(); Logger.Listeners.Add(new ConsoleLogListener()); } ChainloaderLogHelper.PrintLogInfo(Log); Log.Log(LogLevel.Debug, $"Game executable path: {Paths.ExecutablePath}"); Log.Log(LogLevel.Debug, $"Unhollowed assembly directory: {IL2CPPUnhollowedPath}"); Log.Log(LogLevel.Debug, $"BepInEx root path: {Paths.BepInExRootPath}"); UnhollowerLog = Logger.CreateLogSource("Unhollower"); LogSupport.InfoHandler += UnhollowerLog.LogInfo; LogSupport.WarningHandler += UnhollowerLog.LogWarning; LogSupport.TraceHandler += UnhollowerLog.LogDebug; LogSupport.ErrorHandler += UnhollowerLog.LogError; InitializeUnityVersion(); if (ProxyAssemblyGenerator.CheckIfGenerationRequired()) { ProxyAssemblyGenerator.GenerateAssemblies(); } UnityVersionHandler.Initialize(UnityVersion.Major, UnityVersion.Minor, UnityVersion.Build); using (var assemblyPatcher = new AssemblyPatcher()) { assemblyPatcher.AddPatchersFromDirectory(Paths.PatcherPluginPath); Log.LogInfo($"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} patcher plugin{(assemblyPatcher.PatcherContext.PatcherPlugins.Count == 1 ? "" : "s")} loaded"); assemblyPatcher.LoadAssemblyDirectories(IL2CPPUnhollowedPath); Log.LogInfo($"{assemblyPatcher.PatcherContext.PatcherPlugins.Count} assemblies discovered"); assemblyPatcher.PatchAndLoad(); } Logger.Listeners.Remove(PreloaderLog); Chainloader = new IL2CPPChainloader(); Chainloader.Initialize(); } catch (Exception ex) { Log.Log(LogLevel.Fatal, ex); throw; } }