public static int Main(string[] args) { if (args.Length < 1) { return(XConsole.WriteLine("StringFixer.CLI\n" + "by exys, 2019\n" + "\n" + "Usage:\n" + "StringFixer.CLI [module]")); } if (!File.Exists(_modulePath = Path.GetFullPath(args[0]))) { return(XConsole.PrintError("Specified module path does not exist")); } try { _module = ModuleDefMD.Load(_modulePath); _assembly = Assembly.LoadFile(_modulePath); } catch (Exception e) { return(XConsole.PrintError("An error occurred while trying to load and process modules! Details:\n" + e)); } XConsole.PrintInfo($"Loaded module: {_module.Assembly.FullName}."); StringFixer.Fix(_module, _assembly); string filename = Path.GetFileNameWithoutExtension(_modulePath) + "-string" + Path.GetExtension(_modulePath); XConsole.PrintInfo("Finally writing module back (with \"-string\" tag)!"); _module.Write( Path.Combine( Path.GetDirectoryName(_modulePath) ?? throw new StringFixerCliException("Path to write module to is null unexpectedly"), filename), new ModuleWriterOptions(_module) { MetadataOptions = { Flags = DEFAULT_METADATA_FLAGS } }); Console.ReadKey(true); return(0); }
public static int Main(string[] args) { // Console.ReadKey(true); if (args.Length < 2) { return(Exit("osu!patch - osu! assembly patcher based on NameMapper\n" + "by exys, 2019 - 2020\n" + "modded by Aoba Suzukaze, 2020\n" + "\n" + "Usage:\n" + "osu!patch [clean module] [obfuscated module]")); } if (!File.Exists(_cleanOsuPath = Path.GetFullPath(args[0]))) { return(XConsole.PrintFatal("Specified clean module path does not exist!\n")); } if (!File.Exists(_obfOsuPath = Path.GetFullPath(args[1]))) { return(XConsole.PrintFatal("Specified obfuscated module path does not exist!\n")); } try { _obfOsuModule = ModuleDefMD.Load(_obfOsuPath); _cleanOsuModule = ModuleDefMD.Load(_cleanOsuPath); _obfOsuAssembly = Assembly.LoadFile(_obfOsuPath); } catch (Exception ex) { return(XConsole.PrintFatal("Unable to load one of the modules! Details:\n" + ex)); } ObfOsuHash = MD5Helper.Compute(_obfOsuPath); // ORIGINAL!!!!!!! hash, PLEASE PASS UNMODIFIED PEPPY-SIGNED ASSEMBLY AS _obfOsuModule!@!!32R1234 (refer to "Patch on update" patch) XConsole.PrintInfo($"Loaded assemblies: {_cleanOsuModule.Assembly.FullName} (clean); {_obfOsuModule.Assembly.FullName} (obfuscated)."); XConsole.PrintInfo("MD5 hash of obfuscated assembly: " + ObfOsuHash); try { LoadPlugins(); // Loading plugins } catch (Exception ex) { return(XConsole.PrintFatal("Something really bad happened while trying to process plugins! Details:\n" + ex)); } try { XConsole.PrintInfo("Cleaning control flow of obfuscated assembly"); CleanControlFlow(); } catch (Exception ex) { return(XConsole.PrintFatal("Unable to deobfuscate control flow of obfuscated assembly! Details:\n" + ex)); } try { XConsole.PrintInfo("Fixing strings in obfuscated assembly."); StringFixer.Fix(_obfOsuModule, _obfOsuAssembly); // Fixing strings } catch (Exception ex) { return(XConsole.PrintFatal("Unable to fix strings of obfuscated assembly! Details:\n" + ex)); } if (!Directory.Exists(CacheFolderLocation)) { XConsole.PrintInfo("Creating cache folder..."); Directory.CreateDirectory(CacheFolderLocation); } try { var nameProvider = InitializeNameProvider(); // Fixing names (SimpleNameProvider/MapperNameProvider) _obfOsuExplorer = new ModuleExplorer(_obfOsuModule, nameProvider); } catch (Exception ex) { return(XConsole.PrintFatal("Unable to get clean names for obfuscated assembly! Details:\n" + ex)); } #if DEBUG _obfOsuModule.Write(Path.Combine(Path.GetDirectoryName(_obfOsuPath), "OsuObfModule-cflow-string-nmapped.exe"), new ModuleWriterOptions(_obfOsuModule) { MetadataOptions = { Flags = DEFAULT_METADATA_FLAGS } }); #endif XConsole.PrintInfo("Done! Now patching."); bool overallSuccess = true; var failedDetails = new List <PatchResult>(); void ExecutePatchCli(Patch patch) { XConsole.PrintInfo($"{patch.Name}: ", false); if (File.Exists(ConfigFileLocation)) // incase if config file not initialized { patch.Enabled = GetConfigEnabled(patch.Name); } PatchResult res = patch.Execute(_obfOsuExplorer); switch (res.Result) { case PatchStatus.Disabled: Console.ForegroundColor = ConsoleColor.Gray; XConsole.WriteLine("DISABLED"); break; case PatchStatus.Exception: case PatchStatus.Failure: Console.ForegroundColor = ConsoleColor.Red; XConsole.WriteLine("FAIL"); failedDetails.Add(res); overallSuccess = false; break; case PatchStatus.Success: Console.ForegroundColor = ConsoleColor.Green; XConsole.WriteLine("DONE"); break; default: Console.ForegroundColor = ConsoleColor.DarkGray; XConsole.WriteLine("[???]"); break; } Console.ResetColor(); } // Executing local patches. foreach (var patch in LocalPatches.PatchList) { ExecutePatchCli(patch); } XConsole.PrintInfo("Done processing all local patches! Now processing patches from loaded add-ons..."); #if !CORE_PATCHES_ONLY // Executing all patches from all loaded plugins from all loaded assemblies (what a hierarchy) foreach (var plugin in _loadedPlugins) { XConsole.PrintInfo($"{plugin.AssemblyName}: Processing plugin: {plugin.TypeName}."); foreach (var patch in plugin.Type.GetPatches()) { ExecutePatchCli(patch); } XConsole.PrintInfo($"{plugin.AssemblyName}: Done processing: {plugin.TypeName}."); } #endif if (!File.Exists(ConfigFileLocation)) { XConsole.PrintInfo("Creating config file..."); var configLines = string.Empty; foreach (var patch in Patches) { configLines += patch.Name + " = " + "Enabled\n"; // probably bad implementation of config but simplest i can imagine } File.WriteAllText(ConfigFileLocation, configLines); XConsole.PrintInfo("Config file created!"); } XConsole.PrintInfo("Done processing all plugins."); if (failedDetails.Any()) { XConsole.PrintInfo("There's some details about failed patches."); foreach (var details in failedDetails) { details.PrintDetails(Console.Out); XConsole.WriteLine(); } } if (!overallSuccess) { XConsole.PrintInfo("There are some failed patches. Do you want to continue?"); XConsole.PrintWarn("In case of self-update pressing 'N' will leave stock version of osu! without patching it!"); XConsole.Write(XConsole.PAD + "Continue? (y/n) "); var exit = false; while (true) { var key = Console.ReadKey(true).Key; if (key == ConsoleKey.Y) { break; } if (key == ConsoleKey.N) { exit = true; break; } } XConsole.WriteLine(); if (exit) { return(Exit(XConsole.Info("Aborted by user."))); } } //string filename = Path.GetFileNameWithoutExtension(_obfOsuPath) + "-osupatch" + Path.GetExtension(_obfOsuPath); string filename = "ainu.exe"; XConsole.PrintInfo($"Saving assembly as {filename}"); try { _obfOsuModule.Write(Path.Combine(Path.GetDirectoryName(_obfOsuPath), filename), new ModuleWriterOptions(_obfOsuModule) { MetadataOptions = { Flags = DEFAULT_METADATA_FLAGS } }); } catch (Exception ex) { return(Exit(XConsole.Fatal("Unable to save patched assembly! Details:\n" + ex))); } _cleanOsuModule.Dispose(); _obfOsuModule.Dispose(); #if DEBUG Console.ReadKey(true); #endif #if LIVE_DEBUG Process.Start(new ProcessStartInfo { FileName = "cmd", Arguments = "/c timeout /T 1 /NOBREAK & move /Y \"osu!-osupatch.exe\" \"osu!.exe\"", WorkingDirectory = Environment.CurrentDirectory, WindowStyle = ProcessWindowStyle.Hidden, CreateNoWindow = true, UseShellExecute = false }); #endif return(overallSuccess ? 0 : 1); }
private static void Initialize() { var lines = File.ReadAllLines(_osuPath); OsuPath = lines[0]; _osuHash = MD5Helper.ComputeFile(OsuPath); OsuAssembly = Assembly.LoadFrom(OsuPath); _obfOsuModule = ModuleDefMD.Load(OsuPath); _cleanOsuModule = ModuleDefMD.Load(Resource.clean); StringFixer.Fix(_obfOsuModule, OsuAssembly); // Fix troubles when NameMapper can't find name due to unsimilar opcodes. _nameStorage = InitializeNameMapper(); _config = new RConfig(); exp = new ModuleExplorer(_obfOsuModule, _nameStorage); if (_config.Profiles == string.Empty) { // hacky thing because cant read json array when we have only one object so we add two objects, maybe there's a way to do it in other way? var profiles = new List <Profile>(); profiles.Add(new Profile() { Name = "Default", UniqueId = GetRandomUniqueString(), UninstallID = Guid.NewGuid().ToString(), Adapters = GetRandomMacAddress() }); profiles.Add(new Profile() { Name = "Default2", UniqueId = GetRandomUniqueString(), UninstallID = Guid.NewGuid().ToString(), Adapters = GetRandomMacAddress() }); _config.Profiles = JsonConvert.SerializeObject(profiles); _config.SelectedProfile = "Default"; } Console.WriteLine(" --- Select Profile or use current one --- \n"); Console.WriteLine("--- 1) Select"); Console.WriteLine("--- 2) Use current one"); var key = Console.ReadKey(false).Key; Console.Clear(); switch (key) { case ConsoleKey.D1: var profiles = JsonConvert.DeserializeObject <List <Profile> >(_config.Profiles); for (var i = 0; i < profiles.Count; i++) { Console.WriteLine($"{i + 1}) {profiles[i].Name}"); } Console.WriteLine("Select one of them or press SPACE to generate new one."); var _key = Console.ReadKey(false).Key; // TODO: Add support for selecting profiles count more than 9 if (!(_key == ConsoleKey.Spacebar) && _key >= ConsoleKey.D1 && _key <= ConsoleKey.D9) { var number = Convert.ToInt32(_key.ToString().Replace("D", "")); Console.Clear(); _config.SelectedProfile = profiles[number - 1].Name; Console.WriteLine("Selected Profile: " + _config.SelectedProfile); Thread.Sleep(2000); Console.Clear(); } else if (_key == ConsoleKey.Spacebar) { Console.Clear(); Console.WriteLine(" --- Generation Process --- "); Console.Write("Please type name of profile: "); var profileName = Console.ReadLine(); Console.WriteLine("Started generating profile."); profiles.Add(new Profile() { Name = profileName, UniqueId = GetRandomUniqueString(), UninstallID = Guid.NewGuid().ToString(), Adapters = GetRandomMacAddress() }); _config.Profiles = JsonConvert.SerializeObject(profiles); _config.SelectedProfile = profileName; Console.Clear(); Console.WriteLine("Done!\n"); Console.WriteLine($"UniqueId: {_config.CurrentProfile.UniqueId}\nUninstallID: {_config.CurrentProfile.UninstallID}\nMAC Address: {_config.CurrentProfile.Adapters}\n"); Thread.Sleep(2000); Console.Clear(); } break; case ConsoleKey.D2: break; } Patch(); }