From 3d15ab56f221e7f005b3efe7adee5ea04f2a2db9 Mon Sep 17 00:00:00 2001 From: HoLLy Date: Sun, 29 Oct 2017 12:09:03 +0100 Subject: [PATCH] Add source after decompiling and fixing --- osu!decoder.sln | 22 +++ osu!decoder/BinaryPatch.cs | 72 +++++++++ osu!decoder/CliOptions.cs | 98 +++++++++++++ osu!decoder/CryptoHelper.cs | 137 +++++++++++++++++ osu!decoder/Processors/AssemblyChecker.cs | 75 ++++++++++ osu!decoder/Processors/AssemblyDecoder.cs | 171 ++++++++++++++++++++++ osu!decoder/Program.cs | 95 ++++++++++++ osu!decoder/Properties/AssemblyInfo.cs | 21 +++ osu!decoder/SourceMap.cs | 64 ++++++++ osu!decoder/osu!decoder.csproj | 61 ++++++++ osu!decoder/packages.config | 5 + 11 files changed, 821 insertions(+) create mode 100644 osu!decoder.sln create mode 100644 osu!decoder/BinaryPatch.cs create mode 100644 osu!decoder/CliOptions.cs create mode 100644 osu!decoder/CryptoHelper.cs create mode 100644 osu!decoder/Processors/AssemblyChecker.cs create mode 100644 osu!decoder/Processors/AssemblyDecoder.cs create mode 100644 osu!decoder/Program.cs create mode 100644 osu!decoder/Properties/AssemblyInfo.cs create mode 100644 osu!decoder/SourceMap.cs create mode 100644 osu!decoder/osu!decoder.csproj create mode 100644 osu!decoder/packages.config diff --git a/osu!decoder.sln b/osu!decoder.sln new file mode 100644 index 0000000..d560a6b --- /dev/null +++ b/osu!decoder.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26228.4 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu!decoder", "osu!decoder\osu!decoder.csproj", "{7CDE3892-D6ED-4784-AC5C-560649D75F9A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7CDE3892-D6ED-4784-AC5C-560649D75F9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CDE3892-D6ED-4784-AC5C-560649D75F9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CDE3892-D6ED-4784-AC5C-560649D75F9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CDE3892-D6ED-4784-AC5C-560649D75F9A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/osu!decoder/BinaryPatch.cs b/osu!decoder/BinaryPatch.cs new file mode 100644 index 0000000..731ce92 --- /dev/null +++ b/osu!decoder/BinaryPatch.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace osu_decoder_dnlib +{ + // Token: 0x02000002 RID: 2 + internal class BinaryPatch + { + // Token: 0x06000002 RID: 2 RVA: 0x00002058 File Offset: 0x00000258 + public static void PatchSignatureCheck(ModuleDefMD module) + { + bool flag = false; + foreach (TypeDef typeDef in from a in module.Types + where a.Methods.Any((MethodDef b) => b.IsPinvokeImpl && b.ImplMap.Name == "WinVerifyTrust") + select a) + { + foreach (MethodDef methodDef in typeDef.Methods.Where((MethodDef a) => a.ReturnType.TypeName == "Boolean")) + { + Program.Verbose("Writing ret true to " + methodDef.FullName); + methodDef.Body.Instructions.Insert(0, new Instruction(OpCodes.Ldc_I4_1)); + methodDef.Body.Instructions.Insert(1, new Instruction(OpCodes.Ret)); + flag = true; + } + } + if (!flag) + { + Console.WriteLine("WARNING: did not write any changes."); + } + } + + // Token: 0x06000003 RID: 3 RVA: 0x0000217C File Offset: 0x0000037C + public static void PatchExecutableName(ModuleDefMD module) + { + try + { + MethodDef methodDef = (MethodDef)module.EntryPoint.Body.Instructions[0].Operand; + Program.Verbose("Patching name check in " + methodDef.FullName); + IList instructions = methodDef.Body.Instructions; + Instruction item = instructions.Last((Instruction a) => a.OpCode == OpCodes.Brfalse_S); + int num = instructions.IndexOf(item); + for (int i = -4; i < 10; i++) + { + instructions[i + num].OpCode = OpCodes.Nop; + } + } + catch (Exception ex) + { + Console.WriteLine("Could not patch: " + ex.Message); + } + } + + // Token: 0x06000004 RID: 4 RVA: 0x0000224C File Offset: 0x0000044C + public static MethodDef FindEazStringMethod(ModuleDefMD module) + { + MethodDef methodDef = (MethodDef)module.EntryPoint.Body.Instructions[0].Operand; + IList instructions = methodDef.Body.Instructions; + for (int i = 0; i < methodDef.Body.Instructions.Count; i++) + { + Instruction instruction = instructions[i]; + MemberRef memberRef; + if (instruction.OpCode == OpCodes.Newobj && (memberRef = (instruction.Operand as MemberRef)) != null && memberRef.Class.Name == "Exception") + { + return (MethodDef)instructions[i - 1].Operand; + } + } + return null; + } + } +} diff --git a/osu!decoder/CliOptions.cs b/osu!decoder/CliOptions.cs new file mode 100644 index 0000000..eb2a3e8 --- /dev/null +++ b/osu!decoder/CliOptions.cs @@ -0,0 +1,98 @@ +using System; +using CommandLine; +using CommandLine.Text; + +namespace osu_decoder_dnlib +{ + // Token: 0x02000004 RID: 4 + internal class CliOptions + { + // Token: 0x17000001 RID: 1 + // (get) Token: 0x0600000C RID: 12 RVA: 0x00002388 File Offset: 0x00000588 + // (set) Token: 0x0600000D RID: 13 RVA: 0x00002390 File Offset: 0x00000590 + [ValueOption(0)] + public string Input { get; set; } + + // Token: 0x17000002 RID: 2 + // (get) Token: 0x0600000E RID: 14 RVA: 0x0000239C File Offset: 0x0000059C + // (set) Token: 0x0600000F RID: 15 RVA: 0x000023A4 File Offset: 0x000005A4 + [ValueOption(1)] + public string Input2 { get; set; } + + // Token: 0x17000003 RID: 3 + // (get) Token: 0x06000010 RID: 16 RVA: 0x000023B0 File Offset: 0x000005B0 + // (set) Token: 0x06000011 RID: 17 RVA: 0x000023B8 File Offset: 0x000005B8 + [Option('o', "output", HelpText = "Path of the output file")] + public string Output { get; set; } + + // Token: 0x17000004 RID: 4 + // (get) Token: 0x06000012 RID: 18 RVA: 0x000023C4 File Offset: 0x000005C4 + // (set) Token: 0x06000013 RID: 19 RVA: 0x000023CC File Offset: 0x000005CC + [Option('v', "verbose", HelpText = "Prints more output.")] + public bool Verbose { get; set; } + + // Token: 0x17000005 RID: 5 + // (get) Token: 0x06000014 RID: 20 RVA: 0x000023D8 File Offset: 0x000005D8 + // (set) Token: 0x06000015 RID: 21 RVA: 0x000023E0 File Offset: 0x000005E0 + [Option('d', "debug", HelpText = "Prints a lot more output, you should pipe this to a file.")] + public bool Debug { get; set; } + + // Token: 0x17000006 RID: 6 + // (get) Token: 0x06000016 RID: 22 RVA: 0x000023EC File Offset: 0x000005EC + // (set) Token: 0x06000017 RID: 23 RVA: 0x000023F4 File Offset: 0x000005F4 + [Option('s', "sourcemap", HelpText = "Writes a file containing an original:decoded source map.")] + public bool Sourcemap { get; set; } + + // Token: 0x17000007 RID: 7 + // (get) Token: 0x06000018 RID: 24 RVA: 0x00002400 File Offset: 0x00000600 + // (set) Token: 0x06000019 RID: 25 RVA: 0x00002408 File Offset: 0x00000608 + [Option('r', "dry-run", HelpText = "Do not write decoded executable to disk.")] + public bool DryRun { get; set; } + + // Token: 0x17000008 RID: 8 + // (get) Token: 0x0600001A RID: 26 RVA: 0x00002414 File Offset: 0x00000614 + public string Password { get; } = "recorderinthesandybridge"; + + // Token: 0x17000009 RID: 9 + // (get) Token: 0x0600001B RID: 27 RVA: 0x0000241C File Offset: 0x0000061C + // (set) Token: 0x0600001C RID: 28 RVA: 0x00002424 File Offset: 0x00000624 + [Option("exp-eagerdecode", HelpText = "EXPERIMENTAL: decode from reference in method bodies", DefaultValue = false)] + public bool ExperimentEagerDecoding { get; set; } + + // Token: 0x1700000A RID: 10 + // (get) Token: 0x0600001D RID: 29 RVA: 0x00002430 File Offset: 0x00000630 + // (set) Token: 0x0600001E RID: 30 RVA: 0x00002438 File Offset: 0x00000638 + [Option("exp-secondpass", HelpText = "EXPERIMENTAL: do a second pass to check if everything is decoded", DefaultValue = false)] + public bool ExperimentSecondPass { get; set; } + + // Token: 0x1700000B RID: 11 + // (get) Token: 0x0600001F RID: 31 RVA: 0x00002444 File Offset: 0x00000644 + // (set) Token: 0x06000020 RID: 32 RVA: 0x0000244C File Offset: 0x0000064C + [Option("exp-patch", HelpText = "Apply patch to remove signature and filename check", DefaultValue = false)] + public bool ExperimentPatch { get; set; } + + // Token: 0x1700000C RID: 12 + // (get) Token: 0x06000021 RID: 33 RVA: 0x00002458 File Offset: 0x00000658 + // (set) Token: 0x06000022 RID: 34 RVA: 0x00002460 File Offset: 0x00000660 + [Option("exp-full", HelpText = "Decode everything, resulting in unrunnable executable", DefaultValue = false)] + public bool ExperimentFullDecrypt { get; set; } + + // Token: 0x06000023 RID: 35 RVA: 0x0000246C File Offset: 0x0000066C + [HelpOption] + public string GetHelp() + { + HelpText helpText = new HelpText(); + helpText.Heading = new HeadingInfo("osu!decoder", "v1.2"); + helpText.Copyright = new CopyrightInfo("HoLLy/JustM3", 2017); + helpText.AdditionalNewLineAfterOption = false; + helpText.AddDashesToOption = true; + helpText.MaximumDisplayWidth = Console.BufferWidth; + helpText.AddPreOptionsLine("\nExample usage:"); + helpText.AddPreOptionsLine("\t- osu!decoder osu!.exe"); + helpText.AddPreOptionsLine("\t- osu!decoder osu!.exe -v -p updated_password -o c:/newosu!.exe"); + helpText.AddOptions(this); + helpText.AddPostOptionsLine("Please do not distribute. If you have this, it means I trust you :)"); + return helpText; + } + } +} diff --git a/osu!decoder/CryptoHelper.cs b/osu!decoder/CryptoHelper.cs new file mode 100644 index 0000000..b1c0fc4 --- /dev/null +++ b/osu!decoder/CryptoHelper.cs @@ -0,0 +1,137 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +namespace osu_decoder_dnlib +{ + // Token: 0x02000005 RID: 5 + internal class CryptoHelper + { + // Token: 0x04000012 RID: 18 + private readonly SymmetricAlgorithm _symmetricAlgorithm; + + // Token: 0x06000025 RID: 37 RVA: 0x00002518 File Offset: 0x00000718 + public CryptoHelper(string password) + { + Program.Verbose("Creating new decoder with password " + password); + this._symmetricAlgorithm = new RijndaelManaged + { + KeySize = 256, + BlockSize = 128, + IV = new Rfc2898DeriveBytes(password, new byte[] + { + 28, + 136, + 27, + 216, + 83, + 147, + 140, + 207, + 60, + 153, + 41, + 107, + 117, + 164, + 37, + 157, + 94, + 233, + 51, + 48, + 146, + 108, + 127, + 191, + 30, + 226, + 250, + 88, + 109, + 7, + 132, + 15 + }).GetBytes(16), + Key = new Rfc2898DeriveBytes(password, new byte[] + { + 167, + 126, + 112, + 16, + 4, + 244, + 15, + 120, + 135, + 116, + 123, + 212, + 157, + 48, + 5, + 194, + 12, + 179, + 153, + 201, + 204, + 249, + 248, + 212, + 86, + 20, + 215, + 55, + 105, + 157, + 111, + 11 + }).GetBytes(32) + }; + } + + // Token: 0x06000026 RID: 38 RVA: 0x000025A8 File Offset: 0x000007A8 + internal string Decrypt(string input) + { + if (!input.StartsWith("#=")) + { + throw new NotImplementedException("I don't support this encryption type"); + } + string text = input.Substring(2); + if (text.StartsWith("q")) + { + text = text.Substring(1); + text = text.Replace('_', '+').Replace('$', '/'); + string text2 = this.DecryptWithXor(Convert.FromBase64String(text)); + Program.Debug(string.Format("Decrypted {0} to {1}", input, text2)); + return text2; + } + throw new NotImplementedException("I don't support this encryption type (yet), poke me about it"); + } + + // Token: 0x06000027 RID: 39 RVA: 0x0000262C File Offset: 0x0000082C + private string DecryptWithXor(byte[] toDecrypt) + { + MemoryStream memoryStream = new MemoryStream(); + using (ICryptoTransform cryptoTransform = this._symmetricAlgorithm.CreateDecryptor()) + { + CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write); + cryptoStream.Write(toDecrypt, 0, toDecrypt.Length); + cryptoStream.FlushFinalBlock(); + cryptoStream.Close(); + } + toDecrypt = memoryStream.ToArray(); + byte b = toDecrypt[toDecrypt.Length - 1]; + Array.Resize(ref toDecrypt, toDecrypt.Length - 1); + for (int i = 0; i < toDecrypt.Length; i++) + { + byte[] array = toDecrypt; + int num = i; + array[num] ^= b; + } + return Encoding.UTF8.GetString(toDecrypt); + } + } +} diff --git a/osu!decoder/Processors/AssemblyChecker.cs b/osu!decoder/Processors/AssemblyChecker.cs new file mode 100644 index 0000000..d3b86f7 --- /dev/null +++ b/osu!decoder/Processors/AssemblyChecker.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace osu_decoder_dnlib.Processors +{ + // Token: 0x0200000B RID: 11 + internal class AssemblyChecker + { + // Token: 0x0400001C RID: 28 + private static readonly Regex RegexObfuscated = new Regex("^#=[a-zA-Z0-9_$]+={0,2}$"); + + // Token: 0x0400001D RID: 29 + private static CryptoHelper _crypto; + + // Token: 0x06000037 RID: 55 RVA: 0x00002AC0 File Offset: 0x00000CC0 + public static void Process(ModuleDefMD ass) + { + AssemblyChecker._crypto = new CryptoHelper(Program.Options.Password); + AssemblyChecker.CheckRecursive(ass.Types); + } + + // Token: 0x06000038 RID: 56 RVA: 0x00002AE4 File Offset: 0x00000CE4 + private static void CheckRecursive(IEnumerable members) + { + foreach (IFullName fullName in members) + { + AssemblyChecker.RegexObfuscated.IsMatch(fullName.Name); + if (fullName is TypeDef) + { + TypeDef typeDef = (TypeDef)fullName; + AssemblyChecker.CheckRecursive(typeDef.Events); + AssemblyChecker.CheckRecursive(typeDef.Fields); + AssemblyChecker.CheckRecursive(typeDef.Methods); + AssemblyChecker.CheckRecursive(typeDef.NestedTypes); + AssemblyChecker.CheckRecursive(typeDef.Properties); + } + else if (fullName is MethodDef) + { + MethodDef methodDef = (MethodDef)fullName; + if (methodDef.HasBody) + { + foreach (Instruction instruction in methodDef.Body.Instructions) + { + if (instruction.Operand is IFullName) + { + IFullName fullName2 = (IFullName)instruction.Operand; + if (AssemblyChecker.RegexObfuscated.IsMatch(fullName2.Name)) + { + MemberRef memberRef = fullName2 as MemberRef; + string newValue = AssemblyChecker._crypto.Decrypt(fullName2.Name); + newValue = memberRef.FullName.Replace(fullName2.Name, newValue); + if (memberRef.Resolve() == null) + { + int num = 0; + foreach (TypeDef typeDef2 in memberRef.Module.Types) + { + memberRef.DeclaringType.Name == typeDef2.Name; + } + if (num == 0) + { + Console.WriteLine("fuck: " + memberRef.DeclaringType.Name + " | " + memberRef.FullName); + } + } + } + } + } + } + } + } + } + } +} diff --git a/osu!decoder/Processors/AssemblyDecoder.cs b/osu!decoder/Processors/AssemblyDecoder.cs new file mode 100644 index 0000000..e12e60d --- /dev/null +++ b/osu!decoder/Processors/AssemblyDecoder.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; +using dnlib.DotNet; +using dnlib.DotNet.Emit; + +namespace osu_decoder_dnlib.Processors +{ + // Token: 0x0200000C RID: 12 + internal class AssemblyDecoder + { + // Token: 0x0400001E RID: 30 + private static readonly Regex RegexObfuscated = new Regex("^#=[a-zA-Z0-9_$]+={0,2}$"); + + // Token: 0x0400001F RID: 31 + private static CryptoHelper _crypto; + + // Token: 0x04000020 RID: 32 + public static SourceMap SrcMap; + + // Token: 0x0600003B RID: 59 RVA: 0x00002D38 File Offset: 0x00000F38 + public static void Process(ModuleDefMD ass) + { + if (Program.Options.ExperimentFullDecrypt) + { + Program.Verbose("Unsafe decoding enabled! Output executable will likely not be runnable."); + } + AssemblyDecoder._crypto = new CryptoHelper(Program.Options.Password); + if (Program.Options.Sourcemap) + { + AssemblyDecoder.SrcMap = new SourceMap(); + } + AssemblyDecoder.DecodeRecursive(ass.Types, Program.Options.ExperimentFullDecrypt); + } + + // Token: 0x0600003C RID: 60 RVA: 0x00002D9C File Offset: 0x00000F9C + public static void DecodeSingleLayer(ModuleDefMD ass, string password) + { + AssemblyDecoder._crypto = new CryptoHelper(password); + AssemblyDecoder.DecodeAll(ass.Types); + } + + // Token: 0x0600003D RID: 61 RVA: 0x00002DB4 File Offset: 0x00000FB4 + private static void DecodeAll(IEnumerable members) + { + foreach (IFullName param in members) + { + AssemblyDecoder.DecodeSingle(param); + } + } + + // Token: 0x0600003E RID: 62 RVA: 0x00002DFC File Offset: 0x00000FFC + private static void DecodeRecursive(IEnumerable members, bool unSafe) + { + foreach (IFullName fullName in members) + { + TypeDef typeDef; + if ((typeDef = (fullName as TypeDef)) != null) + { + AssemblyDecoder.DecodeSingle(typeDef); + foreach (GenericParam param in typeDef.GenericParameters) + { + AssemblyDecoder.DecodeSingle(param); + } + AssemblyDecoder.DecodeRecursive(typeDef.Events, unSafe); + AssemblyDecoder.DecodeRecursive(typeDef.Fields, unSafe); + AssemblyDecoder.DecodeRecursive(typeDef.Methods, unSafe); + AssemblyDecoder.DecodeRecursive(typeDef.NestedTypes, unSafe); + AssemblyDecoder.DecodeRecursive(typeDef.Properties, unSafe); + using (IEnumerator enumerator3 = typeDef.Interfaces.GetEnumerator()) + { + while (enumerator3.MoveNext()) + { + InterfaceImpl interfaceImpl = enumerator3.Current; + AssemblyDecoder.DecodeSingle(interfaceImpl.Interface); + } + continue; + } + } + MethodDef methodDef; + if ((methodDef = (fullName as MethodDef)) != null) + { + if (unSafe) + { + AssemblyDecoder.DecodeSingle(fullName); + } + foreach (GenericParam param2 in methodDef.GenericParameters) + { + AssemblyDecoder.DecodeSingle(param2); + } + foreach (Parameter param3 in methodDef.Parameters) + { + AssemblyDecoder.DecodeSingle(param3); + } + if (methodDef.Body == null || !Program.Options.ExperimentEagerDecoding) + { + continue; + } + using (IEnumerator enumerator5 = methodDef.Body.Instructions.GetEnumerator()) + { + while (enumerator5.MoveNext()) + { + IFullName fullName2; + if ((fullName2 = (enumerator5.Current.Operand as IFullName)) != null && AssemblyDecoder.RegexObfuscated.IsMatch(fullName2.Name)) + { + AssemblyDecoder.DecodeSingle(fullName2); + } + } + continue; + } + } + if (fullName is FieldDef) + { + if (unSafe) + { + AssemblyDecoder.DecodeSingle(fullName); + } + } + else if (fullName is PropertyDef || fullName is EventDef) + { + AssemblyDecoder.DecodeSingle(fullName); + } + else + { + Program.Verbose("Unhandled type: " + fullName.GetType()); + AssemblyDecoder.DecodeSingle(fullName); + } + } + } + + // Token: 0x0600003F RID: 63 RVA: 0x000030B8 File Offset: 0x000012B8 + private static void DecodeSingle(IFullName param) + { + if (!AssemblyDecoder.RegexObfuscated.IsMatch(param.Name)) + { + return; + } + string text = AssemblyDecoder._crypto.Decrypt(param.Name); + SourceMap srcMap = AssemblyDecoder.SrcMap; + if (srcMap != null) + { + srcMap.Add(param.Name, text); + } + TypeDef typeDef; + if ((typeDef = (param as TypeDef)) != null && text.Contains('.')) + { + typeDef.Namespace = text.Substring(0, text.LastIndexOf('.')); + typeDef.Name = text.Substring(text.LastIndexOf('.') + 1); + return; + } + param.Name = text; + } + + // Token: 0x06000040 RID: 64 RVA: 0x00003168 File Offset: 0x00001368 + private static void DecodeSingle(IVariable param) + { + if (!AssemblyDecoder.RegexObfuscated.IsMatch(param.Name)) + { + return; + } + string text = AssemblyDecoder._crypto.Decrypt(param.Name); + SourceMap srcMap = AssemblyDecoder.SrcMap; + if (srcMap != null) + { + srcMap.Add(param.Name, text); + } + param.Name = text; + } + } +} diff --git a/osu!decoder/Program.cs b/osu!decoder/Program.cs new file mode 100644 index 0000000..d348560 --- /dev/null +++ b/osu!decoder/Program.cs @@ -0,0 +1,95 @@ +using System; +using CommandLine; +using dnlib.DotNet; +using osu_decoder_dnlib.Processors; + +namespace osu_decoder_dnlib +{ + // Token: 0x02000006 RID: 6 + internal class Program + { + // Token: 0x04000013 RID: 19 + internal static CliOptions Options = new CliOptions(); + + // Token: 0x04000014 RID: 20 + public const bool DebugBuild = false; + + // Token: 0x06000028 RID: 40 RVA: 0x000026C8 File Offset: 0x000008C8 + private static void Main(string[] args) + { + if (!Parser.Default.ParseArguments(args, Program.Options) || string.IsNullOrWhiteSpace(Program.Options.Input)) + { + Console.WriteLine(Program.Options.GetHelp()); + if (args.Length == 0) + { + Console.Write("Press any key to exit..."); + Console.ReadKey(true); + } + return; + } + Program.Verbose("Verbose output enabled."); + string.IsNullOrWhiteSpace(Program.Options.Input); + string input = Program.Options.Input; + Console.WriteLine("Loading assembly..."); + ModuleDefMD moduleDefMD = ModuleDefMD.Load(input, null); + Console.WriteLine("Loaded assembly."); + Program.Verbose("Total amount of types in root: " + moduleDefMD.Types.Count); + if (Program.Options.ExperimentPatch) + { + Console.WriteLine("Patching Authenticode/WinVerifyTrust"); + BinaryPatch.PatchSignatureCheck(moduleDefMD); + Console.WriteLine("Patching executable name check"); + BinaryPatch.PatchExecutableName(moduleDefMD); + } + Console.WriteLine("Decrypting..."); + AssemblyDecoder.Process(moduleDefMD); + Console.WriteLine("Finished decrypting."); + if (Program.Options.ExperimentSecondPass) + { + Console.WriteLine("Doing second pass to find bad methods"); + AssemblyChecker.Process(moduleDefMD); + } + string text = (!string.IsNullOrEmpty(Program.Options.Output)) ? Program.Options.Output : (input.Substring(0, input.LastIndexOf('.')) + "-decrypted" + input.Substring(input.LastIndexOf('.'))); + if (Program.Options.Sourcemap) + { + AssemblyDecoder.SrcMap.Entries.Sort((SourceMap.Entry entry, SourceMap.Entry entry1) => string.CompareOrdinal(entry.DecodedName, entry1.DecodedName)); + string text2 = string.Format("{0}.srcmap", text); + Console.WriteLine("Writing sourcemap to " + text2); + AssemblyDecoder.SrcMap.Write(text2); + } + if (!Program.Options.DryRun) + { + Console.WriteLine("Writing new assembly..."); + moduleDefMD.Write(text); + Console.WriteLine("Written to " + text); + return; + } + Console.WriteLine("Dry run, not writing new assembly to disk"); + } + + // Token: 0x06000029 RID: 41 RVA: 0x000028C0 File Offset: 0x00000AC0 + public static void Verbose(string input) + { + if (Program.Options.Verbose || Program.Options.Debug) + { + Console.WriteLine("[v] " + input); + } + } + + // Token: 0x0600002A RID: 42 RVA: 0x000028EC File Offset: 0x00000AEC + public static void Debug(string input) + { + if (Program.Options.Debug) + { + Console.WriteLine("[D] " + input); + } + } + + // Token: 0x0600002B RID: 43 RVA: 0x0000290C File Offset: 0x00000B0C + private static void UpdateProgress(string action, int cur, int max) + { + int length = max.ToString().Length; + Console.Write(string.Format("\r{0} [{1}/{2}]", action, cur.ToString().PadLeft(length), max.ToString().PadLeft(length))); + } + } +} diff --git a/osu!decoder/Properties/AssemblyInfo.cs b/osu!decoder/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..11751f1 --- /dev/null +++ b/osu!decoder/Properties/AssemblyInfo.cs @@ -0,0 +1,21 @@ +using System; +using System.Diagnostics; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: Guid("3f4b7f2f-5319-4bf2-b0c8-fe6f539fbf0e")] +[assembly: ComVisible(false)] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCopyright("Copyright © HoLLy 2017")] +[assembly: AssemblyProduct("osu!decoder dnlib")] +[assembly: AssemblyCompany("HoLLy")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyTitle("osu!decoder")] +[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] +[assembly: CompilationRelaxations(8)] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/osu!decoder/SourceMap.cs b/osu!decoder/SourceMap.cs new file mode 100644 index 0000000..4b47517 --- /dev/null +++ b/osu!decoder/SourceMap.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace osu_decoder_dnlib +{ + // Token: 0x02000008 RID: 8 + internal class SourceMap + { + // Token: 0x04000017 RID: 23 + public const string NameFormat = "{0}.srcmap"; + + // Token: 0x04000018 RID: 24 + public List Entries = new List(); + + // Token: 0x06000031 RID: 49 RVA: 0x0000298C File Offset: 0x00000B8C + public void Add(string orig, string dec) + { + if (this.ContainsKey(orig)) + { + return; + } + SourceMap.Entry item = new SourceMap.Entry + { + OriginalName = orig, + DecodedName = dec + }; + this.Entries.Add(item); + } + + // Token: 0x06000032 RID: 50 RVA: 0x000029CC File Offset: 0x00000BCC + public void Write(string path) + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (SourceMap.Entry entry in this.Entries) + { + stringBuilder.AppendLine(string.Format("{0}:{1}", entry.OriginalName, entry.DecodedName)); + } + File.WriteAllText(path, stringBuilder.ToString().TrimEnd(new char[] + { + '\r', + '\n' + })); + } + + // Token: 0x06000033 RID: 51 RVA: 0x00002A5C File Offset: 0x00000C5C + public bool ContainsKey(string orig) + { + return this.Entries.Any((SourceMap.Entry a) => a.OriginalName == orig); + } + + // Token: 0x02000009 RID: 9 + public struct Entry + { + // Token: 0x04000019 RID: 25 + public string OriginalName; + + // Token: 0x0400001A RID: 26 + public string DecodedName; + } + } +} diff --git a/osu!decoder/osu!decoder.csproj b/osu!decoder/osu!decoder.csproj new file mode 100644 index 0000000..a493e33 --- /dev/null +++ b/osu!decoder/osu!decoder.csproj @@ -0,0 +1,61 @@ + + + + + Debug + AnyCPU + {7CDE3892-D6ED-4784-AC5C-560649D75F9A} + Exe + osu!decoder + osu!decoder + v4.5.2 + 512 + osu_decoder_dnlib.Program + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + + + ..\packages\dnlib.1.6.1\lib\dnlib.dll + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/osu!decoder/packages.config b/osu!decoder/packages.config new file mode 100644 index 0000000..3d677df --- /dev/null +++ b/osu!decoder/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file