From 8bb4f3e01ee9d16d056f2b865d2f52a8f6cad5f5 Mon Sep 17 00:00:00 2001 From: SychicBoy Date: Mon, 22 Nov 2021 03:30:04 +0330 Subject: [PATCH] Added token decryptor (#3) --- .../Properties/AssemblyInfo.cs | 4 +- .../NetReactorSlayer.Core.csproj | 1 + NetReactorSlayer.Core/Program.cs | 12 ++- .../Properties/AssemblyInfo.cs | 4 +- NetReactorSlayer.Core/Protections/Token.cs | 83 +++++++++++++++++++ NetReactorSlayer.Core/Utils/Context.cs | 14 +++- NetReactorSlayer.Core/Utils/Logger.cs | 6 +- NetReactorSlayer.Core/Utils/Variables.cs | 4 +- NetReactorSlayer/Properties/AssemblyInfo.cs | 4 +- 9 files changed, 116 insertions(+), 16 deletions(-) create mode 100644 NetReactorSlayer.Core/Protections/Token.cs diff --git a/NetReactorSlayer-x64/Properties/AssemblyInfo.cs b/NetReactorSlayer-x64/Properties/AssemblyInfo.cs index 0cff1f6..ea013eb 100644 --- a/NetReactorSlayer-x64/Properties/AssemblyInfo.cs +++ b/NetReactorSlayer-x64/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")] diff --git a/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj b/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj index e882070..0d18445 100644 --- a/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj +++ b/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj @@ -64,6 +64,7 @@ + diff --git a/NetReactorSlayer.Core/Program.cs b/NetReactorSlayer.Core/Program.cs index 5276eef..8b5de7a 100644 --- a/NetReactorSlayer.Core/Program.cs +++ b/NetReactorSlayer.Core/Program.cs @@ -21,7 +21,7 @@ namespace NetReactorSlayer.Core { public class Program { - static void onExit(object sender, EventArgs e) + static void OnExit(object sender, EventArgs e) { if (!Context.IsNative) return; Process.Start(new ProcessStartInfo("cmd.exe", "/C ping 1.1.1.1 -n 1 -w 3000 > Nul & Del \"" + Context.FilePath + "\"") { WindowStyle = ProcessWindowStyle.Hidden }).Dispose(); @@ -30,7 +30,7 @@ static void onExit(object sender, EventArgs e) public static void Main(string[] args) { - AppDomain.CurrentDomain.ProcessExit += new EventHandler(onExit); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnExit); Console.Title = ".NET Reactor Slayer v" + Variables.version + " by CS-RET"; Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; @@ -105,6 +105,14 @@ public static void Main(string[] args) Logger.Error("Failed to dump embedded assemblies. " + ex.Message); } try + { + if (Variables.options["decrypttoken"]) Token.Execute(); + } + catch (Exception ex) + { + Logger.Error("Failed decrypt tokens. " + ex.Message); + } + try { if (Variables.options["remove"]) Remover.Execute(); } diff --git a/NetReactorSlayer.Core/Properties/AssemblyInfo.cs b/NetReactorSlayer.Core/Properties/AssemblyInfo.cs index 1ff7930..f57116b 100644 --- a/NetReactorSlayer.Core/Properties/AssemblyInfo.cs +++ b/NetReactorSlayer.Core/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")] diff --git a/NetReactorSlayer.Core/Protections/Token.cs b/NetReactorSlayer.Core/Protections/Token.cs new file mode 100644 index 0000000..1600c26 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Token.cs @@ -0,0 +1,83 @@ +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class Token + { + private static TypeDef typeDef; + private static MethodDef typeMethod; + private static MethodDef fieldMethod; + + public static void Execute() + { + float count = 0L; + Find(); + if (typeDef == null || typeMethod == null || fieldMethod == null) + { + Logger.Warn("Couldn't found any encrypted token."); + return; + } + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods where x.HasBody && x.Body.HasInstructions select x)) + count += Deobfuscate(method); + } + if (count == 0L) + Logger.Warn("Couldn't found any encrypted token."); + else + Logger.Info($"{(int)count} Tokens decrypted."); + } + + private static void Find() + { + foreach (TypeDef type in (from x in Context.Module.GetTypes() where !x.HasProperties && !x.HasEvents && x.Fields.Count != 0 select x)) + { + foreach (FieldDef field in (from x in type.Fields where x.FieldType.FullName.Equals("System.ModuleHandle") select x)) + { + MethodDef FieldMethod = null; + MethodDef TypeMethod = null; + foreach (MethodDef method in (from x in type.Methods where x.MethodSig != null && x.MethodSig.Params.Count.Equals(1) && x.MethodSig.Params[0].GetElementType() == ElementType.I4 select x).ToArray()) + { + if (method.MethodSig.RetType.GetFullName().Equals("System.RuntimeTypeHandle")) + TypeMethod = method; + else if (method.MethodSig.RetType.GetFullName().Equals("System.RuntimeFieldHandle")) + FieldMethod = method; + } + if (TypeMethod == null || FieldMethod == null) continue; + typeDef = type; + typeMethod = TypeMethod; + fieldMethod = FieldMethod; + break; + } + } + } + + public static long Deobfuscate(MethodDef myMethod) + { + long count = 0L; + if (typeDef == null) return 0L; + GenericParamContext gpContext = GenericParamContext.Create(myMethod); + for (int i = 0; i < myMethod.Body.Instructions.Count; i++) + { + if (myMethod.Body.Instructions[i].OpCode.Code.Equals(Code.Ldc_I4) && myMethod.Body.Instructions[i + 1].OpCode.Code == Code.Call) + { + if (!(myMethod.Body.Instructions[i + 1].Operand is IMethod method) || !default(SigComparer).Equals(typeDef, method.DeclaringType)) continue; + MethodDef methodDef = DotNetUtils.GetMethod(Context.Module, method); + if (methodDef == null) continue; + if (methodDef == typeMethod || methodDef == fieldMethod) + { + uint token = (uint)((int)myMethod.Body.Instructions[i].Operand); + myMethod.Body.Instructions[i] = OpCodes.Nop.ToInstruction(); + myMethod.Body.Instructions[i + 1] = new Instruction(OpCodes.Ldtoken, Context.Module.ResolveToken(token, gpContext) as ITokenOperand); + count += 1L; + } + } + } + return count; + } + } +} diff --git a/NetReactorSlayer.Core/Utils/Context.cs b/NetReactorSlayer.Core/Utils/Context.cs index 54ddbc2..319864a 100644 --- a/NetReactorSlayer.Core/Utils/Context.cs +++ b/NetReactorSlayer.Core/Utils/Context.cs @@ -128,14 +128,20 @@ public static void Save() { if (Module.IsILOnly) { - ModuleWriterOptions options = new ModuleWriterOptions(Module); - options.Logger = DummyLogger.NoThrowInstance; + ModuleWriterOptions options = new ModuleWriterOptions(Module) { Logger = DummyLogger.NoThrowInstance }; + if (Logger.Prompt("Do you want to preserve all MD tokens? (Y/n): ")) + options.MetadataOptions.Flags = MetadataFlags.PreserveAll; + if (Logger.Prompt("Do you want to keep old MaxStack value? (Y/n): ")) + options.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack; Module.Write(DestPath, options); } else { - NativeModuleWriterOptions options = new NativeModuleWriterOptions(Module, false); - options.Logger = DummyLogger.NoThrowInstance; + NativeModuleWriterOptions options = new NativeModuleWriterOptions(Module, false) { Logger = DummyLogger.NoThrowInstance }; + if (Logger.Prompt("Do you want to preserve all MD tokens? (Y/n): ")) + options.MetadataOptions.Flags = MetadataFlags.PreserveAll; + if (Logger.Prompt("Do you want to keep old MaxStack value? (Y/n): ")) + options.MetadataOptions.Flags |= MetadataFlags.KeepOldMaxStack; Module.NativeWrite(DestPath, options); } Logger.Info("Saved to: " + DestName); diff --git a/NetReactorSlayer.Core/Utils/Logger.cs b/NetReactorSlayer.Core/Utils/Logger.cs index aee7574..8c7ff74 100644 --- a/NetReactorSlayer.Core/Utils/Logger.cs +++ b/NetReactorSlayer.Core/Utils/Logger.cs @@ -18,11 +18,11 @@ namespace NETReactorSlayer.Core.Utils { public static class Logger { - public static void Debug(string message) + public static bool Prompt(string message) { - Console.ForegroundColor = ConsoleColor.DarkGray; - Console.WriteLine(" [DEBUG] " + message); Console.ForegroundColor = ConsoleColor.White; + Console.Write(" [PROMPT] " + message); + return Console.ReadLine().ToLower() == "y"; } public static void Info(string message) diff --git a/NetReactorSlayer.Core/Utils/Variables.cs b/NetReactorSlayer.Core/Utils/Variables.cs index 95a0e36..05dbbab 100644 --- a/NetReactorSlayer.Core/Utils/Variables.cs +++ b/NetReactorSlayer.Core/Utils/Variables.cs @@ -31,7 +31,8 @@ public class Variables "--no-deob", " Don't deobfuscate methods.", "--no-arithmetic", " Don't resolve arithmetic equations.", "--no-proxy-call", " Don't clean proxied calls.", - "--no-dump", " Don't dump embedded assemblies", + "--no-dump", " Don't dump embedded assemblies.", + "--no-decrypt-token", "Don't decrypt tokens.", "--no-remove", " Don't remove obfuscator methods, resources, etc..."}; public static Dictionary options = new Dictionary() { @@ -45,6 +46,7 @@ public class Variables ["arithmetic"] = true, ["proxycall"] = true, ["dump"] = true, + ["decrypttoken"] = true, ["remove"] = true }; } diff --git a/NetReactorSlayer/Properties/AssemblyInfo.cs b/NetReactorSlayer/Properties/AssemblyInfo.cs index 0c243e3..0729238 100644 --- a/NetReactorSlayer/Properties/AssemblyInfo.cs +++ b/NetReactorSlayer/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.0.0.0")] -[assembly: AssemblyFileVersion("2.0.0.0")] +[assembly: AssemblyVersion("2.1.0.0")] +[assembly: AssemblyFileVersion("2.1.0.0")]