From 699c4733568a8b13da762366eabf1604cec8bfd4 Mon Sep 17 00:00:00 2001 From: Owner Date: Mon, 4 Oct 2021 18:56:41 +0330 Subject: [PATCH] Add project files. --- .editorconfig | 4 + NetReactorSlayer-x64/App.config | 10 + .../NetReactorSlayer-x64.csproj | 64 ++ NetReactorSlayer-x64/Program.cs | 7 + .../Properties/AssemblyInfo.cs | 35 + NetReactorSlayer.Core/Libs/de4dot.blocks.dll | Bin 0 -> 163840 bytes .../NetReactorSlayer.Core.csproj | 95 +++ NetReactorSlayer.Core/Program.cs | 47 ++ .../Properties/AssemblyInfo.cs | 35 + NetReactorSlayer.Core/Protections/Anti.cs | 49 ++ .../Protections/ControlFlow.cs | 132 +++ .../Protections/EmbeddedAsm.cs | 140 +++ NetReactorSlayer.Core/Protections/HideCall.cs | 320 +++++++ NetReactorSlayer.Core/Protections/Native.cs | 227 +++++ NetReactorSlayer.Core/Protections/NecroBit.cs | 565 +++++++++++++ .../Protections/ProxyCall.cs | 111 +++ NetReactorSlayer.Core/Protections/Remover.cs | 103 +++ .../Protections/Resources.cs | 796 ++++++++++++++++++ NetReactorSlayer.Core/Protections/Strings.cs | 106 +++ NetReactorSlayer.Core/Utils/Context.cs | 150 ++++ NetReactorSlayer.Core/Utils/Logger.cs | 94 +++ NetReactorSlayer.Core/Utils/Variables.cs | 35 + .../Utils/de4dot/ArrayFinder.cs | 232 +++++ .../Utils/de4dot/AssemblyModule.cs | 100 +++ .../Utils/de4dot/DeobUtils.cs | 309 +++++++ .../Utils/de4dot/DumpedMethodsRestorer.cs | 85 ++ .../Utils/de4dot/LocalTypes.cs | 101 +++ .../Utils/de4dot/MethodBodyParser.cs | 182 ++++ .../Utils/de4dot/MyPEImage.cs | 214 +++++ NetReactorSlayer.Core/Utils/de4dot/QuickLZ.cs | 169 ++++ .../Utils/de4dot/TheAssemblyResolver.cs | 208 +++++ NetReactorSlayer.Core/app.config | 7 + NetReactorSlayer.Core/packages.config | 6 + NetReactorSlayer.sln | 37 + NetReactorSlayer/App.config | 6 + NetReactorSlayer/NetReactorSlayer.csproj | 64 ++ NetReactorSlayer/Program.cs | 7 + NetReactorSlayer/Properties/AssemblyInfo.cs | 35 + 38 files changed, 4887 insertions(+) create mode 100644 .editorconfig create mode 100644 NetReactorSlayer-x64/App.config create mode 100644 NetReactorSlayer-x64/NetReactorSlayer-x64.csproj create mode 100644 NetReactorSlayer-x64/Program.cs create mode 100644 NetReactorSlayer-x64/Properties/AssemblyInfo.cs create mode 100644 NetReactorSlayer.Core/Libs/de4dot.blocks.dll create mode 100644 NetReactorSlayer.Core/NetReactorSlayer.Core.csproj create mode 100644 NetReactorSlayer.Core/Program.cs create mode 100644 NetReactorSlayer.Core/Properties/AssemblyInfo.cs create mode 100644 NetReactorSlayer.Core/Protections/Anti.cs create mode 100644 NetReactorSlayer.Core/Protections/ControlFlow.cs create mode 100644 NetReactorSlayer.Core/Protections/EmbeddedAsm.cs create mode 100644 NetReactorSlayer.Core/Protections/HideCall.cs create mode 100644 NetReactorSlayer.Core/Protections/Native.cs create mode 100644 NetReactorSlayer.Core/Protections/NecroBit.cs create mode 100644 NetReactorSlayer.Core/Protections/ProxyCall.cs create mode 100644 NetReactorSlayer.Core/Protections/Remover.cs create mode 100644 NetReactorSlayer.Core/Protections/Resources.cs create mode 100644 NetReactorSlayer.Core/Protections/Strings.cs create mode 100644 NetReactorSlayer.Core/Utils/Context.cs create mode 100644 NetReactorSlayer.Core/Utils/Logger.cs create mode 100644 NetReactorSlayer.Core/Utils/Variables.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/ArrayFinder.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/AssemblyModule.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/DeobUtils.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/DumpedMethodsRestorer.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/LocalTypes.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/MethodBodyParser.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/MyPEImage.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/QuickLZ.cs create mode 100644 NetReactorSlayer.Core/Utils/de4dot/TheAssemblyResolver.cs create mode 100644 NetReactorSlayer.Core/app.config create mode 100644 NetReactorSlayer.Core/packages.config create mode 100644 NetReactorSlayer.sln create mode 100644 NetReactorSlayer/App.config create mode 100644 NetReactorSlayer/NetReactorSlayer.csproj create mode 100644 NetReactorSlayer/Program.cs create mode 100644 NetReactorSlayer/Properties/AssemblyInfo.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6874697 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# IDE0022: Use block body for methods +csharp_style_expression_bodied_methods = true diff --git a/NetReactorSlayer-x64/App.config b/NetReactorSlayer-x64/App.config new file mode 100644 index 0000000..8fe7366 --- /dev/null +++ b/NetReactorSlayer-x64/App.config @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer-x64/NetReactorSlayer-x64.csproj b/NetReactorSlayer-x64/NetReactorSlayer-x64.csproj new file mode 100644 index 0000000..0a19dc5 --- /dev/null +++ b/NetReactorSlayer-x64/NetReactorSlayer-x64.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {4363303C-AF4C-4C1B-8284-FC468514C15D} + Exe + NetReactorSlayer_x64 + NetReactorSlayer-x64 + v4.5 + 512 + true + + + + + x64 + true + full + false + ..\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + NetReactorSlayer_x64.Program + + + + False + ..\Debug\NetReactorSlayer.Core.dll + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer-x64/Program.cs b/NetReactorSlayer-x64/Program.cs new file mode 100644 index 0000000..6dfe7eb --- /dev/null +++ b/NetReactorSlayer-x64/Program.cs @@ -0,0 +1,7 @@ +namespace NetReactorSlayer_x64 +{ + class Program + { + static void Main(string[] args) => NetReactorSlayer.Core.Program.Main(args); + } +} diff --git a/NetReactorSlayer-x64/Properties/AssemblyInfo.cs b/NetReactorSlayer-x64/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9a47044 --- /dev/null +++ b/NetReactorSlayer-x64/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NetReactorSlayer-x64")] +[assembly: AssemblyDescription("Deobfuscator for eziriz.com/dotnet_reactor.htm")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CS-RET")] +[assembly: AssemblyProduct("NetReactorSlayer")] +[assembly: AssemblyCopyright("Copyright © 2021-2021 CS-RET")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4363303c-af4c-4c1b-8284-fc468514c15d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NetReactorSlayer.Core/Libs/de4dot.blocks.dll b/NetReactorSlayer.Core/Libs/de4dot.blocks.dll new file mode 100644 index 0000000000000000000000000000000000000000..ffe148fd4285b75adc877b7d9fc0b302b6ed1725 GIT binary patch literal 163840 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 literal 0 HcmV?d00001 diff --git a/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj b/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj new file mode 100644 index 0000000..33594de --- /dev/null +++ b/NetReactorSlayer.Core/NetReactorSlayer.Core.csproj @@ -0,0 +1,95 @@ + + + + + Debug + AnyCPU + {11E508F0-7AA3-46BD-9891-766DEB293830} + Library + Properties + NetReactorSlayer.Core + NetReactorSlayer.Core + v4.5 + 512 + true + + + + + true + full + false + ..\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + + ..\packages\Lib.Harmony.2.1.1\lib\net45\0Harmony.dll + + + False + Libs\de4dot.blocks.dll + + + ..\packages\dnlib.3.3.2\lib\net45\dnlib.dll + + + ..\packages\SharpZipLib.1.3.3\lib\net45\ICSharpCode.SharpZipLib.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer.Core/Program.cs b/NetReactorSlayer.Core/Program.cs new file mode 100644 index 0000000..cf535b6 --- /dev/null +++ b/NetReactorSlayer.Core/Program.cs @@ -0,0 +1,47 @@ +using NETReactorSlayer.Core.Protections; +using NETReactorSlayer.Core.Utils; +using System; +using System.Diagnostics; + +namespace NetReactorSlayer.Core +{ + public class Program + { + public static void Main(string[] args) + { + Console.Title = ".NET Reactor Slayer v" + Variables.version + " by CS-RET"; + Console.BackgroundColor = ConsoleColor.Black; + Console.ForegroundColor = ConsoleColor.White; + Console.Clear(); + Logger.PrintLogo(); + if (Context.Parse(args)) + { + if (Variables.options["necrobit"]) NecroBit.Execute(); + + ControlFlow.Execute(); + + Anti.Execute( + Variables.options["antidebug"], + Variables.options["antitamper"]); + + if (Variables.options["proxycall"]) ProxyCall.Execute(); + + if (Variables.options["hidecall"]) HideCall.Execute(); + + if (Variables.options["str"]) Strings.Execute(); + + if (Variables.options["rsrc"]) Resources.Execute(); + + if (Variables.options["dump"]) EmbeddedAsm.Execute(); + + Remover.Execute(); + Context.Save(); + } + Console.WriteLine("\r\n Press any key to exit . . ."); + Console.ReadKey(); + 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(); + Process.GetCurrentProcess().Kill(); + } + } +} diff --git a/NetReactorSlayer.Core/Properties/AssemblyInfo.cs b/NetReactorSlayer.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c0dbd40 --- /dev/null +++ b/NetReactorSlayer.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NetReactorSlayer.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NetReactorSlayer.Core")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("11e508f0-7aa3-46bd-9891-766deb293830")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NetReactorSlayer.Core/Protections/Anti.cs b/NetReactorSlayer.Core/Protections/Anti.cs new file mode 100644 index 0000000..af69956 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Anti.cs @@ -0,0 +1,49 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class Anti + { + public static void Execute(bool antiDebug = true, bool antiTamper = true) + { + bool isAntiTamperFound = false; + bool isAntiDebugFound = false; + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods where x.HasBody && x.Body.HasInstructions && x.IsStatic select x).ToArray()) + { + foreach (Instruction instruction in (from x in method.Body.Instructions where x.OpCode.Equals(OpCodes.Ldstr) select x).ToArray()) + { + if (instruction.Operand.ToString().Contains("Debugger Detected") && antiDebug) + { + isAntiDebugFound = true; + Remover.MethodsToPatch.Add(method); + Instruction ins = Instruction.Create(OpCodes.Ret); + CilBody cli = new CilBody(); + cli.Instructions.Add(ins); + method.Body = cli; + Logger.Info("Anti debugger removed."); + } + if (instruction.Operand.ToString().Contains("is tampered") && antiTamper) + { + isAntiTamperFound = true; + Remover.MethodsToPatch.Add(method); + Instruction ins = Instruction.Create(OpCodes.Ret); + CilBody cli = new CilBody(); + cli.Instructions.Add(ins); + method.Body = cli; + Logger.Info("Anti tamper removed."); + } + } + } + } + if (!isAntiTamperFound) + Logger.Warn("Couldn't find anti tamper method."); + if (!isAntiDebugFound) + Logger.Warn("Couldn't find debugger method."); + } + } +} diff --git a/NetReactorSlayer.Core/Protections/ControlFlow.cs b/NetReactorSlayer.Core/Protections/ControlFlow.cs new file mode 100644 index 0000000..0c59204 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/ControlFlow.cs @@ -0,0 +1,132 @@ +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System.Collections.Generic; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class ControlFlow + { + public static bool ContainsSwitch(MethodDef method) + { + return method.Body.Instructions.Any((Instruction instr) => instr.OpCode.Equals(OpCodes.Switch)); + } + public static void Execute() + { + long count = 0L; + BlocksCflowDeobfuscator blocksCflowDeobfuscator = new BlocksCflowDeobfuscator(); + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods where x.HasBody && x.Body.HasInstructions select x).ToArray()) + { + if (Variables.options["arithmetic"]) + count += Arithmetic(method, type); + if (!Variables.options["deob"]) continue; + try + { + blocksCflowDeobfuscator = new BlocksCflowDeobfuscator(); + Blocks blocks = new Blocks(method); + List allBlocks = blocks.MethodBlocks.GetAllBlocks(); + blocks.RemoveDeadBlocks(); + blocks.RepartitionBlocks(); + blocks.UpdateBlocks(); + blocks.Method.Body.SimplifyBranches(); + blocks.Method.Body.OptimizeBranches(); + blocksCflowDeobfuscator.Initialize(blocks); + blocksCflowDeobfuscator.Deobfuscate(); + blocks.RepartitionBlocks(); + blocks.GetCode(out IList instructions, out IList exceptionHandlers); + DotNetUtils.RestoreBody(method, instructions, exceptionHandlers); + } + catch + { + } + } + } + if (count > 0L) Logger.Info((int)count + " Arithmetic equations resolved."); + else Logger.Warn("Couldn't found any arithmetic equation in methods."); + } + + static long Arithmetic(MethodDef method, TypeDef type) + { + long count = 0L; + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + try + { + if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Ldsfld) && method.Body.Instructions[i].Operand.ToString().Contains("System.Int32") && method.Body.Instructions[i + 1].IsConditionalBranch() && method.Body.Instructions[i + 2].OpCode.Equals(OpCodes.Pop)) + { + if (!(method.Body.Instructions[i].Operand is FieldDef field) || field.FieldType.FullName != "System.Int32" || !field.IsAssembly || !field.IsStatic || field.DeclaringType == null || field.DeclaringType == type || field.DeclaringType.Namespace.String != "") continue; + var obj = Context.Assembly.ManifestModule.ResolveField((int)field.MDToken.Raw).GetValue(null); + if (method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Brtrue)) + { + if (int.Parse(obj.ToString()) == 0) + { + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Nop; + } + else + { + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Br_S; + } + count += 1L; + } + else if (method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Brfalse)) + { + if (int.Parse(obj.ToString()) == 0) + { + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Br_S; + } + else + { + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Nop; + } + count += 1L; + } + } + else + { + if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Call) && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Brtrue) && method.Body.Instructions[i + 2].OpCode.Equals(OpCodes.Pop)) + { + if (method.Body.Instructions[i].Operand.ToString().Contains("System.Boolean")) + { + count += 1L; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Br_S; + } + else + { + count += 1L; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Nop; + } + } + else if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Call) && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Brfalse) && method.Body.Instructions[i + 2].OpCode.Equals(OpCodes.Pop)) + { + if (method.Body.Instructions[i].Operand.ToString().Contains("System.Boolean")) + { + count += 1L; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Nop; + } + else + { + count += 1L; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Br_S; + } + } + } + } + catch { } + } + return count; + } + } +} diff --git a/NetReactorSlayer.Core/Protections/EmbeddedAsm.cs b/NetReactorSlayer.Core/Protections/EmbeddedAsm.cs new file mode 100644 index 0000000..c235510 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/EmbeddedAsm.cs @@ -0,0 +1,140 @@ +using de4dot.blocks; +using dnlib.DotNet; +using NetReactorSlayer.Core.Utils.de4dot; +using NETReactorSlayer.Core.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class EmbeddedAsm + { + static readonly string[] Locals1 = new string[] { "System.Byte[]", "System.Reflection.Assembly", "System.String", "System.IO.BinaryReader", "System.IO.Stream" }; + static readonly string[] Locals2 = new string[] { "System.Reflection.Assembly", "System.IO.BinaryReader", "System.IO.Stream" }; + static readonly string[] Locals3 = new string[] { "System.Reflection.Assembly", "System.Reflection.Assembly[]", "System.IO.Stream" }; + static MethodDef resolverMethod = null; + static MethodDef initialMethod = null; + static TypeDef resolverType = null; + public static void Execute() + { + long count = 0L; + FindRequirements(); + if (resolverMethod == null || initialMethod == null || resolverType == null) + { + Logger.Warn("Couldn't find any embedded assembly."); + return; + } + List Assemblies = new List(); + foreach (string prefix in DotNetUtils.GetCodeStrings(resolverMethod)) + { + Assemblies.AddRange(GetAssemblies(prefix)); + } + if (Assemblies.Count < 1) + { + Logger.Warn("Couldn't find any embedded assembly."); + return; + } + Remover.MethodsToPatch.Add(initialMethod); + foreach (var asm in Assemblies) + { + Remover.ResourceToRemove.Add(asm); + string name = GetAssemblyName(asm, false) + ".dll"; + try + { + File.WriteAllBytes(Context.FileDir + "\\" + name, asm.CreateReader().ToArray()); + count += 1L; + } + catch { } + } + Logger.Info((int)count + " Embedded assemblies dumped."); + } + + static void FindRequirements() + { + foreach (var type in (from x in Context.Module.GetTypes() where x.HasFields && !x.HasNestedTypes && !x.HasEvents && !x.HasProperties select x)) + { + foreach (var method in (from x in type.Methods.ToArray() where x.HasBody && x.Body.HasInstructions && x.IsStatic && DotNetUtils.IsMethod(x, "System.Void", "()") && x.DeclaringType != null select x)) + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + try + { + if (!method.Body.Instructions[i].Operand.ToString().Contains("add_AssemblyResolve")) continue; + if (!CheckFields(method.DeclaringType.Fields)) continue; + if (!FindResolverMethod(type, out MethodDef methodDef)) continue; + LocalTypes localTypes = new LocalTypes(methodDef); + if (!localTypes.All(Locals1) && !localTypes.All(Locals2) && !localTypes.All(Locals3)) continue; + resolverMethod = methodDef; + resolverType = type; + initialMethod = method; + return; + } + catch { } + } + } + } + } + + static bool FindResolverMethod(TypeDef type, out MethodDef method) + { + method = null; + foreach (var methodDef in type.Methods.ToArray()) + { + if (!DotNetUtils.IsMethod(methodDef, "System.Reflection.Assembly", "(System.Object,System.Object)") && !DotNetUtils.IsMethod(methodDef, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)")) continue; + method = methodDef; + return true; + } + return false; + } + + static bool CheckFields(IList fields) + { + if (fields.Count != 2 && fields.Count != 3) return false; + FieldTypes fieldTypes = new FieldTypes(fields); + if (fieldTypes.Count("System.Boolean") != 1) return false; + if (fields.Count == 3) + { + return (fieldTypes.Count("System.Collections.Hashtable") == 2 || fieldTypes.Count("System.Object") == 2); + } + return (fields.Count == 2 && (fieldTypes.Count("System.Collections.Hashtable") == 1 || fieldTypes.Count("System.Object") == 1)); + } + + static List GetAssemblies(string prefix) + { + List result = new List(); + if (string.IsNullOrEmpty(prefix)) return null; + else + { + foreach (Resource rsrc in Context.Module.Resources) + { + if (!(rsrc is EmbeddedResource resource)) return null; + if (StartsWith(resource.Name.String, prefix, StringComparison.Ordinal)) result.Add(resource); + } + } + return result; + } + + static string GetAssemblyName(EmbeddedResource resource, bool FullName) + { + try + { + using (ModuleDefMD module = ModuleDefMD.Load(resource.CreateReader().ToArray())) + { + if (FullName) return module.Assembly.FullName; + else return module.Assembly.Name; + } + } + catch + { + return null; + } + } + + static bool StartsWith(string left, string right, StringComparison stringComparison) + { + return left.Length > right.Length && left.Substring(0, right.Length).Equals(right, stringComparison); + } + } +} diff --git a/NetReactorSlayer.Core/Protections/HideCall.cs b/NetReactorSlayer.Core/Protections/HideCall.cs new file mode 100644 index 0000000..006b4de --- /dev/null +++ b/NetReactorSlayer.Core/Protections/HideCall.cs @@ -0,0 +1,320 @@ +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class HideCall + { + static List locals; + static readonly InstructionEmulator instrEmulator = new InstructionEmulator(); + static MethodDef method; + static Local emuLocal; + static List instructions; + static readonly List delegateCreatorMethods = new List(); + static EmbeddedResource encryptedResource; + static Dictionary dictionary; + + public static void Execute() + { + FindDelegateCreator(); + if (delegateCreatorMethods.Count < 1) + { + Logger.Warn("Couldn't find any hidden call."); + return; + } + encryptedResource = FindMethodsDecrypterResource(delegateCreatorMethods.First()); + method = delegateCreatorMethods.First(); + locals = new List(method.Body.Variables); + IList origInstrs = method.Body.Instructions; + if (!Find(method.Body.Instructions, out int startIndex, out int endIndex, out emuLocal)) + { + if (!FindStartEnd(origInstrs, out startIndex, out endIndex, out emuLocal)) + { + Logger.Warn("Couldn't find any hidden call."); + return; + } + } + int num = endIndex - startIndex + 1; + instructions = new List(num); + for (int i = 0; i < num; i++) instructions.Add(origInstrs[startIndex + i].Clone()); + GetDictionary(); + long count = 0L; + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods where x.HasBody && x.Body.HasInstructions select x).ToArray()) + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + IField field = null; + try + { + if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Ldsfld) && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Call)) + { + field = method.Body.Instructions[i].Operand as IField; + GetCallInfo(field, out IMethod iMethod, out OpCode opCpde); + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1] = Instruction.Create(opCpde, Context.Module.Import(iMethod)); + method.Body.UpdateInstructionOffsets(); + count += 1L; + } + } + catch + { + } + } + } + } + if (count > 0L) Logger.Info((int)count + " Hidden calls restored."); + else Logger.Warn("Couldn't find any hidden call."); + } + + static bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + if (!FindStart(instrs, out int emuStartIndex, out emuLocal)) return false; + if (!FindEnd(instrs, emuStartIndex, out int emuEndIndex)) return false; + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = emuLocal; + return true; + } + + static bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code == Code.Conv_R_Un) + { + if (instrs[i + 1].OpCode.Code == Code.Conv_R8) + { + if (instrs[i + 2].OpCode.Code == Code.Conv_U4) + { + if (instrs[i + 3].OpCode.Code == Code.Add) + { + int newEndIndex = i + 3; + int newStartIndex = -1; + for (int x = newEndIndex; x > 0; x--) + { + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + newStartIndex = x + 1; + break; + } + } + if (newStartIndex > 0) + { + List checkLocs = new List(); + int ckStartIndex = -1; + for (int y = newEndIndex; y >= newStartIndex; y--) + { + Local loc = CheckLocal(instrs[y], true); + if (loc != null) + { + if (!checkLocs.Contains(loc)) checkLocs.Add(loc); + if (checkLocs.Count == 3) break; + ckStartIndex = y; + } + } + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + } + } + } + i++; + } + endIndex = 0; startIndex = 0; tmpLocal = null; return false; + } + + static bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code == Code.Conv_U) + { + if (instrs[i + 1].OpCode.Code == Code.Ldelem_U1) + { + if (instrs[i + 2].OpCode.Code == Code.Or) + { + if (CheckLocal(instrs[i + 3], false) != null) + { + Local local; + if ((local = CheckLocal(instrs[i + 4], true)) != null) + { + if (CheckLocal(instrs[i + 5], true) != null) + { + if (instrs[i + 6].OpCode.Code == Code.Add) + { + if (CheckLocal(instrs[i + 7], false) == local) + { + Instruction instr = instrs[i + 8]; + int newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = (instr.Operand as Instruction); + newStartIndex = instrs.IndexOf(instr); + } + if (newStartIndex > 0 && instr != null) + { + if (CheckLocal(instr, true) == local) + { + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + } + } + } + } + } + } + } + } + } + i++; + } + startIndex = 0; + tmpLocal = null; + return false; + } + + static Local CheckLocal(Instruction instr, bool isLdloc) + { + if (isLdloc && !instr.IsLdloc()) return null; + if (!isLdloc && !instr.IsStloc()) return null; + return instr.GetLocal(locals); + } + + static bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (int i = startIndex; i < instrs.Count; i++) + { + Instruction instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) break; + if (instr.IsStloc() && instr.GetLocal(locals) == emuLocal) + { + endIndex = i - 1; + return true; + } + } + endIndex = 0; + return false; + } + + static EmbeddedResource FindMethodsDecrypterResource(MethodDef method) + { + foreach (string s in DotNetUtils.GetCodeStrings(method)) + { + if (DotNetUtils.GetResource(Context.Module, s) is EmbeddedResource resource) return resource; + } + return null; + } + + static void GetCallInfo(IField field, out IMethod calledMethod, out OpCode callOpcode) + { + callOpcode = OpCodes.Call; + dictionary.TryGetValue((int)field.MDToken.Raw, out int token); + if ((token & 1073741824) > 0) callOpcode = OpCodes.Callvirt; + token &= 1073741823; + calledMethod = (Context.Module.ResolveToken(token) as IMethod); + } + + static void GetDictionary() + { + byte[] resource = Decrypt(); + int length = resource.Length / 8; + dictionary = new Dictionary(); + BinaryReader reader = new BinaryReader(new MemoryStream(resource)); + for (int i = 0; i < length; i++) + { + int key = reader.ReadInt32(); + int value = reader.ReadInt32(); + if (!dictionary.ContainsKey(key)) dictionary.Add(key, value); + } + reader.Close(); + } + + static void FindDelegateCreator() + { + CallCounter callCounter = new CallCounter(); + foreach (TypeDef type in (from x in Context.Module.GetTypes() where x.Namespace.Equals("") && DotNetUtils.DerivesFromDelegate(x) select x)) + { + MethodDef cctor = type.FindStaticConstructor(); + if (cctor != null) + { + foreach (IMethod method in DotNetUtils.GetMethodCalls(cctor)) + { + if (method.MethodSig.GetParamCount() == 1 && method.GetParam(0).FullName == "System.RuntimeTypeHandle") + callCounter.Add(method); + } + } + } + IMethod mostCalls = callCounter.Most(); + if (mostCalls != null) delegateCreatorMethods.Add(DotNetUtils.GetMethod(Context.Module, mostCalls)); + } + + static byte[] Decrypt() + { + byte[] encrypted = encryptedResource.CreateReader().ToArray(); + byte[] decrypted = new byte[encrypted.Length]; + uint sum = 0U; + for (int i = 0; i < encrypted.Length; i += 4) + { + sum = CalculateMagic(sum); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + Remover.ResourceToRemove.Add(encryptedResource); + return decrypted; + } + + static uint ReadUInt32(byte[] ary, int index) + { + int sizeLeft = ary.Length - index; + if (sizeLeft >= 4) return BitConverter.ToUInt32(ary, index); + switch (sizeLeft) + { + case 1: + return (uint)ary[index]; + case 2: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8); + case 3: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8 | (int)ary[index + 2] << 16); + default: + throw new ApplicationException("Can't read data"); + } + } + static void WriteUInt32(byte[] ary, int index, uint value) + { + int num = ary.Length - index; + if (num >= 1) ary[index] = (byte)value; + if (num >= 2) ary[index + 1] = (byte)(value >> 8); + if (num >= 3) ary[index + 2] = (byte)(value >> 16); + if (num >= 4) ary[index + 3] = (byte)(value >> 24); + } + static uint CalculateMagic(uint input) + { + instrEmulator.Initialize(method, method.Parameters, locals, method.Body.InitLocals, false); + instrEmulator.SetLocal(emuLocal, new Int32Value((int)input)); + foreach (Instruction instr in instructions) + { + instrEmulator.Emulate(instr); + } + if (!(instrEmulator.Pop() is Int32Value tos) || !tos.AllBitsValid()) throw new Exception("Couldn't calculate magic value"); + return (uint)tos.Value; + } + } +} diff --git a/NetReactorSlayer.Core/Protections/Native.cs b/NetReactorSlayer.Core/Protections/Native.cs new file mode 100644 index 0000000..b4be668 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Native.cs @@ -0,0 +1,227 @@ +using dnlib.DotNet; +using dnlib.PE; +using ICSharpCode.SharpZipLib.Zip.Compression; +using NetReactorSlayer.Core.Utils.de4dot; +using System; + +namespace NETReactorSlayer.Core.Protections +{ + class Native + { + readonly MyPEImage peImage; + bool isNet1x; + const int loaderHeaderSizeV45 = 14; + + public Native(IPEImage peImage) + { + this.peImage = new MyPEImage(peImage); + } + public byte[] Unpack() + { + if (peImage.PEImage.Win32Resources == null) + return null; + var dataEntry = peImage.PEImage.Win32Resources.Find(10, "__", 0); + if (dataEntry == null) + return null; + + var encryptedData = dataEntry.CreateReader().ToArray(); + + var keyData = GetKeyData(); + if (keyData == null) + return null; + Decrypt(keyData, encryptedData, 0, encryptedData.Length); + + byte[] inflatedData; + if (isNet1x) + inflatedData = DeobUtils.Inflate(encryptedData, false); + else + { + int inflatedSize = BitConverter.ToInt32(encryptedData, 0); + inflatedData = new byte[inflatedSize]; + var inflater = new Inflater(false); + inflater.SetInput(encryptedData, 4, encryptedData.Length - 4); + int count = inflater.Inflate(inflatedData); + if (count != inflatedSize) + return null; + } + if (BitConverter.ToInt16(inflatedData, 0) == 0x5A4D) + return inflatedData; + if (BitConverter.ToInt16(inflatedData, loaderHeaderSizeV45) == 0x5A4D) + return UnpackLoader(inflatedData); + + return null; + } + + byte[] UnpackLoader(byte[] loaderData) + { + var loaderBytes = new byte[loaderData.Length - loaderHeaderSizeV45]; + Array.Copy(loaderData, loaderHeaderSizeV45, loaderBytes, 0, loaderBytes.Length); + try + { + using (var asmLoader = ModuleDefMD.Load(loaderBytes)) + { + if (asmLoader.Resources.Count == 0) + return null; + if (asmLoader.Resources[0] as EmbeddedResource == null) + return null; + + return (asmLoader.Resources[0] as EmbeddedResource).CreateReader().ToArray(); + } + } + catch + { + return null; + } + } + + readonly uint[] baseOffsets = new uint[] { + 0x1C00, + 0x1900, + 0x1B60, + 0x700, + }; + readonly short[] decryptMethodPattern = new short[] { + 0x83, 0xEC, 0x38, + 0x53, + 0xB0, -1, + 0x88, 0x44, 0x24, 0x2B, + 0x88, 0x44, 0x24, 0x2F, + 0xB0, -1, + 0x88, 0x44, 0x24, 0x30, + 0x88, 0x44, 0x24, 0x31, + 0x88, 0x44, 0x24, 0x33, + 0x55, + 0x56, + }; + readonly short[] startMethodNet1xPattern = new short[] { + 0x55, + 0x8B, 0xEC, + 0xB9, 0x14, 0x00, 0x00, 0x00, + 0x6A, 0x00, + 0x6A, 0x00, + 0x49, + 0x75, 0xF9, + 0x53, + 0x56, + 0x57, + 0xB8, -1, -1, -1, -1, + 0xE8, -1, -1, -1, -1, + }; + byte[] GetKeyData() + { + isNet1x = false; + for (int i = 0; i < baseOffsets.Length; i++) + { + var code = peImage.OffsetReadBytes(baseOffsets[i], decryptMethodPattern.Length); + if (DeobUtils.IsCode(decryptMethodPattern, code)) + return GetKeyData(baseOffsets[i]); + } + + var net1xCode = peImage.OffsetReadBytes(0x207E0, startMethodNet1xPattern.Length); + if (DeobUtils.IsCode(startMethodNet1xPattern, net1xCode)) + { + isNet1x = true; + return new byte[6] { 0x34, 0x38, 0x63, 0x65, 0x7A, 0x35 }; + } + + return null; + } + + byte[] GetKeyData(uint baseOffset) => + new byte[6] { + peImage.OffsetReadByte(baseOffset + 5), + peImage.OffsetReadByte(baseOffset + 0xF), + peImage.OffsetReadByte(baseOffset + 0x58), + peImage.OffsetReadByte(baseOffset + 0x6D), + peImage.OffsetReadByte(baseOffset + 0x98), + peImage.OffsetReadByte(baseOffset + 0xA6), + }; + void Decrypt(byte[] keyData, byte[] data, int offset, int count) + { + byte[,] transform = new byte[256, 256]; + byte kb = 0; + byte[] key; + var keyInit = new byte[] { + 0x78, 0x61, 0x32, keyData[0], keyData[2], + 0x62, keyData[3], keyData[0], keyData[1], keyData[1], + 0x66, keyData[1], keyData[5], 0x33, keyData[2], + keyData[4], 0x74, 0x32, keyData[3], keyData[2], + }; + key = new byte[32]; + for (int i = 0; i < 32; i++) + { + key[i] = (byte)(i + keyInit[i % keyInit.Length] * keyInit[((i + 0x0B) | 0x1F) % keyInit.Length]); + kb += key[i]; + } + + var transformTemp = new ushort[256, 256]; + for (int i = 0; i < 256; i++) + for (int j = 0; j < 256; j++) + transformTemp[i, j] = 0x400; + int counter = 0x0B; + byte newByte = 0; + int ki = 0; + for (int i = 0; i < 256; i++) + { + while (true) + { + for (int j = key.Length - 1; j >= ki; j--) + newByte += (byte)(key[j] + counter); + bool done = true; + ki = (ki + 1) % key.Length; + for (int k = 0; k <= i; k++) + { + if (newByte == transformTemp[k, 0]) + { + done = false; + break; + } + } + if (done) + break; + counter++; + } + transformTemp[i, 0] = newByte; + } + + counter = ki = 0; + for (int i = 1; i < 256; i++) + { + ki++; + int i1; + do + { + counter++; + i1 = 1 + (key[(i + 37 + counter) % key.Length] + counter + kb) % 255; + } while (transformTemp[0, i1] != 0x400); + for (int i0 = 0; i0 < 256; i0++) + transformTemp[i0, i1] = transformTemp[(i0 + ki) % 256, 0]; + } + + for (int i = 0; i < 256; i++) + { + for (int j = 0; j < 256; j++) + transform[(byte)transformTemp[i, j], j] = (byte)i; + } + + for (int i = 0; i < count; i += 1024, offset += 1024) + { + int blockLen = Math.Min(1024, count - i); + + if (blockLen == 1) + { + data[offset] = transform[data[offset], kb]; + continue; + } + + for (int j = 0; j < blockLen - 1; j++) + data[offset + j] = transform[data[offset + j], data[offset + j + 1]]; + data[offset + blockLen - 1] = transform[data[offset + blockLen - 1], kb ^ 0x55]; + + for (int j = blockLen - 1; j > 0; j--) + data[offset + j] = transform[data[offset + j], data[offset + j - 1]]; + data[offset] = transform[data[offset], kb]; + } + } + } +} diff --git a/NetReactorSlayer.Core/Protections/NecroBit.cs b/NetReactorSlayer.Core/Protections/NecroBit.cs new file mode 100644 index 0000000..9991df1 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/NecroBit.cs @@ -0,0 +1,565 @@ +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.IO; +using NetReactorSlayer.Core.Utils.de4dot; +using NETReactorSlayer.Core.Utils; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using static NETReactorSlayer.Core.Protections.Resources; + +namespace NETReactorSlayer.Core.Protections +{ + class NecroBit + { + static readonly List methodDefs = new List(); + static EmbeddedResource encryptedResource = null; + static byte[] methodsData = null; + enum CompileMethodType { Unknown, V1, V2 } + static readonly short[] nativeLdci4 = new short[] { 85, 139, 236, 184, -1, -1, -1, -1, 93, 195 }; + static readonly short[] nativeLdci4_0 = new short[] { 85, 139, 236, 51, 192, 93, 195 }; + [Flags] public enum SimpleDeobfuscatorFlags : uint { Force = 1U, DisableConstantsFolderExtraInstrs = 2U } + [Flags] enum SimpleDeobFlags { HasDeobfuscated = 1 } + static readonly Dictionary simpleDeobfuscatorFlags = new Dictionary(); + public static void Execute() + { + DumpedMethods dumpedMethods = new DumpedMethods(); + Dictionary tokenToNativeMethod = new Dictionary(); + Dictionary tokenToNativeCode = new Dictionary(); + MethodDef cctor = Context.Module.EntryPoint.DeclaringType.FindMethods(".cctor").FirstOrDefault(); + if (cctor != null && cctor.HasBody && cctor.Body.HasInstructions) + { + for (int i = 0; i < cctor.Body.Instructions.Count; i++) + { + if (cctor.Body.Instructions[i].OpCode.Equals(OpCodes.Call)) + { + if (cctor.Body.Instructions[i].Operand is MethodDef methodDef && methodDef.DeclaringType != null && methodDef.HasBody && methodDef.Body.HasInstructions) + methodDefs.Add(cctor.Body.Instructions[i].Operand as MethodDef); + } + } + } + foreach (var method in (from x in methodDefs where x.HasBody && x.Body.HasInstructions && x.IsStatic && x.IsAssembly select x)) + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Ldstr) && method.Body.Instructions[i].Operand.ToString().Equals("clrjit.dll")) + { + for (int x = 0; x < method.Body.Instructions.Count; x++) + { + if (method.Body.Instructions[x].OpCode.Equals(OpCodes.Ldsfld) && method.Body.Instructions[x + 1].OpCode.Equals(OpCodes.Ldstr)) + { + foreach (var name in Context.Assembly.GetManifestResourceNames()) + { + if (method.Body.Instructions[x + 1].Operand.ToString() != name) continue; + if ((encryptedResource = (DotNetUtils.GetResource(Context.Module, method.Body.Instructions[x + 1].Operand.ToString()) as EmbeddedResource)) != null) + { + Remover.ResourceToRemove.Add(encryptedResource); + Remover.MethodsToPatch.Add(method); + methodDefs.Clear(); + Deobfuscate(method); + methodDefs.Add(method); + DnrDecrypterType decrypterType = GetDecrypterType(methodDefs[0], new string[0]); + byte[] key = GetBytes(methodDefs[0], 32); + if (decrypterType == DnrDecrypterType.V3) + { + V3 V3 = new V3(methodDefs[0]); + methodsData = V3.Decrypt(encryptedResource); + return; + } + byte[] iv = GetBytes(methodDefs[0], 16); + if (IsNeedReverse(methodDefs[0])) + Array.Reverse(iv); + if (UsesPublicKeyToken(methodDefs[0])) + { + PublicKeyToken publicKeyToken = Context.Module.Assembly.PublicKeyToken; + if (publicKeyToken != null && publicKeyToken.Data.Length != 0) + { + for (int z = 0; z < 8; z++) + { + iv[z * 2 + 1] = publicKeyToken.Data[z]; + } + } + } + if (decrypterType == DnrDecrypterType.V1) + { + V1 V1 = new V1(iv, key); + methodsData = V1.Decrypt(encryptedResource); + return; + } + else if (decrypterType == DnrDecrypterType.V2) + { + V2 V2 = new V2(iv, key, methodDefs[0]); + methodsData = V2.Decrypt(encryptedResource); + goto Continue; + } + else + { + Logger.Warn("Couldn't find assembly resource decrypter method."); + return; + } + } + } + } + } + } + } + } + Logger.Warn("Couldn't find any encrypted method."); + return; + Continue: + if (encryptedResource == null) + { + Logger.Warn("Couldn't find any encrypted method."); + return; + } + if (methodsData == null) + { + Logger.Error("Failed to decrypt methods."); + return; + } + bool isFindDnrMethod = false; + int mode = -1; + try + { + XorEncrypt(methodsData, GetXorKey(methodDefs[0])); + isFindDnrMethod = FindDnrCompileMethod(methodDefs[0].DeclaringType) != null; + DataReader methodsDataReader = ByteArrayDataReaderFactory.CreateReader(methodsData); + int tmp = methodsDataReader.ReadInt32(); + if (((long)tmp & unchecked((long)((ulong)-16777216))) == 100663296L) + { + methodsDataReader.ReadInt32(); + } + else + { + methodsDataReader.Position -= 4U; + } + int patchCount = methodsDataReader.ReadInt32(); + mode = methodsDataReader.ReadInt32(); + tmp = methodsDataReader.ReadInt32(); + methodsDataReader.Position -= 4U; + if (((long)tmp & unchecked((long)((ulong)-16777216))) == 100663296L) + { + methodsDataReader.Position += (uint)(8 * patchCount); + patchCount = methodsDataReader.ReadInt32(); + mode = methodsDataReader.ReadInt32(); + PatchDwords(Context.PEImage, ref methodsDataReader, patchCount); + while ((ulong)methodsDataReader.Position < (ulong)((long)(methodsData.Length - 1))) + { + methodsDataReader.ReadUInt32(); + int numDwords = methodsDataReader.ReadInt32(); + PatchDwords(Context.PEImage, ref methodsDataReader, numDwords / 2); + } + } + else + { + if (!isFindDnrMethod || mode == 1) + { + PatchDwords(Context.PEImage, ref methodsDataReader, patchCount); + bool isNewer45Decryption = IsNewer45Decryption(methodDefs[0]); + bool isUsingOffset = !IsUsingRva(methodDefs[0]); + while ((ulong)methodsDataReader.Position < (ulong)((long)(methodsData.Length - 1))) + { + uint rva = (uint)methodsDataReader.ReadInt32(); + int size; + if (!isNewer45Decryption) + { + methodsDataReader.ReadInt32(); + size = methodsDataReader.ReadInt32(); + } + else + { + size = methodsDataReader.ReadInt32() * 4; + } + byte[] newData = methodsDataReader.ReadBytes(size); + if (Context.IsNative && isUsingOffset) + { + Context.PEImage.DotNetSafeWriteOffset(rva, newData); + } + else + { + Context.PEImage.DotNetSafeWrite(rva, newData); + } + } + } + else + { + MDTable methodDef = Context.PEImage.Metadata.TablesStream.MethodTable; + Dictionary rvaToIndex = new Dictionary((int)methodDef.Rows); + uint offset = (uint)methodDef.StartOffset; + int i = 0; + while ((long)i < (long)((ulong)methodDef.Rows)) + { + uint rva2 = Context.PEImage.OffsetReadUInt32(offset); + offset += methodDef.RowSize; + if (rva2 != 0U) + { + if ((Context.PEImage.ReadByte(rva2) & 3) == 2) + { + rva2 += 1U; + } + else + { + rva2 += (uint)(4 * (Context.PEImage.ReadByte(rva2 + 1U) >> 4)); + } + rvaToIndex[rva2] = i; + } + i++; + } + PatchDwords(Context.PEImage, ref methodsDataReader, patchCount); + methodsDataReader.ReadInt32(); + dumpedMethods = new DumpedMethods(); + while ((ulong)methodsDataReader.Position < (ulong)((long)(methodsData.Length - 1))) + { + uint rva3 = methodsDataReader.ReadUInt32(); + uint index = methodsDataReader.ReadUInt32(); + bool isNativeCode = index >= 1879048192U; + int size2 = methodsDataReader.ReadInt32(); + byte[] methodData = methodsDataReader.ReadBytes(size2); + if (!rvaToIndex.TryGetValue(rva3, out int methodIndex)) + { + Logger.Warn("Couldn't find method with RVA: " + rva3); + } + else + { + uint methodToken = (uint)(100663297 + methodIndex); + if (isNativeCode) + { + if (tokenToNativeCode != null) + { + tokenToNativeCode[methodToken] = methodData; + } + if (DeobUtils.IsCode(nativeLdci4, methodData)) + { + uint val = BitConverter.ToUInt32(methodData, 4); + methodData = new byte[] + { + 32, + 0, + 0, + 0, + 0, + 42 + }; + methodData[1] = (byte)val; + methodData[2] = (byte)(val >> 8); + methodData[3] = (byte)(val >> 16); + methodData[4] = (byte)(val >> 24); + } + else + { + if (DeobUtils.IsCode(nativeLdci4_0, methodData)) + { + methodData = new byte[] + { + 22, + 42 + }; + } + else + { + tokenToNativeMethod[methodToken] = methodData; + methodData = new byte[] + { + 32, + 222, + 192, + 173, + 222, + 109, + 122 + }; + } + } + } + DumpedMethod dumpedMethod = new DumpedMethod(); + Context.PEImage.ReadMethodTableRowTo(dumpedMethod, MDToken.ToRID(methodToken)); + dumpedMethod.code = methodData; + DataReader codeReader = Context.PEImage.Reader; + codeReader.Position = Context.PEImage.RvaToOffset(dumpedMethod.mdRVA); + MethodBodyHeader mbHeader = MethodBodyParser.ParseMethodBody(ref codeReader, out byte[] code, out dumpedMethod.extraSections); + Context.PEImage.UpdateMethodHeaderInfo(dumpedMethod, mbHeader); + dumpedMethods.Add(dumpedMethod); + } + } + } + } + } + catch (Exception ex) + { + Logger.Error("Failed to decrypt methods. " + ex.Message); + } + + using (Context.Module) + { + if (!isFindDnrMethod || mode == 1) + Context.Module = Context.AssemblyModule.Reload(Context.PEImage.peImageData, CreateDumpedMethodsRestorer(dumpedMethods), null); + else if (dumpedMethods.Count > 0) + Context.Module = Context.AssemblyModule.Reload(DeobUtils.ReadModule(Context.Module), CreateDumpedMethodsRestorer(dumpedMethods), null); + else + { + Logger.Error("Failed to decrypt methods."); + return; + } + Logger.Info("Methods decrypted."); + } + } + static MethodDef FindDnrCompileMethod(TypeDef type) + { + foreach (MethodDef method in type.Methods) + { + if (!method.IsStatic || method.Body == null) continue; + MethodSig sig = method.MethodSig; + if (sig != null || sig.Params.Count == 6) + { + if (GetCompileMethodType(method) != CompileMethodType.Unknown) + { + return method; + } + } + } + return null; + } + static CompileMethodType GetCompileMethodType(MethodDef method) + { + if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.UInt64&,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr&,System.UInt32&)")) + { + return CompileMethodType.V1; + } + else + { + if (DotNetUtils.IsMethod(method, "System.UInt32", "(System.IntPtr,System.IntPtr,System.IntPtr,System.UInt32,System.IntPtr,System.UInt32&)")) + { + return CompileMethodType.V2; + } + else + { + return CompileMethodType.Unknown; + } + } + } + static DumpedMethodsRestorer CreateDumpedMethodsRestorer(DumpedMethods dumpedMethods) + { + if (dumpedMethods == null || dumpedMethods.Count == 0) return null; + return new DumpedMethodsRestorer(dumpedMethods); + } + static void PatchDwords(MyPEImage peImage, ref DataReader reader, int count) + { + for (int i = 0; i < count; i++) + { + uint rva = reader.ReadUInt32(); + uint data = reader.ReadUInt32(); + peImage.DotNetSafeWrite(rva, BitConverter.GetBytes(data)); + } + } + static bool IsUsingRva(MethodDef method) + { + if (method == null || method.Body == null) return false; + IList instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) + { + if (instrs[i].OpCode.Equals(OpCodes.Ldloca_S)) + { + if (instrs[i + 1].OpCode.Equals(OpCodes.Ldsfld)) + { + if (instrs[i + 2].OpCode.Equals(OpCodes.Ldloc_S)) + { + if (instrs[i + 3].OpCode.Equals(OpCodes.Conv_I8)) + { + if (instrs[i + 4].OpCode.Equals(OpCodes.Add)) + { + if (instrs[i + 5].OpCode.Equals(OpCodes.Ldloc_S)) + { + if (instrs[i + 6].OpCode.Equals(OpCodes.Conv_I8)) + { + if (instrs[i + 7].OpCode.Equals(OpCodes.Sub)) + { + if (instrs[i + 8].OpCode.Code.Equals(Code.Call)) + { + return true; + } + } + } + } + } + } + } + } + } + } + return false; + } + static bool IsNewer45Decryption(MethodDef method) + { + if (method == null || method.Body == null) return false; + for (int i = 0; i < method.Body.Instructions.Count - 4; i++) + { + if (method.Body.Instructions[i].IsLdcI4()) + { + if (method.Body.Instructions[i + 1].OpCode.Code.Equals(Code.Mul)) + { + if (method.Body.Instructions[i + 2].IsLdcI4()) + { + if (method.Body.Instructions[i + 3].OpCode.Code.Equals(Code.Ldloca_S) || method.Body.Instructions[i + 3].OpCode.Code.Equals(Code.Ldloca)) + { + if (method.Body.Instructions[i + 4].OpCode.Code.Equals(Code.Call)) + { + return true; + } + } + } + } + } + } + return false; + } + static long GetXorKey(MethodDef method) + { + for (int i = 0; i < method.Body.Instructions.Count - 1; i++) + { + if (method.Body.Instructions[i].OpCode.Code.Equals(Code.Ldind_I8)) + { + Instruction ldci4 = method.Body.Instructions[i + 1]; + long result; + if (ldci4.IsLdcI4()) + { + result = (long)ldci4.GetLdcI4Value(); + } + else + { + if (!ldci4.OpCode.Code.Equals(Code.Ldc_I8)) + { + goto Continue; + } + result = (long)ldci4.Operand; + } + return result; + } + Continue:; + } + return 0L; + } + + static void XorEncrypt(byte[] data, long xorKey) + { + if (xorKey != 0L) + { + MemoryStream stream = new MemoryStream(data); + BinaryReader reader = new BinaryReader(stream); + BinaryWriter writer = new BinaryWriter(stream); + int count = data.Length / 8; + for (int i = 0; i < count; i++) + { + long val = reader.ReadInt64(); + val ^= xorKey; + stream.Position -= 8L; + writer.Write(val); + } + } + } + static void Deobfuscate(MethodDef method) + { + List list = new List { new MethodCallInliner(false) }; + SimpleDeobfuscatorFlags flags = 0; + if (!(method == null || (!((flags & SimpleDeobfuscatorFlags.Force) > 0U) && Check(method, SimpleDeobFlags.HasDeobfuscated)))) + { + Deobfuscate(method, delegate (Blocks blocks) + { + bool disableNewCFCode = (flags & SimpleDeobfuscatorFlags.DisableConstantsFolderExtraInstrs) > (SimpleDeobfuscatorFlags)0U; + BlocksCflowDeobfuscator cflowDeobfuscator = new BlocksCflowDeobfuscator(list, disableNewCFCode); + cflowDeobfuscator.Initialize(blocks); + cflowDeobfuscator.Deobfuscate(); + }); + } + } + static bool Check(MethodDef method, SimpleDeobFlags flags) + { + if (method == null) return false; + simpleDeobfuscatorFlags.TryGetValue(method, out SimpleDeobFlags oldFlags); + simpleDeobfuscatorFlags[method] = (oldFlags | flags); + return ((oldFlags & flags) == flags); + } + + static void Deobfuscate(MethodDef method, Action handler) + { + if (method == null || !method.HasBody || !method.Body.HasInstructions) return; + try + { + if (ControlFlow.ContainsSwitch(method)) + { + Arithmetic(method); + } + Blocks blocks = new Blocks(method); + handler(blocks); + blocks.GetCode(out IList allInstructions, out IList allExceptionHandlers); + DotNetUtils.RestoreBody(method, allInstructions, allExceptionHandlers); + } + catch + { + Logger.Warn("Couldn't deobfuscate " + method.FullName); + } + } + static void Arithmetic(MethodDef method) + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + if (method.Body.Instructions[i].IsBrtrue() && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Pop) && method.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Call)) + { + if (method.Body.Instructions[i - 1].Operand is MethodDef methodDef) + { + IList methodDefInstr = methodDef.Body.Instructions; + if (methodDef.ReturnType.FullName == "System.Boolean") + { + if (methodDefInstr[methodDefInstr.Count - 2].OpCode.Equals(OpCodes.Ldc_I4_0)) + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + } + else + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Br_S; + } + } + else + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + } + } + } + else + { + if (method.Body.Instructions[i].IsBrfalse() && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Pop) && method.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Call)) + { + if (method.Body.Instructions[i - 1].Operand is MethodDef methodDef2) + { + IList methodDefInstr2 = methodDef2.Body.Instructions; + if (methodDef2.ReturnType.FullName == "System.Boolean") + { + if (methodDefInstr2[methodDefInstr2.Count - 2].OpCode.Equals(OpCodes.Ldc_I4_0)) + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Br_S; + } + else + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Nop; + } + } + else + { + method.Body.Instructions[i - 1].OpCode = OpCodes.Nop; + method.Body.Instructions[i].OpCode = OpCodes.Br_S; + } + } + } + } + } + } + } +} diff --git a/NetReactorSlayer.Core/Protections/ProxyCall.cs b/NetReactorSlayer.Core/Protections/ProxyCall.cs new file mode 100644 index 0000000..70340ff --- /dev/null +++ b/NetReactorSlayer.Core/Protections/ProxyCall.cs @@ -0,0 +1,111 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System.Collections.Generic; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + class ProxyCall + { + public static void Execute() + { + long count = 0L; + HashSet proxies = new HashSet(); + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods.ToArray() where x.HasBody && x.Body.HasInstructions select x)) + { + try + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + MethodDef Method; + if (method.Body.Instructions[i].OpCode.Equals(OpCodes.Call) && (Method = (method.Body.Instructions[i].Operand as MethodDef)) != null && (IsProxy(Method, out OpCode opCode, out object obj, out int num3) || IsProxy2(Method, out opCode, out obj, out num3))) + { + count += 1L; + if (Method.DeclaringType == method.DeclaringType) + { + method.Body.Instructions[i].OpCode = opCode; + method.Body.Instructions[i].Operand = obj; + proxies.Add(Method); + + } + } + } + } + catch { } + } + } + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods.ToArray() where x.HasBody && x.Body.HasInstructions select x)) + { + foreach (Instruction instruction in method.Body.Instructions) + { + try + { + MethodDef item; + if (instruction.OpCode.OperandType == OperandType.InlineMethod && (item = (instruction.Operand as MethodDef)) != null && proxies.Contains(item)) + { + proxies.Remove(item); + } + } + catch { } + } + } + } + foreach (MethodDef Method in proxies) Method.DeclaringType.Remove(Method); + if (count > 0L) Logger.Info((int)count + " Proxied calls removed."); + else Logger.Warn("Couldn't find any proxied call."); + } + static bool IsProxy(MethodDef method, out OpCode code, out object operand, out int len) + { + code = null; + operand = null; + len = 0; + if (!method.HasBody || !method.IsStatic) return false; + IList instructions = method.Body.Instructions; + int num = instructions.Count - 1; + if (num < 1 || instructions[num].OpCode != OpCodes.Ret) return false; + Code code2 = instructions[num - 1].OpCode.Code; + if (code2 != Code.Call && code2 != Code.Callvirt && code2 != Code.Newobj) return false; + code = instructions[num - 1].OpCode; + operand = instructions[num - 1].Operand; + len = (from i in instructions + where i.OpCode != OpCodes.Nop + select i).Count() - 2; + if (len != method.Parameters.Count) return false; + int num2 = 0; + for (int j = 0; j < instructions.Count - 2; j++) + { + if (instructions[j].OpCode != OpCodes.Nop) + { + if (!instructions[j].IsLdarg()) return false; + if (instructions[j].GetParameterIndex() != num2) return false; + num2++; + } + } + return len == num2; + } + + static bool IsProxy2(MethodDef method, out OpCode code, out object operand, out int len) + { + code = null; + operand = null; + len = 0; + if (!method.HasBody || !method.IsInternalCall) return false; + IList instructions = method.Body.Instructions; + int num = instructions.Count - 1; + if (num < 1 || instructions[num].OpCode != OpCodes.Ret) return false; + Code code2 = instructions[num - 1].OpCode.Code; + if (code2 != Code.Ldfld) return false; + code = instructions[num - 1].OpCode; + operand = instructions[num - 1].Operand; + len = (from i in instructions + where i.OpCode != OpCodes.Nop + select i).Count() - 2; + return len == 1 && len == method.Parameters.Count - 1; + } + } +} diff --git a/NetReactorSlayer.Core/Protections/Remover.cs b/NetReactorSlayer.Core/Protections/Remover.cs new file mode 100644 index 0000000..27be0d8 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Remover.cs @@ -0,0 +1,103 @@ +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NETReactorSlayer.Core.Utils; +using System.Collections.Generic; +using System.Linq; + +namespace NETReactorSlayer.Core.Protections +{ + public class Remover + { + public static HashSet MethodsToPatch = new HashSet(); + public static HashSet ResourceToRemove = new HashSet(); + public static void Execute() + { + #region Patch Methods + Instruction Ldnull = Instruction.Create(OpCodes.Ldnull); + Instruction Ret = Instruction.Create(OpCodes.Ret); + CilBody cliBody; + foreach (var method in MethodsToPatch) + { + try + { + MethodDef methodDef = Context.Module.Find(method.DeclaringType.FullName, false).FindMethod(method.Name); + if (!methodDef.HasReturnType) + { + cliBody = new CilBody(); + cliBody.Instructions.Add(Ret); + methodDef.Body = cliBody; + } + else + { + cliBody = new CilBody(); + cliBody.Instructions.Add(Ldnull); + cliBody.Instructions.Add(Ret); + methodDef.Body = cliBody; + } + if (methodDef.DeclaringType.FindStaticConstructor() != null) + { + cliBody = new CilBody(); + cliBody.Instructions.Add(Ret); + methodDef.DeclaringType.FindStaticConstructor().Body = cliBody; + } + } + catch { } + } + #endregion + #region Remove NoInline Attributes + foreach (var type in Context.Module.GetTypes()) + { + foreach (var method in type.Methods.ToArray()) + { + method.IsNoInlining = false; + for (int i = 0; i < method.CustomAttributes.Count; i++) + { + try + { + var cattr = method.CustomAttributes[i]; + if (cattr.TypeFullName != "System.Runtime.CompilerServices.MethodImplAttribute") + continue; + int options = 0; + if (!GetMethodImplOptions(cattr, ref options)) + continue; + if (options != 0 && options != (int)MethodImplAttributes.NoInlining) + continue; + method.CustomAttributes.RemoveAt(i); + i--; + } + catch { } + } + } + } + #endregion + #region Remove Unused Resources + foreach (var rrsource in ResourceToRemove) + { + Context.Module.Resources.Remove(Context.Module.Resources.Find(rrsource.Name)); + } + #endregion + } + static bool GetMethodImplOptions(CustomAttribute cA, ref int value) + { + if (cA.IsRawBlob) + return false; + if (cA.ConstructorArguments.Count != 1) + return false; + if (cA.ConstructorArguments[0].Type.ElementType != ElementType.I2 && + cA.ConstructorArguments[0].Type.FullName != "System.Runtime.CompilerServices.MethodImplOptions") + return false; + var arg = cA.ConstructorArguments[0].Value; + if (arg is short @int) + { + value = @int; + return true; + } + if (arg is int int1) + { + value = int1; + return true; + } + return false; + } + } +} diff --git a/NetReactorSlayer.Core/Protections/Resources.cs b/NetReactorSlayer.Core/Protections/Resources.cs new file mode 100644 index 0000000..e5dad95 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Resources.cs @@ -0,0 +1,796 @@ +using de4dot.blocks; +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using NetReactorSlayer.Core.Utils.de4dot; +using NETReactorSlayer.Core.Utils; +using System; +using System.Collections.Generic; + +namespace NETReactorSlayer.Core.Protections +{ + class Resources + { + static readonly int[] pktIndexes = new int[] + { + 1, + 0, + 3, + 1, + 5, + 2, + 7, + 3, + 9, + 4, + 11, + 5, + 13, + 6, + 15, + 7 + }; + internal enum DnrDecrypterType + { + Unknown, + V1, + V2, + V3 + } + public static void Execute() + { + byte[] decryptedBytes = null; + EmbeddedResource encryptedResource = null; + HashSet methodsToPatch = new HashSet(); + foreach (TypeDef type in Context.Module.GetTypes()) + { + MethodDef method1 = type.FindMethod(".ctor"); + if (method1 != null && method1.HasBody && method1.Body.HasInstructions) + { + for (int i = 0; i < method1.Body.Instructions.Count; i++) + { + if (method1.Body.Instructions[i].OpCode.Equals(OpCodes.Newobj) && method1.Body.Instructions[i].Operand.ToString().Contains("System.ResolveEventHandler") && method1.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Ldftn)) + { + if (method1.Body.Instructions[i - 1].Operand is MethodDef method2 && method2.DeclaringType.Equals(type) && method2.HasReturnType && method2.ReturnType.FullName.Equals("System.Reflection.Assembly")) + { + foreach (Instruction instruction in method2.Body.Instructions) + { + try + { + if (instruction.OpCode.Equals(OpCodes.Call)) + { + if (instruction.Operand is MethodDef decryptorMethod && decryptorMethod.DeclaringType.Equals(type) && !decryptorMethod.HasReturnType) + { + foreach (string s in DotNetUtils.GetCodeStrings(decryptorMethod)) + { + if ((encryptedResource = (DotNetUtils.GetResource(Context.Module, s) as EmbeddedResource)) != null) + { + methodsToPatch.Add(decryptorMethod); + methodsToPatch.Add(method1); + methodsToPatch.Add(method2); + DnrDecrypterType decrypterType = GetDecrypterType(decryptorMethod, new string[0]); + byte[] key = ArrayFinder.GetInitializedByteArray(decryptorMethod, 32); + if (decrypterType == DnrDecrypterType.V3) + { + V3 V3 = new V3(decryptorMethod); + decryptedBytes = V3.Decrypt(encryptedResource); + return; + } + byte[] iv = ArrayFinder.GetInitializedByteArray(decryptorMethod, 16); + if (DotNetUtils.CallsMethod(decryptorMethod, "System.Array::Reverse")) + Array.Reverse(iv); + if (UsesPublicKeyToken(decryptorMethod)) + { + PublicKeyToken publicKeyToken = Context.Module.Assembly.PublicKeyToken; + if (publicKeyToken != null && publicKeyToken.Data.Length != 0) + { + for (int z = 0; z < 8; z++) + { + iv[z * 2 + 1] = publicKeyToken.Data[z]; + } + } + } + if (decrypterType == DnrDecrypterType.V1) + { + V1 V1 = new V1(iv, key); + decryptedBytes = V1.Decrypt(encryptedResource); + return; + } + else if (decrypterType == DnrDecrypterType.V2) + { + V2 V2 = new V2(iv, key, decryptorMethod); + decryptedBytes = V2.Decrypt(encryptedResource); + goto Decompress; + } + else + { + Logger.Warn("Couldn't find resource decrypter method."); + return; + } + } + + } + } + } + } + catch { } + } + } + } + } + } + } + Logger.Warn("Couldn't find any encrypted resource."); + return; + Decompress: + if (encryptedResource == null) + { + Logger.Warn("Couldn't find any encrypted resource."); + return; + } + try + { + DeobUtils.DecryptAndAddResources(Context.Module, delegate + { + byte[] result; + try + { + result = QuickLZ.Decompress(decryptedBytes); + } + catch + { + try + { + result = DeobUtils.Inflate(decryptedBytes, true); + } + catch + { + result = null; + } + } + return result; + }); + foreach (var m in methodsToPatch) + { + Remover.MethodsToPatch.Add(m); + } + Remover.ResourceToRemove.Add(encryptedResource); + Logger.Info("Assembly resources decrypted"); + } + catch (Exception ex) + { + Logger.Error("Failed to decrypt resources. " + ex.Message); + } + } + public class V1 + { + readonly byte[] key, iv; + public V1(byte[] IV, byte[] KEY) + { + iv = IV; + key = KEY; + } + public static bool CouldBeResourceDecrypter(MethodDef method, LocalTypes localTypes, IList additionalTypes) + { + List requiredTypes = new List + { + "System.Byte[]", + "System.Security.Cryptography.CryptoStream", + "System.Security.Cryptography.ICryptoTransform", + "System.String", + "System.Boolean" + }; + List requiredTypes2 = new List + { + "System.Security.Cryptography.ICryptoTransform", + "System.IO.Stream", + "System.Int32", + "System.Byte[]", + "System.Boolean" + }; + requiredTypes.AddRange(additionalTypes); + return (localTypes.All(requiredTypes) || localTypes.All(requiredTypes2)) && (DotNetUtils.GetMethod(method.DeclaringType, "System.Security.Cryptography.SymmetricAlgorithm", "()") == null || (!localTypes.Exists("System.UInt64") && (!localTypes.Exists("System.UInt32") || localTypes.Exists("System.Reflection.Assembly")))); + } + public byte[] Decrypt(EmbeddedResource resource) + { + return DeobUtils.AesDecrypt(resource.CreateReader().ToArray(), key, iv); + } + } + public static byte[] GetBytes(MethodDef method, int size) + { + byte[] result = new byte[size]; + Local local = null; + if (method == null || !method.HasBody || !method.Body.HasInstructions) return null; + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + if (local == null && method.Body.Instructions[i].OpCode.Equals(OpCodes.Newarr) && method.Body.Instructions[i].Operand.ToString().Equals("System.Byte") && method.Body.Instructions[i - 1].OpCode.Equals(OpCodes.Ldc_I4) && method.Body.Instructions[i - 1].GetLdcI4Value() == size && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Stloc)) + { + local = method.Body.Instructions[i + 1].Operand as Local; + i = 0; + } + else if (method.Body.Instructions[i].IsLdloc() && + (method.Body.Instructions[i].Operand as Local) == local && + method.Body.Instructions[i + 1].IsLdcI4() && + method.Body.Instructions[i + 2].IsLdcI4() && + method.Body.Instructions[i + 3].OpCode.Equals(OpCodes.Stelem_I1)) + { + try + { + result[method.Body.Instructions[i + 1].GetLdcI4Value()] = (byte)(int)(method.Body.Instructions[i + 2].GetLdcI4Value()); + } + catch { return result; } + } + } + return result; + } + public static bool IsNeedReverse(MethodDef method) + { + if (method != null && method.HasBody && method.Body.HasInstructions) + { + foreach (var instr in method.Body.Instructions) + { + try + { + IMethod calledMethod = instr.Operand as IMethod; + if (calledMethod.FullName.Contains("System.Array::Reverse")) + return true; + } + catch { } + } + } + return false; + } + public class V2 + { + bool isNewDecrypter; + readonly InstructionEmulator instrEmulator = new InstructionEmulator(); + Parameter emuArg; + readonly byte[] key, iv; + readonly MethodDef method; + List locals; + Local emuLocal; + MethodDef emuMethod; + List instructions; + public V2(byte[] IV, byte[] KEY, MethodDef Method) + { + iv = IV; + key = KEY; + method = Method; + locals = new List(method.Body.Variables); + if (!Initialize()) + { + throw new ApplicationException("Could not initialize decrypter"); + } + } + bool Initialize() + { + IList origInstrs = method.Body.Instructions; + if (!Find(origInstrs, out int emuStartIndex, out int emuEndIndex, out emuLocal) && !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out emuLocal)) + { + if (!FindStartEnd2(ref origInstrs, out emuStartIndex, out emuEndIndex, out emuLocal, out emuArg, ref emuMethod, ref locals)) + { + return false; + } + isNewDecrypter = true; + } + if (!isNewDecrypter) + { + for (int i = 0; i < iv.Length; i++) + { + byte[] array = key; + int num = i; + array[num] ^= iv[i]; + } + } + int count = emuEndIndex - emuStartIndex + 1; + instructions = new List(count); + for (int j = 0; j < count; j++) + { + instructions.Add(origInstrs[emuStartIndex + j].Clone()); + } + return true; + } + Local CheckLocal(Instruction instr, bool isLdloc) + { + if (isLdloc && !instr.IsLdloc()) + { + return null; + } + if (!isLdloc && !instr.IsStloc()) + { + return null; + } + return instr.GetLocal(locals); + } + bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + if (!FindStart(instrs, out int emuStartIndex, out emuLocal)) + { + return false; + } + if (!FindEnd(instrs, emuStartIndex, out int emuEndIndex)) + { + return false; + } + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = emuLocal; + return true; + } + bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (int i = startIndex; i < instrs.Count; i++) + { + Instruction instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + { + break; + } + if (instr.IsStloc() && instr.GetLocal(locals) == emuLocal) + { + endIndex = i - 1; + return true; + } + } + endIndex = 0; + return false; + } + bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + Local local; + if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) + { + Instruction instr = instrs[i + 8]; + int newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = (instr.Operand as Instruction); + newStartIndex = instrs.IndexOf(instr); + } + if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) + { + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + } + i++; + } + startIndex = 0; + tmpLocal = null; + return false; + } + bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && instrs[i + 3].OpCode.Code.Equals(Code.Add)) + { + int newEndIndex = i + 3; + int newStartIndex = -1; + for (int x = newEndIndex; x > 0; x--) + { + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + newStartIndex = x + 1; + break; + } + } + if (newStartIndex >= 0) + { + List checkLocs = new List(); + int ckStartIndex = -1; + for (int y = newEndIndex; y >= newStartIndex; y--) + { + Local loc = CheckLocal(instrs[y], true); + if (loc != null) + { + if (!checkLocs.Contains(loc)) + { + checkLocs.Add(loc); + } + if (checkLocs.Count == 3) + { + break; + } + ckStartIndex = y; + } + } + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + i++; + } + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + bool FindStartEnd2(ref IList instrs, out int startIndex, out int endIndex, out Local tmpLocal, out Parameter tmpArg, ref MethodDef methodDef, ref List locals) + { + foreach (Instruction instr in instrs) + { + MethodDef method; + if (instr.OpCode.Equals(OpCodes.Call) && (method = (instr.Operand as MethodDef)) != null && method.ReturnType.FullName == "System.Byte[]") + { + using (IEnumerator enumerator2 = DotNetUtils.GetMethodCalls(method).GetEnumerator()) + { + while (enumerator2.MoveNext()) + { + MethodDef calledMethod; + if ((calledMethod = (enumerator2.Current as MethodDef)) != null && calledMethod.Parameters.Count == 2) + { + instrs = calledMethod.Body.Instructions; + methodDef = calledMethod; + locals = new List(calledMethod.Body.Variables); + startIndex = 0; + endIndex = instrs.Count - 1; + tmpLocal = null; + tmpArg = calledMethod.Parameters[1]; + return true; + } + } + } + } + } + endIndex = 0; + startIndex = 0; + tmpLocal = null; + tmpArg = null; + return false; + } + public static bool CouldBeResourceDecrypter(LocalTypes localTypes, IList additionalTypes) + { + List requiredTypes = new List + { + "System.Int32", + "System.Byte[]" + }; + requiredTypes.AddRange(additionalTypes); + return localTypes.All(requiredTypes); + } + public byte[] Decrypt(EmbeddedResource resource) + { + byte[] encrypted = resource.CreateReader().ToArray(); + byte[] decrypted = new byte[encrypted.Length]; + uint sum = 0U; + if (isNewDecrypter) + { + for (int i = 0; i < encrypted.Length; i += 4) + { + uint value = ReadUInt32(key, i % key.Length); + sum += value + CalculateMagic(sum + value); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + } + else + { + for (int j = 0; j < encrypted.Length; j += 4) + { + sum = CalculateMagic(sum + ReadUInt32(key, j % key.Length)); + WriteUInt32(decrypted, j, sum ^ ReadUInt32(encrypted, j)); + } + } + return decrypted; + } + uint ReadUInt32(byte[] ary, int index) + { + int sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + { + return BitConverter.ToUInt32(ary, index); + } + switch (sizeLeft) + { + case 1: + return (uint)ary[index]; + case 2: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8); + case 3: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8 | (int)ary[index + 2] << 16); + default: + throw new ApplicationException("Can't read data"); + } + } + void WriteUInt32(byte[] ary, int index, uint value) + { + int num = ary.Length - index; + if (num >= 1) + { + ary[index] = (byte)value; + } + if (num >= 2) + { + ary[index + 1] = (byte)(value >> 8); + } + if (num >= 3) + { + ary[index + 2] = (byte)(value >> 16); + } + if (num >= 4) + { + ary[index + 3] = (byte)(value >> 24); + } + } + public uint CalculateMagic(uint input) + { + if (emuArg == null) + { + instrEmulator.Initialize(method, method.Parameters, locals, method.Body.InitLocals, false); + instrEmulator.SetLocal(emuLocal, new Int32Value((int)input)); + } + else + { + instrEmulator.Initialize(emuMethod, emuMethod.Parameters, locals, emuMethod.Body.InitLocals, false); + instrEmulator.SetArg(emuArg, new Int32Value((int)input)); + } + foreach (Instruction instr in instructions) + { + instrEmulator.Emulate(instr); + } + if (!(instrEmulator.Pop() is Int32Value tos) || !tos.AllBitsValid()) + { + throw new ApplicationException("Couldn't calculate magic value"); + } + return (uint)tos.Value; + } + } + + public class V3 + { + readonly MethodDef method; + readonly List locals; + Local emuLocal; + List instructions; + readonly InstructionEmulator instrEmulator = new InstructionEmulator(); + public V3(MethodDef Method) + { + method = Method; + locals = new List(method.Body.Variables); + if (!Initialize()) + { + throw new ApplicationException("Could not initialize decrypter"); + } + } + bool Initialize() + { + IList origInstrs = method.Body.Instructions; + if (!Find(origInstrs, out int emuStartIndex, out int emuEndIndex, out emuLocal) && !FindStartEnd(origInstrs, out emuStartIndex, out emuEndIndex, out emuLocal)) + { + return false; + } + int count = emuEndIndex - emuStartIndex + 1; + instructions = new List(count); + for (int i = 0; i < count; i++) + { + instructions.Add(origInstrs[emuStartIndex + i].Clone()); + } + return true; + } + public byte[] Decrypt(EmbeddedResource resource) + { + byte[] encrypted = resource.CreateReader().ToArray(); + byte[] decrypted = new byte[encrypted.Length]; + uint sum = 0U; + for (int i = 0; i < encrypted.Length; i += 4) + { + sum = CalculateMagic(sum); + WriteUInt32(decrypted, i, sum ^ ReadUInt32(encrypted, i)); + } + return decrypted; + } + uint CalculateMagic(uint input) + { + instrEmulator.Initialize(method, method.Parameters, locals, method.Body.InitLocals, false); + instrEmulator.SetLocal(emuLocal, new Int32Value((int)input)); + foreach (Instruction instr in instructions) + { + instrEmulator.Emulate(instr); + } + if (!(instrEmulator.Pop() is Int32Value tos) || !tos.AllBitsValid()) + { + throw new ApplicationException("Couldn't calculate magic value"); + } + return (uint)tos.Value; + } + static uint ReadUInt32(byte[] ary, int index) + { + int sizeLeft = ary.Length - index; + if (sizeLeft >= 4) + { + return BitConverter.ToUInt32(ary, index); + } + switch (sizeLeft) + { + case 1: + return (uint)ary[index]; + case 2: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8); + case 3: + return (uint)((int)ary[index] | (int)ary[index + 1] << 8 | (int)ary[index + 2] << 16); + default: + throw new ApplicationException("Can't read data"); + } + } + static void WriteUInt32(byte[] ary, int index, uint value) + { + int num = ary.Length - index; + if (num >= 1) ary[index] = (byte)value; + if (num >= 2) ary[index + 1] = (byte)(value >> 8); + if (num >= 3) ary[index + 2] = (byte)(value >> 16); + if (num >= 4) ary[index + 3] = (byte)(value >> 24); + } + bool Find(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + startIndex = 0; + endIndex = 0; + tmpLocal = null; + if (!FindStart(instrs, out int emuStartIndex, out emuLocal)) return false; + if (!FindEnd(instrs, emuStartIndex, out int emuEndIndex)) return false; + startIndex = emuStartIndex; + endIndex = emuEndIndex; + tmpLocal = emuLocal; + return true; + } + bool FindEnd(IList instrs, int startIndex, out int endIndex) + { + for (int i = startIndex; i < instrs.Count; i++) + { + Instruction instr = instrs[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) break; + if (instr.IsStloc() && instr.GetLocal(locals) == emuLocal) + { + endIndex = i - 1; + return true; + } + } + endIndex = 0; + return false; + } + bool FindStart(IList instrs, out int startIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + Local local; + if (instrs[i].OpCode.Code.Equals(Code.Conv_U) && instrs[i + 1].OpCode.Code.Equals(Code.Ldelem_U1) && instrs[i + 2].OpCode.Code.Equals(Code.Or) && CheckLocal(instrs[i + 3], false) != null && (local = CheckLocal(instrs[i + 4], true)) != null && CheckLocal(instrs[i + 5], true) != null && instrs[i + 6].OpCode.Code.Equals(Code.Add) && CheckLocal(instrs[i + 7], false) == local) + { + Instruction instr = instrs[i + 8]; + int newStartIndex = i + 8; + if (instr.IsBr()) + { + instr = (instr.Operand as Instruction); + newStartIndex = instrs.IndexOf(instr); + } + if (newStartIndex >= 0 && instr != null && CheckLocal(instr, true) == local) + { + startIndex = newStartIndex; + tmpLocal = local; + return true; + } + } + i++; + } + startIndex = 0; + tmpLocal = null; + return false; + } + bool FindStartEnd(IList instrs, out int startIndex, out int endIndex, out Local tmpLocal) + { + int i = 0; + while (i + 8 < instrs.Count) + { + if (instrs[i].OpCode.Code.Equals(Code.Conv_R_Un) && instrs[i + 1].OpCode.Code.Equals(Code.Conv_R8) && instrs[i + 2].OpCode.Code.Equals(Code.Conv_U4) && instrs[i + 3].OpCode.Code.Equals(Code.Add)) + { + int newEndIndex = i + 3; + int newStartIndex = -1; + for (int x = newEndIndex; x > 0; x--) + { + if (instrs[x].OpCode.FlowControl != FlowControl.Next) + { + newStartIndex = x + 1; + break; + } + } + if (newStartIndex >= 0) + { + List checkLocs = new List(); + int ckStartIndex = -1; + for (int y = newEndIndex; y >= newStartIndex; y--) + { + Local loc = CheckLocal(instrs[y], true); + if (loc != null) + { + if (!checkLocs.Contains(loc)) + { + checkLocs.Add(loc); + } + if (checkLocs.Count == 3) + { + break; + } + ckStartIndex = y; + } + } + endIndex = newEndIndex; + startIndex = Math.Max(ckStartIndex, newStartIndex); + tmpLocal = CheckLocal(instrs[startIndex], true); + return true; + } + } + i++; + } + endIndex = 0; + startIndex = 0; + tmpLocal = null; + return false; + } + Local CheckLocal(Instruction instr, bool isLdloc) + { + if (isLdloc && !instr.IsLdloc()) return null; + if (!isLdloc && !instr.IsStloc()) return null; + return instr.GetLocal(locals); + } + public static bool CouldBeResourceDecrypter(LocalTypes localTypes, IList additionalTypes) + { + List requiredTypes = new List + { + "System.Reflection.Emit.DynamicMethod", + "System.Reflection.Emit.ILGenerator" + }; + requiredTypes.AddRange(additionalTypes); + return localTypes.All(requiredTypes); + } + } + public static DnrDecrypterType GetDecrypterType(MethodDef method, IList additionalTypes) + { + if (method == null || !method.IsStatic || method.Body == null) + { + return DnrDecrypterType.Unknown; + } + if (additionalTypes == null) + { + additionalTypes = new string[0]; + } + LocalTypes localTypes = new LocalTypes(method); + if (V1.CouldBeResourceDecrypter(method, localTypes, additionalTypes)) + { + return DnrDecrypterType.V1; + } + if (V2.CouldBeResourceDecrypter(localTypes, additionalTypes)) + { + return DnrDecrypterType.V2; + } + if (V3.CouldBeResourceDecrypter(localTypes, additionalTypes)) + { + return DnrDecrypterType.V3; + } + return DnrDecrypterType.Unknown; + } + + public static bool UsesPublicKeyToken(MethodDef resourceDecrypterMethod) + { + int pktIndex = 0; + foreach (Instruction instr in resourceDecrypterMethod.Body.Instructions) + { + if (instr.OpCode.FlowControl != FlowControl.Next) pktIndex = 0; + else if (instr.IsLdcI4()) + { + if (instr.GetLdcI4Value() != pktIndexes[pktIndex++]) pktIndex = 0; + else if (pktIndex >= pktIndexes.Length) return true; + } + } + return false; + } + } +} diff --git a/NetReactorSlayer.Core/Protections/Strings.cs b/NetReactorSlayer.Core/Protections/Strings.cs new file mode 100644 index 0000000..ed40a50 --- /dev/null +++ b/NetReactorSlayer.Core/Protections/Strings.cs @@ -0,0 +1,106 @@ +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using HarmonyLib; +using NETReactorSlayer.Core.Utils; +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection; + +namespace NETReactorSlayer.Core.Protections +{ + class Strings + { + public static void Execute() + { + StacktracePatcher.Patch(); + long count = 0L; + foreach (TypeDef type in Context.Module.GetTypes()) + { + foreach (MethodDef method in (from x in type.Methods where x.HasBody && x.Body.HasInstructions select x).ToArray()) + { + for (int i = 0; i < method.Body.Instructions.Count; i++) + { + try + { + if (method.Body.Instructions[i].IsLdcI4() && method.Body.Instructions[i + 1].OpCode.Equals(OpCodes.Call)) + { + object result = null; + IMethod strMethod = (IMethod)method.Body.Instructions[i + 1].Operand; + try + { + if (!DotNetUtils.IsMethod(strMethod, "System.String", "(System.Int32)")) + { + if (type == strMethod.DeclaringType) + { + if (!DotNetUtils.IsMethod(strMethod, "System.Object", "(System.Int32)")) continue; + } + else continue; + } + } + catch { } + result = (StacktracePatcher.PatchStackTraceGetMethod.MethodToReplace = (Context.Assembly.ManifestModule.ResolveMethod((int)strMethod.ResolveMethodDef().MDToken.Raw) as MethodInfo)).Invoke(null, new object[] + { + method.Body.Instructions[i].GetLdcI4Value() + }); + if (result != null && result.GetType() == typeof(string)) + { + try + { + foreach (var str in DotNetUtils.GetCodeStrings(strMethod.ResolveMethodDef())) + { + foreach (var name in Context.Assembly.GetManifestResourceNames()) + { + if (str == name) + { + if (strMethod.DeclaringType != type) + { + Remover.ResourceToRemove.Add(Context.Module.Resources.Find(name)); + Remover.MethodsToPatch.Add(strMethod.ResolveMethodDef()); + } + } + } + } + } + catch { } + method.Body.Instructions[i].OpCode = OpCodes.Nop; + method.Body.Instructions[i + 1].OpCode = OpCodes.Ldstr; + method.Body.Instructions[i + 1].Operand = (string)result; + count += 1L; + } + } + } + catch { } + } + } + } + if (count > 0L) Logger.Info((int)count + " Strings decrypted."); + else Logger.Warn("Couldn't find any encrypted string."); + } + + public static class StacktracePatcher + { + const string HarmonyId = "_"; + static Harmony harmony; + + public static void Patch() + { + harmony = new Harmony(HarmonyId); + harmony.PatchAll(Assembly.GetExecutingAssembly()); + } + + [HarmonyPatch(typeof(StackFrame), "GetMethod")] + public class PatchStackTraceGetMethod + { + public static MethodInfo MethodToReplace; + + public static void Postfix(ref MethodBase __result) + { + if (__result.DeclaringType != typeof(RuntimeMethodHandle)) return; + __result = MethodToReplace ?? MethodBase.GetCurrentMethod(); + } + } + } + } +} diff --git a/NetReactorSlayer.Core/Utils/Context.cs b/NetReactorSlayer.Core/Utils/Context.cs new file mode 100644 index 0000000..3996a62 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/Context.cs @@ -0,0 +1,150 @@ +using dnlib.DotNet; +using dnlib.DotNet.Writer; +using dnlib.PE; +using NetReactorSlayer.Core.Utils.de4dot; +using NETReactorSlayer.Core.Protections; +using System; +using System.IO; +using System.Reflection; + +namespace NETReactorSlayer.Core.Utils +{ + public class Context + { + public static bool Parse(string[] args) + { + #region Parse Arguments + bool isValid = false; + string path = string.Empty; + foreach (var item in args) + { + if (File.Exists(item) && !isValid) + { + isValid = true; + path = item; + } + else + { + foreach (var opt in Variables.options) + { + if (item.ToLower().Replace("--no-", string.Empty).Replace("-", string.Empty) == opt.Key) + { + Variables.options[opt.Key] = false; + break; + } + } + } + } + #endregion + if (isValid) + { + #region Get Assembly Infos + FilePath = path; + FileName = Path.GetFileNameWithoutExtension(path); + FileExt = Path.GetExtension(path); + FileDir = Path.GetDirectoryName(path); + DestPath = FileDir + "\\" + FileName + "_Slayed" + FileExt; + DestName = FileName + "_Slayed" + FileExt; + ModuleContext = GetModuleContext(); + AssemblyModule = new AssemblyModule(FilePath, ModuleContext); + #endregion + #region Load Assembly + try + { + Context.Module = AssemblyModule.Load(); + PEImage = new MyPEImage(DeobUtils.ReadModule(Context.Module)); + try { Assembly = Assembly.Load(FilePath); } catch { Assembly = Assembly.UnsafeLoadFrom(FilePath); } + return true; + } + catch (Exception ex) + { + try + { + byte[] unpacked = new Native(new PEImage(FilePath)).Unpack(); + if (unpacked != null) + { + #region Save + FilePath = FileDir + "\\" + "_native" + ".tmp"; + File.WriteAllBytes(FilePath, unpacked); + #endregion + AssemblyModule = new AssemblyModule(FilePath, ModuleContext); + Context.Module = AssemblyModule.Load(unpacked); + try { Assembly = Assembly.Load(FilePath); } catch { Assembly = Assembly.UnsafeLoadFrom(FilePath); } + PEImage = new MyPEImage(unpacked); + IsNative = true; + Logger.Info("Native image unpacked."); + return true; + } + else + { + Logger.Error("Failed to load assembly. " + ex.Message); + return false; + } + } + catch (Exception ex1) + { + Logger.Error("Failed to load assembly. " + ex1.Message); + return false; + } + } + #endregion + } + else + { + Logger.Error("No input files specified."); + Logger.PrintUsage(); + return false; + } + } + + public static ModuleContext GetModuleContext() + { + ModuleContext moduleContext = new ModuleContext(); + AssemblyResolver assemblyResolver = new AssemblyResolver(moduleContext); + Resolver resolver = new Resolver(assemblyResolver); + moduleContext.AssemblyResolver = assemblyResolver; + moduleContext.Resolver = resolver; + assemblyResolver.DefaultModuleContext = moduleContext; + return moduleContext; + } + + public static void Save() + { + try + { + if (Module.IsILOnly) + { + ModuleWriterOptions options = new ModuleWriterOptions(Module); + options.Logger = DummyLogger.NoThrowInstance; + options.MetadataOptions.Flags |= (MetadataFlags.PreserveTypeRefRids | MetadataFlags.PreserveTypeDefRids | MetadataFlags.PreserveFieldRids | MetadataFlags.PreserveMethodRids | MetadataFlags.PreserveParamRids | MetadataFlags.PreserveMemberRefRids | MetadataFlags.PreserveStandAloneSigRids | MetadataFlags.PreserveEventRids | MetadataFlags.PreservePropertyRids | MetadataFlags.PreserveTypeSpecRids | MetadataFlags.PreserveMethodSpecRids | MetadataFlags.PreserveUSOffsets | MetadataFlags.PreserveBlobOffsets | MetadataFlags.PreserveExtraSignatureData); + Module.Write(DestPath, options); + } + else + { + NativeModuleWriterOptions options = new NativeModuleWriterOptions(Module, false); + options.Logger = DummyLogger.NoThrowInstance; + options.MetadataOptions.Flags |= (MetadataFlags.PreserveTypeRefRids | MetadataFlags.PreserveTypeDefRids | MetadataFlags.PreserveFieldRids | MetadataFlags.PreserveMethodRids | MetadataFlags.PreserveParamRids | MetadataFlags.PreserveMemberRefRids | MetadataFlags.PreserveStandAloneSigRids | MetadataFlags.PreserveEventRids | MetadataFlags.PreservePropertyRids | MetadataFlags.PreserveTypeSpecRids | MetadataFlags.PreserveMethodSpecRids | MetadataFlags.PreserveUSOffsets | MetadataFlags.PreserveBlobOffsets | MetadataFlags.PreserveExtraSignatureData); + Module.NativeWrite(DestPath, options); + } + Logger.Info("Saved to: " + DestName); + } + catch (Exception ex) + { + Logger.Error("Failed to save file. " + ex.Message); + } + } + + public static bool IsNative = false; + public static string FileName { get; set; } + public static string FileExt { get; set; } + public static string FileDir { get; set; } + public static string FilePath { get; set; } + public static string DestPath { get; set; } + public static string DestName { get; set; } + public static ModuleDefMD Module { get; set; } + public static Assembly Assembly { get; set; } + public static AssemblyModule AssemblyModule { get; set; } + public static ModuleContext ModuleContext { get; set; } + public static MyPEImage PEImage { get; set; } + } +} diff --git a/NetReactorSlayer.Core/Utils/Logger.cs b/NetReactorSlayer.Core/Utils/Logger.cs new file mode 100644 index 0000000..d929ec5 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/Logger.cs @@ -0,0 +1,94 @@ +using System; + +namespace NETReactorSlayer.Core.Utils +{ + public static class Logger + { + public static void Debug(string message) + { + Console.ForegroundColor = ConsoleColor.DarkGray; + Console.WriteLine(" [DEBUG] " + message); + Console.ForegroundColor = ConsoleColor.White; + } + + public static void Info(string message) + { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine(" [DONE] " + message); + Console.ForegroundColor = ConsoleColor.White; + } + + public static void Warn(string message) + { + Console.ForegroundColor = ConsoleColor.Yellow; + Console.WriteLine(" [WARN] " + message); + Console.ForegroundColor = ConsoleColor.White; + } + + public static void Error(string message) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(" [ERROR] " + message); + Console.ForegroundColor = ConsoleColor.White; + } + + static void PrintSupportedVersions() + { + foreach (var item in Variables.supportedVersions) + { + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write("("); + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.Write(item); + Console.ForegroundColor = ConsoleColor.Gray; + Console.Write(") "); + } + Console.ForegroundColor = ConsoleColor.White; + } + + public static void PrintUsage() + { + Console.WriteLine(" Usage: NETReactorSlayer \r\n Options:"); + Console.ForegroundColor = ConsoleColor.Gray; + for (int i = 0; i < Variables.arguments.Length; i += 2) + { + Console.WriteLine(" " + Variables.arguments[i] + " " + Variables.arguments[i + 1]); + } + Console.ForegroundColor = ConsoleColor.White; + } + + public static void PrintLogo() + { + Console.Clear(); + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine(@" + __ __ _____ __ _ __ _ + /\ \ \/__\/__ \ /__\ ___ __ _ ___| |_ ___ _ __ / _\ | __ _ _ _ ___ _ __ + / \/ /_\ / /\/ / \/// _ \/ _` |/ __| __/ _ \| '__| \ \| |/ _` | | | |/ _ \ '__| + _/ /\ //__ / / / _ \ __/ (_| | (__| || (_) | | _\ \ | (_| | |_| | __/ | + (_)_\ \/\__/ \/ \/ \_/\___|\__,_|\___|\__\___/|_| \__/_|\__,_|\__, |\___|_| + |___/"); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(" .NET Reactor Slayer by CS-RET"); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(" Website: "); + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.WriteLine("www.CodeStrikers.org"); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(" Lates version on Github: "); + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.WriteLine("https://github.com/SychicBoy/NetReactorSlayer"); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(" Version: "); + Console.ForegroundColor = ConsoleColor.DarkCyan; + Console.WriteLine(Variables.version); + Console.ForegroundColor = ConsoleColor.White; + Console.Write(" Supported .NET Reactor versions: "); + Console.ForegroundColor = ConsoleColor.DarkCyan; + PrintSupportedVersions(); + Console.ForegroundColor = ConsoleColor.White; + Console.WriteLine(Environment.NewLine + " =========================="); + Console.ForegroundColor = ConsoleColor.White; + } + } +} diff --git a/NetReactorSlayer.Core/Utils/Variables.cs b/NetReactorSlayer.Core/Utils/Variables.cs new file mode 100644 index 0000000..ed5b974 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/Variables.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace NETReactorSlayer.Core.Utils +{ + public class Variables + { + public static readonly string version = Assembly.GetExecutingAssembly().GetName().Version.ToString(); + public static readonly string[] supportedVersions = { "6.0", "6.2", "6.3", "6.5", "6.7" }; + public static readonly string[] arguments ={ + "--no-necrobit", " Don't decrypt methods (NecroBit).", + "--no-anti-tamper", " Don't remove anti tamper.", + "--no-anti-debug", " Don't remove anti debugger.", + "--no-hide-call", " Don't restore hidden calls.", + "--no-str", " Don't decrypt strings.", + "--no-rsrc", " Don't decrypt assembly resources.", + "--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"}; + public static Dictionary options = new Dictionary() + { + ["necrobit"] = true, + ["antitamper"] = true, + ["antidebug"] = true, + ["hidecall"] = true, + ["str"] = true, + ["rsrc"] = true, + ["deob"] = true, + ["arithmetic"] = true, + ["proxycall"] = true, + ["dump"] = true + }; + } +} diff --git a/NetReactorSlayer.Core/Utils/de4dot/ArrayFinder.cs b/NetReactorSlayer.Core/Utils/de4dot/ArrayFinder.cs new file mode 100644 index 0000000..3ae182b --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/ArrayFinder.cs @@ -0,0 +1,232 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using de4dot.blocks.cflow; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System.Collections.Generic; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public static class ArrayFinder + { + public static List GetArrays(MethodDef method) => GetArrays(method, null); + + public static List GetArrays(MethodDef method, IType arrayElementType) + { + var arrays = new List(); + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) + { + var ary = GetArray(instrs, ref i, out var type); + if (ary == null) + break; + if (arrayElementType != null && !new SigComparer().Equals(type, arrayElementType)) + continue; + + arrays.Add(ary); + } + return arrays; + } + + public static byte[] GetArray(IList instrs, ref int index, out IType type) + { + for (int i = index; i < instrs.Count - 2; i++) + { + var newarr = instrs[i++]; + if (newarr.OpCode.Code != Code.Newarr) + continue; + + if (instrs[i++].OpCode.Code != Code.Dup) + continue; + + var ldtoken = instrs[i++]; + if (ldtoken.OpCode.Code != Code.Ldtoken) + continue; + var field = ldtoken.Operand as FieldDef; + if (field == null || field.InitialValue == null) + continue; + + index = i - 3; + type = newarr.Operand as IType; + return field.InitialValue; + } + + index = instrs.Count; + type = null; + return null; + } + + public static byte[] GetInitializedByteArray(MethodDef method, int arraySize) + { + int newarrIndex = FindNewarr(method, arraySize); + if (newarrIndex < 0) + return null; + return GetInitializedByteArray(arraySize, method, ref newarrIndex); + } + + public static byte[] GetInitializedByteArray(int arraySize, MethodDef method, ref int newarrIndex) + { + var resultValueArray = GetInitializedArray(arraySize, method, ref newarrIndex, Code.Stelem_I1); + + var resultArray = new byte[resultValueArray.Length]; + for (int i = 0; i < resultArray.Length; i++) + { + var intValue = resultValueArray[i] as Int32Value; + if (intValue == null || !intValue.AllBitsValid()) + return null; + resultArray[i] = (byte)intValue.Value; + } + return resultArray; + } + + public static short[] GetInitializedInt16Array(int arraySize, MethodDef method, ref int newarrIndex) + { + var resultValueArray = GetInitializedArray(arraySize, method, ref newarrIndex, Code.Stelem_I2); + + var resultArray = new short[resultValueArray.Length]; + for (int i = 0; i < resultArray.Length; i++) + { + var intValue = resultValueArray[i] as Int32Value; + if (intValue == null || !intValue.AllBitsValid()) + return null; + resultArray[i] = (short)intValue.Value; + } + return resultArray; + } + + public static int[] GetInitializedInt32Array(int arraySize, MethodDef method, ref int newarrIndex) + { + var resultValueArray = GetInitializedArray(arraySize, method, ref newarrIndex, Code.Stelem_I4); + + var resultArray = new int[resultValueArray.Length]; + for (int i = 0; i < resultArray.Length; i++) + { + var intValue = resultValueArray[i] as Int32Value; + if (intValue == null || !intValue.AllBitsValid()) + return null; + resultArray[i] = (int)intValue.Value; + } + return resultArray; + } + + public static uint[] GetInitializedUInt32Array(int arraySize, MethodDef method, ref int newarrIndex) + { + var resultArray = GetInitializedInt32Array(arraySize, method, ref newarrIndex); + if (resultArray == null) + return null; + + var ary = new uint[resultArray.Length]; + for (int i = 0; i < ary.Length; i++) + ary[i] = (uint)resultArray[i]; + return ary; + } + + public static Value[] GetInitializedArray(int arraySize, MethodDef method, ref int newarrIndex, Code stelemOpCode) + { + var resultValueArray = new Value[arraySize]; + + var emulator = new InstructionEmulator(method); + var theArray = new UnknownValue(); + emulator.Push(theArray); + + var instructions = method.Body.Instructions; + int i; + for (i = newarrIndex + 1; i < instructions.Count; i++) + { + var instr = instructions[i]; + if (instr.OpCode.FlowControl != FlowControl.Next) + break; + if (instr.OpCode.Code == Code.Newarr) + break; + switch (instr.OpCode.Code) + { + case Code.Newarr: + case Code.Newobj: + goto done; + + case Code.Stloc: + case Code.Stloc_S: + case Code.Stloc_0: + case Code.Stloc_1: + case Code.Stloc_2: + case Code.Stloc_3: + case Code.Starg: + case Code.Starg_S: + case Code.Stsfld: + case Code.Stfld: + if (emulator.Peek() == theArray && i != newarrIndex + 1 && i != newarrIndex + 2) + goto done; + break; + } + + if (instr.OpCode.Code == stelemOpCode) + { + var value = emulator.Pop(); + var index = emulator.Pop() as Int32Value; + var array = emulator.Pop(); + if (ReferenceEquals(array, theArray) && index != null && index.AllBitsValid()) + { + if (0 <= index.Value && index.Value < resultValueArray.Length) + resultValueArray[index.Value] = value; + } + } + else + emulator.Emulate(instr); + } + done: + if (i != newarrIndex + 1) + i--; + newarrIndex = i; + + return resultValueArray; + } + + static int FindNewarr(MethodDef method, int arraySize) + { + for (int i = 0; ; i++) + { + if (!FindNewarr(method, ref i, out int size)) + return -1; + if (size == arraySize) + return i; + } + } + + public static bool FindNewarr(MethodDef method, ref int i, out int size) + { + var instructions = method.Body.Instructions; + for (; i < instructions.Count; i++) + { + var instr = instructions[i]; + if (instr.OpCode.Code != Code.Newarr || i < 1) + continue; + var ldci4 = instructions[i - 1]; + if (!ldci4.IsLdcI4()) + continue; + + size = ldci4.GetLdcI4Value(); + return true; + } + + size = -1; + return false; + } + } +} diff --git a/NetReactorSlayer.Core/Utils/de4dot/AssemblyModule.cs b/NetReactorSlayer.Core/Utils/de4dot/AssemblyModule.cs new file mode 100644 index 0000000..4f423c4 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/AssemblyModule.cs @@ -0,0 +1,100 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using dnlib.DotNet; +using dnlib.DotNet.Writer; +using System.IO; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public interface IModuleWriterListener + { + void OnWriterEvent(ModuleWriterBase writer, ModuleWriterEvent evt); + } + + public class AssemblyModule + { + readonly string filename; + ModuleDefMD module; + readonly ModuleContext moduleContext; + + public AssemblyModule(string filename, ModuleContext moduleContext) + { + this.filename = Path.GetFullPath(filename); + this.moduleContext = moduleContext; + } + + public ModuleDefMD Load() + { + var options = new ModuleCreationOptions(moduleContext) { TryToLoadPdbFromDisk = false }; + return SetModule(ModuleDefMD.Load(filename, options)); + } + + public ModuleDefMD Load(byte[] fileData) + { + var options = new ModuleCreationOptions(moduleContext) { TryToLoadPdbFromDisk = false }; + return SetModule(ModuleDefMD.Load(fileData, options)); + } + + ModuleDefMD SetModule(ModuleDefMD newModule) + { + module = newModule; + TheAssemblyResolver.Instance.AddModule(module); + module.EnableTypeDefFindCache = true; + module.Location = filename; + return module; + } + + public void Save(string newFilename, MetadataFlags mdFlags, IModuleWriterListener writerListener) + { + if (module.IsILOnly) + { + var writerOptions = new ModuleWriterOptions(module); + writerOptions.WriterEvent += (s, e) => writerListener?.OnWriterEvent(e.Writer, e.Event); + writerOptions.MetadataOptions.Flags |= mdFlags; + module.Write(newFilename, writerOptions); + } + else + { + var writerOptions = new NativeModuleWriterOptions(module, optimizeImageSize: true); + writerOptions.WriterEvent += (s, e) => writerListener?.OnWriterEvent(e.Writer, e.Event); + writerOptions.MetadataOptions.Flags |= mdFlags; + writerOptions.KeepExtraPEData = true; + writerOptions.KeepWin32Resources = true; + module.NativeWrite(newFilename, writerOptions); + } + } + + public ModuleDefMD Reload(byte[] newModuleData, DumpedMethodsRestorer dumpedMethodsRestorer, IStringDecrypter stringDecrypter) + { + TheAssemblyResolver.Instance.Remove(module); + var options = new ModuleCreationOptions(moduleContext) { TryToLoadPdbFromDisk = false }; + var mod = ModuleDefMD.Load(newModuleData, options); + if (dumpedMethodsRestorer != null) + dumpedMethodsRestorer.Module = mod; + mod.StringDecrypter = stringDecrypter; + mod.MethodDecrypter = dumpedMethodsRestorer; + mod.TablesStream.ColumnReader = dumpedMethodsRestorer; + mod.TablesStream.MethodRowReader = dumpedMethodsRestorer; + return SetModule(mod); + } + + public override string ToString() => filename; + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/DeobUtils.cs b/NetReactorSlayer.Core/Utils/de4dot/DeobUtils.cs new file mode 100644 index 0000000..7461013 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/DeobUtils.cs @@ -0,0 +1,309 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using ICSharpCode.SharpZipLib.Zip.Compression; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Security.Cryptography; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public static class DeobUtils + { + public static void DecryptAndAddResources(ModuleDef module, Func decryptResource) + { + var decryptedResourceData = decryptResource(); + if (decryptedResourceData == null) + throw new ApplicationException("decryptedResourceData is null"); + var resourceModule = ModuleDefMD.Load(decryptedResourceData); + + foreach (var rsrc in resourceModule.Resources) + { + module.Resources.Add(rsrc); + } + } + + public static T Lookup(ModuleDefMD module, T def, string errorMessage) where T : class, ICodedToken + { + if (def == null) + return null; + if (!(module.ResolveToken(def.MDToken.Raw) is T newDef)) + throw new ApplicationException(errorMessage); + return newDef; + } + + public static byte[] ReadModule(ModuleDef module) => ReadFile(module.Location); + + public static bool IsCode(short[] nativeCode, byte[] code) + { + if (nativeCode.Length != code.Length) + return false; + for (int i = 0; i < nativeCode.Length; i++) + { + if (nativeCode[i] == -1) + continue; + if ((byte)nativeCode[i] != code[i]) + return false; + } + return true; + } + + public static byte[] ReadFile(string filename) + { + const int MAX_BYTES_READ = 0x200000; + + using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + var fileData = new byte[(int)fileStream.Length]; + + int bytes, offset = 0, length = fileData.Length; + while ((bytes = fileStream.Read(fileData, offset, Math.Min(MAX_BYTES_READ, length - offset))) > 0) + offset += bytes; + if (offset != length) + throw new ApplicationException("Could not read all bytes"); + + return fileData; + } + } + + public static byte[] Md5Sum(byte[] data) => MD5.Create().ComputeHash(data); + public static byte[] Sha1Sum(byte[] data) => SHA1.Create().ComputeHash(data); + public static byte[] Sha256Sum(byte[] data) => SHA256.Create().ComputeHash(data); + + public static byte[] AesDecrypt(byte[] data, byte[] key, byte[] iv) + { + using (var aes = new RijndaelManaged { Mode = CipherMode.CBC }) + { + using (var transform = aes.CreateDecryptor(key, iv)) + { + return transform.TransformFinalBlock(data, 0, data.Length); + } + } + } + + public static byte[] Des3Decrypt(byte[] data, byte[] key, byte[] iv) + { + using (var des3 = TripleDES.Create()) + { + using (var transform = des3.CreateDecryptor(key, iv)) + { + return transform.TransformFinalBlock(data, 0, data.Length); + } + } + } + + public static byte[] DesDecrypt(byte[] data, int start, int len, byte[] key, byte[] iv) + { + using (var des = new DESCryptoServiceProvider()) + { + using (var transform = des.CreateDecryptor(key, iv)) + { + return transform.TransformFinalBlock(data, start, len); + } + } + } + + // Code converted from C implementation @ http://en.wikipedia.org/wiki/XXTEA (btea() func) + public static void XxteaDecrypt(uint[] v, uint[] key) + { + const uint DELTA = 0x9E3779B9; + int n = v.Length; + uint rounds = (uint)(6 + 52 / n); + uint sum = rounds * DELTA; + uint y = v[0]; + uint z; + //#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))) + do + { + int e = (int)((sum >> 2) & 3); + int p; + for (p = n - 1; p > 0; p--) + { + z = v[p - 1]; + y = v[p] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))); + } + z = v[n - 1]; + y = v[0] -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))); + } while ((sum -= DELTA) != 0); + } + + // Code converted from C implementation @ http://en.wikipedia.org/wiki/XTEA (decipher() func) + public static void XteaDecrypt(ref uint v0, ref uint v1, uint[] key, int rounds) + { + const uint delta = 0x9E3779B9; + uint sum = (uint)(delta * rounds); + for (int i = 0; i < rounds; i++) + { + v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]); + sum -= delta; + v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]); + } + } + + public static string GetExtension(ModuleKind kind) + { + switch (kind) + { + case ModuleKind.Dll: + return ".dll"; + case ModuleKind.NetModule: + return ".netmodule"; + case ModuleKind.Console: + case ModuleKind.Windows: + default: + return ".exe"; + } + } + + public static byte[] Inflate(byte[] data, bool noHeader) => + Inflate(data, 0, data.Length, noHeader); + + public static byte[] Inflate(byte[] data, int start, int len, bool noHeader) => + Inflate(data, start, len, new Inflater(noHeader)); + + public static byte[] Inflate(byte[] data, Inflater inflater) => + Inflate(data, 0, data.Length, inflater); + + public static byte[] Inflate(byte[] data, int start, int len, Inflater inflater) + { + var buffer = new byte[0x1000]; + var memStream = new MemoryStream(); + inflater.SetInput(data, start, len); + while (true) + { + int count = inflater.Inflate(buffer, 0, buffer.Length); + if (count == 0) + break; + memStream.Write(buffer, 0, count); + } + return memStream.ToArray(); + } + + public static byte[] Gunzip(Stream input, int decompressedSize) + { + using (var gzip = new GZipStream(input, CompressionMode.Decompress)) + { + var decompressed = new byte[decompressedSize]; + if (gzip.Read(decompressed, 0, decompressedSize) != decompressedSize) + throw new ApplicationException("Could not gzip decompress"); + return decompressed; + } + } + + public static EmbeddedResource GetEmbeddedResourceFromCodeStrings(ModuleDef module, MethodDef method) + { + foreach (var s in DotNetUtils.GetCodeStrings(method)) + { + if (DotNetUtils.GetResource(module, s) is EmbeddedResource resource) + return resource; + } + return null; + } + + public static int ReadVariableLengthInt32(byte[] data, ref int index) + { + byte b = data[index++]; + if ((b & 0x80) == 0) + return b; + if ((b & 0x40) == 0) + return (((int)b & 0x3F) << 8) + data[index++]; + return (((int)b & 0x1F) << 24) + + ((int)data[index++] << 16) + + ((int)data[index++] << 8) + + data[index++]; + } + + public static bool HasInteger(MethodDef method, uint value) => HasInteger(method, (int)value); + public static bool HasInteger(MethodDef method, int value) => IndexOfLdci4Instruction(method, value) >= 0; + + public static int IndexOfLdci4Instruction(MethodDef method, int value) + { + if (method == null || method.Body == null) + return -1; + var instrs = method.Body.Instructions; + for (int i = 0; i < instrs.Count; i++) + { + var instr = instrs[i]; + if (!instr.IsLdcI4()) + continue; + if (instr.GetLdcI4Value() == value) + return i; + } + return -1; + } + + public static IEnumerable GetInitCctors(ModuleDef module, int maxCctors) + { + var cctor = DotNetUtils.GetModuleTypeCctor(module); + if (cctor != null) + yield return cctor; + + var entryPoint = module.EntryPoint; + if (entryPoint != null) + { + cctor = entryPoint.DeclaringType.FindStaticConstructor(); + if (cctor != null) + yield return cctor; + } + + foreach (var type in module.GetTypes()) + { + if (type == module.GlobalType) + continue; + cctor = type.FindStaticConstructor(); + if (cctor == null) + continue; + yield return cctor; + if (!type.IsEnum && --maxCctors <= 0) + break; + } + } + + public static List GetAllResolveHandlers(MethodDef method) + { + var list = new List(); + if (method == null || method.Body == null) + return list; + foreach (var instr in method.Body.Instructions) + { + if (instr.OpCode.Code != Code.Ldftn && instr.OpCode.Code != Code.Ldvirtftn) + continue; + if (!(instr.Operand is MethodDef handler)) + continue; + if (!DotNetUtils.IsMethod(handler, "System.Reflection.Assembly", "(System.Object,System.ResolveEventArgs)")) + continue; + list.Add(handler); + } + return list; + } + + public static MethodDef GetResolveMethod(MethodDef method) + { + var handlers = DeobUtils.GetAllResolveHandlers(method); + if (handlers.Count == 0) + return null; + return handlers[0]; + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/DumpedMethodsRestorer.cs b/NetReactorSlayer.Core/Utils/de4dot/DumpedMethodsRestorer.cs new file mode 100644 index 0000000..5a7945b --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/DumpedMethodsRestorer.cs @@ -0,0 +1,85 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using de4dot.blocks; +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using dnlib.DotNet.MD; +using dnlib.PE; +using System.Collections.Generic; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public class DumpedMethodsRestorer : IRowReader, IColumnReader, IMethodDecrypter + { + ModuleDefMD module; + readonly DumpedMethods dumpedMethods; + + public ModuleDefMD Module + { + set => module = value; + } + + public DumpedMethodsRestorer(DumpedMethods dumpedMethods) => this.dumpedMethods = dumpedMethods; + + DumpedMethod GetDumpedMethod(uint rid) => dumpedMethods.Get(0x06000000 | rid); + + public bool TryReadRow(uint rid, out RawMethodRow row) + { + var dm = GetDumpedMethod(rid); + if (dm == null) + { + row = default; + return false; + } + else + { + row = new RawMethodRow(dm.mdRVA, dm.mdImplFlags, dm.mdFlags, dm.mdName, dm.mdSignature, dm.mdParamList); + return true; + } + } + + public bool ReadColumn(MDTable table, uint rid, ColumnInfo column, out uint value) + { + if (table.Table == Table.Method) + { + if (TryReadRow(rid, out var row)) + { + value = row[column.Index]; + return true; + } + } + + value = 0; + return false; + } + + public bool GetMethodBody(uint rid, RVA rva, IList parameters, GenericParamContext gpContext, out MethodBody methodBody) + { + var dm = GetDumpedMethod(rid); + if (dm == null) + { + methodBody = null; + return false; + } + methodBody = MethodBodyReader.CreateCilBody(module, dm.code, dm.extraSections, parameters, dm.mhFlags, dm.mhMaxStack, dm.mhCodeSize, dm.mhLocalVarSigTok, gpContext); + return true; + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/LocalTypes.cs b/NetReactorSlayer.Core/Utils/de4dot/LocalTypes.cs new file mode 100644 index 0000000..80c00a0 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/LocalTypes.cs @@ -0,0 +1,101 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using dnlib.DotNet; +using dnlib.DotNet.Emit; +using System; +using System.Collections.Generic; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public class StringCounts + { + readonly Dictionary strings = new Dictionary(StringComparer.Ordinal); + + public IEnumerable Strings => strings.Keys; + public int NumStrings => strings.Count; + + public void Add(string s) + { + strings.TryGetValue(s, out int count); + strings[s] = count + 1; + } + + public bool Exists(string s) + { + if (s == null) + return false; + return strings.ContainsKey(s); + } + + public bool All(IList list) + { + foreach (var s in list) + { + if (!Exists(s)) + return false; + } + return true; + } + + public bool Exactly(IList list) => list.Count == strings.Count && All(list); + + public int Count(string s) + { + strings.TryGetValue(s, out int count); + return count; + } + } + + public class FieldTypes : StringCounts + { + public FieldTypes(TypeDef type) => Initialize(type.Fields); + public FieldTypes(IEnumerable fields) => Initialize(fields); + + void Initialize(IEnumerable fields) + { + if (fields == null) + return; + foreach (var field in fields) + { + var type = field.FieldSig.GetFieldType(); + if (type != null) + Add(type.FullName); + } + } + } + public class LocalTypes : StringCounts + { + public LocalTypes(MethodDef method) + { + if (method != null && method.Body != null) + Initialize(method.Body.Variables); + } + + public LocalTypes(IEnumerable locals) => Initialize(locals); + + void Initialize(IEnumerable locals) + { + if (locals == null) + return; + foreach (var local in locals) + Add(local.Type.FullName); + } + } +} diff --git a/NetReactorSlayer.Core/Utils/de4dot/MethodBodyParser.cs b/NetReactorSlayer.Core/Utils/de4dot/MethodBodyParser.cs new file mode 100644 index 0000000..5e253b8 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/MethodBodyParser.cs @@ -0,0 +1,182 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using dnlib.IO; +using System; +using System.IO; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + [Serializable] + public class InvalidMethodBody : Exception + { + public InvalidMethodBody() + { + } + + public InvalidMethodBody(string msg) + : base(msg) + { + } + } + + public class MethodBodyHeader + { + public ushort flags; + public ushort maxStack; + public uint codeSize; + public uint localVarSigTok; + } + + public static class MethodBodyParser + { + public static MethodBodyHeader ParseMethodBody(ref DataReader reader, out byte[] code, out byte[] extraSections) + { + try + { + return ParseMethodBody2(ref reader, out code, out extraSections); + } + catch (Exception ex) when (ex is IOException || ex is ArgumentException) + { + throw new InvalidMethodBody(); + } + } + + public static bool Verify(byte[] data) + { + var reader = ByteArrayDataReaderFactory.CreateReader(data); + return Verify(ref reader); + } + + public static bool Verify(ref DataReader reader) + { + try + { + ParseMethodBody(ref reader, out var code, out var extraSections); + return true; + } + catch (InvalidMethodBody) + { + return false; + } + } + + static MethodBodyHeader ParseMethodBody2(ref DataReader reader, out byte[] code, out byte[] extraSections) + { + var mbHeader = new MethodBodyHeader(); + + uint codeOffset; + byte b = Peek(ref reader); + if ((b & 3) == 2) + { + mbHeader.flags = 2; + mbHeader.maxStack = 8; + mbHeader.codeSize = (uint)(reader.ReadByte() >> 2); + mbHeader.localVarSigTok = 0; + codeOffset = 1; + } + else if ((b & 7) == 3) + { + mbHeader.flags = reader.ReadUInt16(); + codeOffset = (uint)(4 * (mbHeader.flags >> 12)); + if (codeOffset != 12) + throw new InvalidMethodBody(); + mbHeader.maxStack = reader.ReadUInt16(); + mbHeader.codeSize = reader.ReadUInt32(); + if (mbHeader.codeSize > int.MaxValue) + throw new InvalidMethodBody(); + mbHeader.localVarSigTok = reader.ReadUInt32(); + if (mbHeader.localVarSigTok != 0 && (mbHeader.localVarSigTok >> 24) != 0x11) + throw new InvalidMethodBody(); + } + else + throw new InvalidMethodBody(); + + if (mbHeader.codeSize + codeOffset > reader.Length) + throw new InvalidMethodBody(); + code = reader.ReadBytes((int)mbHeader.codeSize); + + if ((mbHeader.flags & 8) != 0) + extraSections = ReadExtraSections2(ref reader); + else + extraSections = null; + + return mbHeader; + } + + static void Align(ref DataReader reader, int alignment) => + reader.Position = (reader.Position + (uint)alignment - 1) & ~((uint)alignment - 1); + + public static byte[] ReadExtraSections(ref DataReader reader) + { + try + { + return ReadExtraSections2(ref reader); + } + catch (Exception ex) when (ex is IOException || ex is ArgumentException) + { + throw new InvalidMethodBody(); + } + } + + static byte[] ReadExtraSections2(ref DataReader reader) + { + Align(ref reader, 4); + int startPos = (int)reader.Position; + ParseSection(ref reader); + int size = (int)reader.Position - startPos; + reader.Position = (uint)startPos; + return reader.ReadBytes(size); + } + + static void ParseSection(ref DataReader reader) + { + byte flags; + do + { + Align(ref reader, 4); + + flags = reader.ReadByte(); + if ((flags & 1) == 0) + throw new InvalidMethodBody("Not an exception section"); + if ((flags & 0x3E) != 0) + throw new InvalidMethodBody("Invalid bits set"); + + if ((flags & 0x40) != 0) + { + reader.Position--; + int num = (int)(reader.ReadUInt32() >> 8) / 24; + reader.Position += (uint)num * 24; + } + else + { + int num = reader.ReadByte() / 12; + reader.Position += 2 + (uint)num * 12; + } + } while ((flags & 0x80) != 0); + } + + static byte Peek(ref DataReader reader) + { + byte b = reader.ReadByte(); + reader.Position--; + return b; + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/MyPEImage.cs b/NetReactorSlayer.Core/Utils/de4dot/MyPEImage.cs new file mode 100644 index 0000000..6eaae8a --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/MyPEImage.cs @@ -0,0 +1,214 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using de4dot.blocks; +using dnlib.DotNet.MD; +using dnlib.IO; +using dnlib.PE; +using System; +using System.Collections.Generic; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public sealed class MyPEImage : IDisposable + { + IPEImage peImage; + public byte[] peImageData; + Metadata metadata; + bool dnFileInitialized; + ImageSectionHeader dotNetSection; + readonly bool ownPeImage; + + public Metadata Metadata + { + get + { + if (dnFileInitialized) + return metadata; + dnFileInitialized = true; + + var dotNetDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[14]; + if (dotNetDir.VirtualAddress != 0 && dotNetDir.Size >= 0x48) + { + metadata = MetadataFactory.CreateMetadata(peImage, false); + dotNetSection = FindSection(dotNetDir.VirtualAddress); + } + return metadata; + } + } + + public ImageCor20Header Cor20Header => Metadata.ImageCor20Header; + + public DataReader Reader; + + public IPEImage PEImage => peImage; + public ImageFileHeader FileHeader => peImage.ImageNTHeaders.FileHeader; + public IImageOptionalHeader OptionalHeader => peImage.ImageNTHeaders.OptionalHeader; + public IList Sections => peImage.ImageSectionHeaders; + public uint Length => (uint)Reader.Length; + public MyPEImage(IPEImage peImage) => Initialize(peImage); + + public MyPEImage(byte[] peImageData) + { + ownPeImage = true; + this.peImageData = peImageData; + Initialize(new PEImage(peImageData)); + } + + void Initialize(IPEImage peImage) + { + this.peImage = peImage; + Reader = peImage.CreateReader(); + } + + public ImageSectionHeader FindSection(RVA rva) + { + foreach (var section in peImage.ImageSectionHeaders) + { + if (section.VirtualAddress <= rva && rva < section.VirtualAddress + Math.Max(section.VirtualSize, section.SizeOfRawData)) + return section; + } + return null; + } + + public ImageSectionHeader FindSection(string name) + { + foreach (var section in peImage.ImageSectionHeaders) + { + if (section.DisplayName == name) + return section; + } + return null; + } + + public void ReadMethodTableRowTo(DumpedMethod dm, uint rid) + { + dm.token = 0x06000000 + rid; + if (!Metadata.TablesStream.TryReadMethodRow(rid, out var row)) + throw new ArgumentException("Invalid Method rid"); + dm.mdRVA = row.RVA; + dm.mdImplFlags = row.ImplFlags; + dm.mdFlags = row.Flags; + dm.mdName = row.Name; + dm.mdSignature = row.Signature; + dm.mdParamList = row.ParamList; + } + + public void UpdateMethodHeaderInfo(DumpedMethod dm, MethodBodyHeader mbHeader) + { + dm.mhFlags = mbHeader.flags; + dm.mhMaxStack = mbHeader.maxStack; + dm.mhCodeSize = dm.code == null ? 0 : (uint)dm.code.Length; + dm.mhLocalVarSigTok = mbHeader.localVarSigTok; + } + + public uint RvaToOffset(uint rva) => (uint)peImage.ToFileOffset((RVA)rva); + + static bool IsInside(ImageSectionHeader section, uint offset, uint length) => + offset >= section.PointerToRawData && offset + length <= section.PointerToRawData + section.SizeOfRawData; + + public void WriteUInt32(uint rva, uint data) => OffsetWriteUInt32(RvaToOffset(rva), data); + public void WriteUInt16(uint rva, ushort data) => OffsetWriteUInt16(RvaToOffset(rva), data); + public byte ReadByte(uint rva) => OffsetReadByte(RvaToOffset(rva)); + public int ReadInt32(uint rva) => (int)OffsetReadUInt32(RvaToOffset(rva)); + public ushort ReadUInt16(uint rva) => OffsetReadUInt16(RvaToOffset(rva)); + public byte[] ReadBytes(uint rva, int size) => OffsetReadBytes(RvaToOffset(rva), size); + + public void OffsetWriteUInt32(uint offset, uint val) + { + peImageData[offset + 0] = (byte)val; + peImageData[offset + 1] = (byte)(val >> 8); + peImageData[offset + 2] = (byte)(val >> 16); + peImageData[offset + 3] = (byte)(val >> 24); + } + + public void OffsetWriteUInt16(uint offset, ushort val) + { + peImageData[offset + 0] = (byte)val; + peImageData[offset + 1] = (byte)(val >> 8); + } + + public uint OffsetReadUInt32(uint offset) + { + Reader.Position = offset; + return Reader.ReadUInt32(); + } + + public ushort OffsetReadUInt16(uint offset) + { + Reader.Position = offset; + return Reader.ReadUInt16(); + } + + public byte OffsetReadByte(uint offset) + { + Reader.Position = offset; + return Reader.ReadByte(); + } + + public byte[] OffsetReadBytes(uint offset, int size) + { + Reader.Position = offset; + return Reader.ReadBytes(size); + } + + public void OffsetWrite(uint offset, byte[] data) => + Array.Copy(data, 0, peImageData, offset, data.Length); + bool Intersect(uint offset1, uint length1, uint offset2, uint length2) => + !(offset1 + length1 <= offset2 || offset2 + length2 <= offset1); + bool Intersect(uint offset, uint length, IFileSection location) => + Intersect(offset, length, (uint)location.StartOffset, (uint)(location.EndOffset - location.StartOffset)); + + public bool DotNetSafeWriteOffset(uint offset, byte[] data) + { + if (Metadata != null) + { + uint length = (uint)data.Length; + + if (!IsInside(dotNetSection, offset, length)) + return false; + if (Intersect(offset, length, Metadata.ImageCor20Header)) + return false; + if (Intersect(offset, length, Metadata.MetadataHeader)) + return false; + } + + OffsetWrite(offset, data); + return true; + } + + public bool DotNetSafeWrite(uint rva, byte[] data) => + DotNetSafeWriteOffset((uint)peImage.ToFileOffset((RVA)rva), data); + + public void Dispose() + { + if (ownPeImage) + { + if (metadata != null) + metadata.Dispose(); + if (peImage != null) + peImage.Dispose(); + } + + metadata = null; + peImage = null; + Reader = default; + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/QuickLZ.cs b/NetReactorSlayer.Core/Utils/de4dot/QuickLZ.cs new file mode 100644 index 0000000..8803eeb --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/QuickLZ.cs @@ -0,0 +1,169 @@ +// QuickLZ data compression library +// Copyright (C) 2006-2011 Lasse Mikkel Reinhold +// lar@quicklz.com +// +// QuickLZ can be used for free under the GPL 1, 2 or 3 license (where anything +// released into public must be open source) or under a commercial license if such +// has been acquired (see http://www.quicklz.com/order.html). The commercial license +// does not cover derived or ported versions created by third parties under GPL. + +// Port of QuickLZ to C# by de4dot@gmail.com. This code is most likely not working now. + +using System; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public class QuickLZBase + { + protected static uint Read32(byte[] data, int index) => BitConverter.ToUInt32(data, index); + + // Can't use Array.Copy() when data overlaps so here's one that works + protected static void Copy(byte[] src, int srcIndex, byte[] dst, int dstIndex, int size) + { + for (int i = 0; i < size; i++) + dst[dstIndex++] = src[srcIndex++]; + } + + static readonly int[] indexInc = new int[] { 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; + public static void Decompress(byte[] inData, int inIndex, byte[] outData) + { + int decompressedLength = outData.Length; + int outIndex = 0; + uint val1 = 1; + uint count; + int size; + + while (true) + { + if (val1 == 1) + { + val1 = Read32(inData, inIndex); + inIndex += 4; + } + uint val2 = Read32(inData, inIndex); + if ((val1 & 1) == 1) + { + val1 >>= 1; + if ((val2 & 3) == 0) + { + count = (val2 & 0xFF) >> 2; + Copy(outData, (int)(outIndex - count), outData, outIndex, 3); + outIndex += 3; + inIndex++; + } + else if ((val2 & 2) == 0) + { + count = (val2 & 0xFFFF) >> 2; + Copy(outData, (int)(outIndex - count), outData, outIndex, 3); + outIndex += 3; + inIndex += 2; + } + else if ((val2 & 1) == 0) + { + size = (int)((val2 >> 2) & 0x0F) + 3; + count = (val2 & 0xFFFF) >> 6; + Copy(outData, (int)(outIndex - count), outData, outIndex, size); + outIndex += size; + inIndex += 2; + } + else if ((val2 & 4) == 0) + { + size = (int)((val2 >> 3) & 0x1F) + 3; + count = (val2 & 0xFFFFFF) >> 8; + Copy(outData, (int)(outIndex - count), outData, outIndex, size); + outIndex += size; + inIndex += 3; + } + else if ((val2 & 8) == 0) + { + count = val2 >> 15; + if (count != 0) + { + size = (int)((val2 >> 4) & 0x07FF) + 3; + inIndex += 4; + } + else + { + size = (int)Read32(inData, inIndex + 4); + count = Read32(inData, inIndex + 8); + inIndex += 12; + } + Copy(outData, (int)(outIndex - count), outData, outIndex, size); + outIndex += size; + } + else + { + byte b = (byte)(val2 >> 16); + size = (int)(val2 >> 4) & 0x0FFF; + if (size == 0) + { + size = (int)Read32(inData, inIndex + 3); + inIndex += 7; + } + else + inIndex += 3; + for (int i = 0; i < size; i++) + outData[outIndex++] = b; + } + } + else + { + Copy(inData, inIndex, outData, outIndex, 4); + int index = (int)(val1 & 0x0F); + outIndex += indexInc[index]; + inIndex += indexInc[index]; + val1 >>= indexInc[index]; + if (outIndex >= decompressedLength - 4) + break; + } + } + while (outIndex < decompressedLength) + { + if (val1 == 1) + { + inIndex += 4; + val1 = 0x80000000; + } + outData[outIndex++] = inData[inIndex++]; + val1 >>= 1; + } + } + } + + public class QuickLZ : QuickLZBase + { + static readonly int DEFAULT_QCLZ_SIG = 0x5A4C4351; + + public static bool IsCompressed(byte[] data) + { + if (data.Length < 4) + return false; + return BitConverter.ToInt32(data, 0) == DEFAULT_QCLZ_SIG; + } + + public static byte[] Decompress(byte[] inData) => Decompress(inData, DEFAULT_QCLZ_SIG); + + public static byte[] Decompress(byte[] inData, int sig) + { + /*int mode =*/ + BitConverter.ToInt32(inData, 4); + int compressedLength = BitConverter.ToInt32(inData, 8); + int decompressedLength = BitConverter.ToInt32(inData, 12); + bool isDataCompressed = BitConverter.ToInt32(inData, 16) == 1; + int headerLength = 32; + if (BitConverter.ToInt32(inData, 0) != sig || BitConverter.ToInt32(inData, compressedLength - 4) != sig) + throw new ApplicationException("No QCLZ sig"); + + byte[] outData = new byte[decompressedLength]; + + if (!isDataCompressed) + { + Copy(inData, headerLength, outData, 0, decompressedLength); + return outData; + } + + Decompress(inData, headerLength, outData); + return outData; + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/Utils/de4dot/TheAssemblyResolver.cs b/NetReactorSlayer.Core/Utils/de4dot/TheAssemblyResolver.cs new file mode 100644 index 0000000..05c96e3 --- /dev/null +++ b/NetReactorSlayer.Core/Utils/de4dot/TheAssemblyResolver.cs @@ -0,0 +1,208 @@ +/* + Copyright (C) 2011-2015 de4dot@gmail.com + + This file is part of de4dot. + + de4dot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + de4dot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with de4dot. If not, see . +*/ + +using dnlib.DotNet; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; + +namespace NetReactorSlayer.Core.Utils.de4dot +{ + public class TheAssemblyResolver : AssemblyResolver + { + public static readonly TheAssemblyResolver Instance = new TheAssemblyResolver(); + + public TheAssemblyResolver() + { + EnableTypeDefCache = true; + AddOtherSearchPaths(PostSearchPaths); + } + + public void AddSearchDirectory(string dir) + { + if (!PostSearchPaths.Contains(dir)) + PostSearchPaths.Add(dir); + } + + public void AddModule(ModuleDef module) => AddToCache(module.Assembly); + + public void RemoveModule(ModuleDef module) + { + var assembly = module.Assembly; + if (assembly == null) + return; + + Remove(module.Assembly); + } + + public void ClearAll() + { + //TODO: cache.Clear(); + //TODO: resetSearchPaths(); + } + + static void AddOtherSearchPaths(IList paths) + { + var dirPF = Environment.GetEnvironmentVariable("ProgramFiles"); + AddOtherAssemblySearchPaths(paths, dirPF); + var dirPFx86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); + if (!StringComparer.OrdinalIgnoreCase.Equals(dirPF, dirPFx86)) + AddOtherAssemblySearchPaths(paths, dirPFx86); + + var windir = Environment.GetEnvironmentVariable("WINDIR"); + if (!string.IsNullOrEmpty(windir)) + { + AddIfExists(paths, windir, @"Microsoft.NET\Framework\v1.1.4322"); + AddIfExists(paths, windir, @"Microsoft.NET\Framework\v1.0.3705"); + } + } + + static void AddOtherAssemblySearchPaths(IList paths, string path) + { + if (string.IsNullOrEmpty(path)) + return; + AddSilverlightDirs(paths, Path.Combine(path, @"Microsoft Silverlight")); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Libraries\Client"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Libraries\Server"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v2.0\Reference Assemblies"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v3.0\Libraries\Client"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v3.0\Libraries\Server"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v4.0\Libraries\Client"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v4.0\Libraries\Server"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v5.0\Libraries\Client"); + AddIfExists(paths, path, @"Microsoft SDKs\Silverlight\v5.0\Libraries\Server"); + AddIfExists(paths, path, @"Microsoft.NET\SDK\CompactFramework\v2.0\WindowsCE"); + AddIfExists(paths, path, @"Microsoft.NET\SDK\CompactFramework\v3.5\WindowsCE"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.6"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Profile\Client"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETCore\v5.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETCore\v4.5.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETCore\v4.5"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v3.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.2"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETMicroFramework\v4.3"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v4.6"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\.NETPortable\v5.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\v3.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\v3.5"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v3.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\Silverlight\v5.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\WindowsPhone\v8.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\Framework\WindowsPhoneApp\v8.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.259.3.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.78.3.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.7.4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETCore\3.3.1.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v2.0\2.3.0.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.0.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.3.1.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETFramework\v4.0\4.4.0.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\2.3.5.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\2.3.5.1"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\.NETPortable\3.47.4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v2.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\2.0\Runtime\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\.NETPortable"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v2.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\FSharp\3.0\Runtime\v4.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\WindowsPowerShell\v1.0"); + AddIfExists(paths, path, @"Reference Assemblies\Microsoft\WindowsPowerShell\3.0"); + AddIfExists(paths, path, @"Microsoft Visual Studio .NET\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio .NET\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio .NET 2003\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio .NET 2003\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 8\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 11.0\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 11.0\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 12.0\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 12.0\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 14.0\Common7\IDE\PublicAssemblies"); + AddIfExists(paths, path, @"Microsoft Visual Studio 14.0\Common7\IDE\PrivateAssemblies"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Windows\x86"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v2.0\References\Xbox360"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Windows\x86"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Xbox360"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.0\References\Zune"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Windows\x86"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v3.1\References\Zune"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Windows\x86"); + AddIfExists(paths, path, @"Microsoft XNA\XNA Game Studio\v4.0\References\Xbox360"); + AddIfExists(paths, path, @"Windows CE Tools\wce500\Windows Mobile 5.0 Pocket PC SDK\Designtimereferences"); + AddIfExists(paths, path, @"Windows CE Tools\wce500\Windows Mobile 5.0 Smartphone SDK\Designtimereferences"); + AddIfExists(paths, path, @"Windows Mobile 5.0 SDK R2\Managed Libraries"); + AddIfExists(paths, path, @"Windows Mobile 6 SDK\Managed Libraries"); + AddIfExists(paths, path, @"Windows Mobile 6.5.3 DTK\Managed Libraries"); + AddIfExists(paths, path, @"Microsoft SQL Server\90\SDK\Assemblies"); + AddIfExists(paths, path, @"Microsoft SQL Server\100\SDK\Assemblies"); + AddIfExists(paths, path, @"Microsoft SQL Server\110\SDK\Assemblies"); + AddIfExists(paths, path, @"Microsoft SQL Server\120\SDK\Assemblies"); + AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 2\Assemblies"); + AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 3\Assemblies"); + AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET MVC 4\Assemblies"); + AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET Web Pages\v1.0\Assemblies"); + AddIfExists(paths, path, @"Microsoft ASP.NET\ASP.NET Web Pages\v2.0\Assemblies"); + AddIfExists(paths, path, @"Microsoft SDKs\F#\3.0\Framework\v4.0"); + } + + static void AddSilverlightDirs(IList paths, string basePath) + { + if (!Directory.Exists(basePath)) + return; + try + { + var di = new DirectoryInfo(basePath); + foreach (var dir in di.GetDirectories()) + { + if (Regex.IsMatch(dir.Name, @"^\d+(?:\.\d+){3}$")) + AddIfExists(paths, basePath, dir.Name); + } + } + catch + { + } + } + + static void AddIfExists(IList paths, string basePath, string extraPath) + { + var path = Path.Combine(basePath, extraPath); + if (Directory.Exists(path)) + paths.Add(path); + } + } +} \ No newline at end of file diff --git a/NetReactorSlayer.Core/app.config b/NetReactorSlayer.Core/app.config new file mode 100644 index 0000000..58c0121 --- /dev/null +++ b/NetReactorSlayer.Core/app.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer.Core/packages.config b/NetReactorSlayer.Core/packages.config new file mode 100644 index 0000000..617f264 --- /dev/null +++ b/NetReactorSlayer.Core/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer.sln b/NetReactorSlayer.sln new file mode 100644 index 0000000..56aed9b --- /dev/null +++ b/NetReactorSlayer.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31613.86 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetReactorSlayer", "NetReactorSlayer\NetReactorSlayer.csproj", "{91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetReactorSlayer-x64", "NetReactorSlayer-x64\NetReactorSlayer-x64.csproj", "{4363303C-AF4C-4C1B-8284-FC468514C15D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetReactorSlayer.Core", "NetReactorSlayer.Core\NetReactorSlayer.Core.csproj", "{11E508F0-7AA3-46BD-9891-766DEB293830}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48}.Release|Any CPU.Build.0 = Release|Any CPU + {4363303C-AF4C-4C1B-8284-FC468514C15D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4363303C-AF4C-4C1B-8284-FC468514C15D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4363303C-AF4C-4C1B-8284-FC468514C15D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4363303C-AF4C-4C1B-8284-FC468514C15D}.Release|Any CPU.Build.0 = Release|Any CPU + {11E508F0-7AA3-46BD-9891-766DEB293830}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {11E508F0-7AA3-46BD-9891-766DEB293830}.Debug|Any CPU.Build.0 = Debug|Any CPU + {11E508F0-7AA3-46BD-9891-766DEB293830}.Release|Any CPU.ActiveCfg = Release|Any CPU + {11E508F0-7AA3-46BD-9891-766DEB293830}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D66B7617-0D26-456C-8639-71BB0CCEA94A} + EndGlobalSection +EndGlobal diff --git a/NetReactorSlayer/App.config b/NetReactorSlayer/App.config new file mode 100644 index 0000000..8e15646 --- /dev/null +++ b/NetReactorSlayer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer/NetReactorSlayer.csproj b/NetReactorSlayer/NetReactorSlayer.csproj new file mode 100644 index 0000000..db473f7 --- /dev/null +++ b/NetReactorSlayer/NetReactorSlayer.csproj @@ -0,0 +1,64 @@ + + + + + Debug + AnyCPU + {91F328FB-CEEC-4869-8DAD-0F9A3EEFAB48} + Exe + NetReactorSlayer + NetReactorSlayer + v4.5 + 512 + true + + + + + x86 + true + full + false + ..\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + NetReactorSlayer.Program + + + + False + ..\Debug\NetReactorSlayer.Core.dll + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetReactorSlayer/Program.cs b/NetReactorSlayer/Program.cs new file mode 100644 index 0000000..9d803b5 --- /dev/null +++ b/NetReactorSlayer/Program.cs @@ -0,0 +1,7 @@ +namespace NetReactorSlayer +{ + class Program + { + static void Main(string[] args) => NetReactorSlayer.Core.Program.Main(args); + } +} diff --git a/NetReactorSlayer/Properties/AssemblyInfo.cs b/NetReactorSlayer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4ef9937 --- /dev/null +++ b/NetReactorSlayer/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NetReactorSlayer")] +[assembly: AssemblyDescription("Deobfuscator for eziriz.com/dotnet_reactor.htm")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("CS-RET")] +[assembly: AssemblyProduct("NetReactorSlayer")] +[assembly: AssemblyCopyright("Copyright © 2021-2021 CS-RET")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("91f328fb-ceec-4869-8dad-0f9a3eefab48")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]