From 22b7c4350634cdc86b2ccd17ccf3a4672849bb83 Mon Sep 17 00:00:00 2001 From: Luke Dennyel <65508738+MRLuke956@users.noreply.github.com> Date: Sat, 24 Aug 2024 17:37:02 -0300 Subject: [PATCH 1/2] Delete src directory --- .../.idea.UnmanagedString/.idea/.gitignore | 13 -- src/.idea/.idea.UnmanagedString/.idea/.name | 1 - .../.idea.UnmanagedString/.idea/encodings.xml | 4 - .../.idea/indexLayout.xml | 8 - src/.idea/.idea.UnmanagedString/.idea/vcs.xml | 6 - src/UnmanagedString.sln | 16 -- src/UnmanagedString/EntryPoint.cs | 193 ------------------ src/UnmanagedString/Logger.cs | 76 ------- src/UnmanagedString/UnmanagedString.csproj | 17 -- 9 files changed, 334 deletions(-) delete mode 100644 src/.idea/.idea.UnmanagedString/.idea/.gitignore delete mode 100644 src/.idea/.idea.UnmanagedString/.idea/.name delete mode 100644 src/.idea/.idea.UnmanagedString/.idea/encodings.xml delete mode 100644 src/.idea/.idea.UnmanagedString/.idea/indexLayout.xml delete mode 100644 src/.idea/.idea.UnmanagedString/.idea/vcs.xml delete mode 100644 src/UnmanagedString.sln delete mode 100644 src/UnmanagedString/EntryPoint.cs delete mode 100644 src/UnmanagedString/Logger.cs delete mode 100644 src/UnmanagedString/UnmanagedString.csproj diff --git a/src/.idea/.idea.UnmanagedString/.idea/.gitignore b/src/.idea/.idea.UnmanagedString/.idea/.gitignore deleted file mode 100644 index 2458d04..0000000 --- a/src/.idea/.idea.UnmanagedString/.idea/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Rider ignored files -/contentModel.xml -/modules.xml -/projectSettingsUpdater.xml -/.idea.UnmanagedString.iml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/src/.idea/.idea.UnmanagedString/.idea/.name b/src/.idea/.idea.UnmanagedString/.idea/.name deleted file mode 100644 index c007076..0000000 --- a/src/.idea/.idea.UnmanagedString/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -UnmanagedString \ No newline at end of file diff --git a/src/.idea/.idea.UnmanagedString/.idea/encodings.xml b/src/.idea/.idea.UnmanagedString/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/src/.idea/.idea.UnmanagedString/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/.idea/.idea.UnmanagedString/.idea/indexLayout.xml b/src/.idea/.idea.UnmanagedString/.idea/indexLayout.xml deleted file mode 100644 index 7b08163..0000000 --- a/src/.idea/.idea.UnmanagedString/.idea/indexLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/src/.idea/.idea.UnmanagedString/.idea/vcs.xml b/src/.idea/.idea.UnmanagedString/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/src/.idea/.idea.UnmanagedString/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/UnmanagedString.sln b/src/UnmanagedString.sln deleted file mode 100644 index f5de716..0000000 --- a/src/UnmanagedString.sln +++ /dev/null @@ -1,16 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnmanagedString", "UnmanagedString\UnmanagedString.csproj", "{9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/UnmanagedString/EntryPoint.cs b/src/UnmanagedString/EntryPoint.cs deleted file mode 100644 index 5e8a50b..0000000 --- a/src/UnmanagedString/EntryPoint.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Text; -using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Native; -using AsmResolver.DotNet.Signatures; -using AsmResolver.PE.DotNet; -using AsmResolver.PE.DotNet.Cil; -using AsmResolver.PE.DotNet.Metadata.Tables.Rows; -using AsmResolver.PE.File.Headers; -using MethodDefinition = AsmResolver.DotNet.MethodDefinition; -using ModuleDefinition = AsmResolver.DotNet.ModuleDefinition; - -namespace UnmanagedString; - -public static class EntryPoint -{ - public static void Main(string[] args) - { - if (args.Length != 1) - { - Logger.Error("Usage: UnmanagedString.exe "); - return; - } - - if (!File.Exists(args[0])) - { - Logger.Error($"File not found: {args[0]}"); - return; - } - - var module = ModuleDefinition.FromFile(args[0]); - var importer = new ReferenceImporter(module); - - var stringSbytePointerCtor = - importer.ImportMethod(typeof(string).GetConstructor(new[] { typeof(sbyte*) })!); - var stringCharPointerCtor = - importer.ImportMethod(typeof(string).GetConstructor(new[] { typeof(char*) })!); - var stringSbytePointerWithLengthCtor = - importer.ImportMethod(typeof(string).GetConstructor(new[] { typeof(sbyte*), typeof(int), typeof(int) })!); - var stringCharPointerWithLengthCtor = - importer.ImportMethod(typeof(string).GetConstructor(new[] { typeof(char*), typeof(int), typeof(int) })!); - - Logger.Information("Starting..."); - - module.Attributes &= ~DotNetDirectoryFlags.ILOnly; - var isx86 = module.MachineType == MachineType.I386; - - if (isx86) - { - module.PEKind = OptionalHeaderMagic.Pe32; - module.MachineType = MachineType.I386; - module.Attributes |= DotNetDirectoryFlags.Bit32Required; - } - else - { - module.PEKind = OptionalHeaderMagic.Pe32Plus; - module.MachineType = MachineType.Amd64; - } - - var encodedStrings = new Dictionary(); - - foreach (var type in module.GetAllTypes()) - foreach (var method in type.Methods) - for (var index = 0; index < method.CilMethodBody!.Instructions.Count; ++index) - { - var instruction = method.CilMethodBody!.Instructions[index]; - - if (instruction.OpCode == CilOpCodes.Ldstr && - instruction.Operand is string { Length: > 0 } content) - { - var useUnicode = !CanBeEncodedIn7BitAscii(content); - var addNullTerminator = !HasNullCharacter(content); - - if (!encodedStrings.TryGetValue(content, out var nativeMethod)) // reuse encoded strings - { - nativeMethod = CreateNewNativeMethodWithString(content, module, isx86, useUnicode, - addNullTerminator); - encodedStrings.Add(content, nativeMethod); - } - - instruction.ReplaceWith(CilOpCodes.Call, nativeMethod); - if (addNullTerminator) - { - method.CilMethodBody.Instructions.Insert(++index, - new CilInstruction(CilOpCodes.Newobj, - useUnicode ? stringCharPointerCtor : stringSbytePointerCtor)); - } - else - { - method.CilMethodBody.Instructions.Insert(++index, - CilInstruction.CreateLdcI4(0)); - method.CilMethodBody.Instructions.Insert(++index, - CilInstruction.CreateLdcI4(content.Length)); - method.CilMethodBody.Instructions.Insert(++index, - new CilInstruction(CilOpCodes.Newobj, - useUnicode ? stringCharPointerWithLengthCtor : stringSbytePointerWithLengthCtor)); - } - } - } - - module.Write(args[0] + "_strings.exe"); - Logger.Success("Done!"); - } - - private static MethodDefinition? CreateNewNativeMethodWithString(string content, ModuleDefinition originalModule, - bool isX86, bool useUnicode, bool addNullTerminator) - { - ArgumentNullException.ThrowIfNull(originalModule); - ArgumentNullException.ThrowIfNull(content); - - var factory = originalModule.CorLibTypeFactory; - - var methodName = Guid.NewGuid().ToString(); - var method = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Static, - MethodSignature.CreateStatic(factory.SByte.MakePointerType())); - - method.ImplAttributes |= MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | - MethodImplAttributes.PreserveSig; - method.Attributes |= MethodAttributes.PInvokeImpl; - - originalModule.GetOrCreateModuleType().Methods.Add(method); - - if (addNullTerminator) - { - content += "\0"; // not adding on byte level as it has encoding-dependent size - } - - var stringBytes = useUnicode - ? Encoding.Unicode.GetBytes(content) - : Encoding.ASCII.GetBytes(content); - - var prefix = isX86 - ? stackalloc byte[] - { - 0x55, // push ebp - 0x89, 0xE5, // mov ebp, esp - 0xE8, 0x05, 0x00, 0x00, 0x00, // call - 0x83, 0xC0, 0x01, // add eax, 1 - // : - 0x5D, // pop ebp - 0xC3, // ret - // : - 0x58, // pop eax - 0x83, 0xC0, 0x0B, // add eax, 0xb - 0xEB, 0xF8 // jmp - } - : stackalloc byte[] - { - 0x48, 0x8D, 0x05, 0x01, 0x00, 0x00, 0x00, // lea rax, [rip + 0x1] - 0xC3 // ret - }; - - Span code = stackalloc byte[prefix.Length + stringBytes.Length]; - prefix.CopyTo(code); - stringBytes.CopyTo(code[prefix.Length..]); - - var body = new NativeMethodBody(method) - { - Code = code.ToArray() - }; - - Logger.Success($"Created new native method with name: {methodName} for string: {content.TrimEnd()}"); - method.NativeMethodBody = body; - return method; - } - - private static bool CanBeEncodedIn7BitAscii(ReadOnlySpan text) - { - // ReSharper disable once ForCanBeConvertedToForeach - for (var i = 0; i < text.Length; i++) - { - if (text[i] > '\x7f') - { - return false; - } - } - - return true; - } - - private static bool HasNullCharacter(ReadOnlySpan text) - { - // ReSharper disable once ForCanBeConvertedToForeach - for (var i = 0; i < text.Length; i++) - { - if (text[i] == '\0') - { - return true; - } - } - - return false; - } -} \ No newline at end of file diff --git a/src/UnmanagedString/Logger.cs b/src/UnmanagedString/Logger.cs deleted file mode 100644 index 7faca62..0000000 --- a/src/UnmanagedString/Logger.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System.Drawing; -using Colorful; -using Console = Colorful.Console; - -namespace UnmanagedString; - -public static class Logger -{ - private const string MessageStyle = "[{0}] [{1}] {2}"; - - public static void Information(ReadOnlySpan message) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.LightGreen), - new Formatter("INFO", Color.LightGreen), - new Formatter(message.ToString(), Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } - - public static void Success(ReadOnlySpan message) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.Green), - new Formatter("SUCCESS", Color.Green), - new Formatter(message.ToString(), Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } - - public static void Warning(ReadOnlySpan message) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.Yellow), - new Formatter("WARNING", Color.Yellow), - new Formatter(message.ToString(), Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } - - public static void Error(ReadOnlySpan message) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.Red), - new Formatter("ERROR", Color.Red), - new Formatter(message.ToString(), Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } - - public static void Skipped(ReadOnlySpan message) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.DarkGray), - new Formatter("SKIPPED", Color.DarkGray), - new Formatter(message.ToString(), Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } - - public static void Exception(Exception ex) - { - var replacements = new[] - { - new(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), Color.Red), - new Formatter("EXCEPTION", Color.Red), - new Formatter(ex.Message, Color.White) - }; - Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); - } -} \ No newline at end of file diff --git a/src/UnmanagedString/UnmanagedString.csproj b/src/UnmanagedString/UnmanagedString.csproj deleted file mode 100644 index 5684109..0000000 --- a/src/UnmanagedString/UnmanagedString.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - Exe - net7.0-windows - enable - enable - - - - - - - - - - From 7f7747b3732f0d97a4fffed7762456c6edb9fef4 Mon Sep 17 00:00:00 2001 From: Luke Dennyel <65508738+MRLuke956@users.noreply.github.com> Date: Sat, 24 Aug 2024 17:43:24 -0300 Subject: [PATCH 2/2] Add files via upload --- src/UnmanagedString.sln | 16 ++ src/UnmanagedString/EntryPoint.cs | 219 +++++++++++++++++++++ src/UnmanagedString/Logger.cs | 47 +++++ src/UnmanagedString/UnmanagedString.csproj | 18 ++ 4 files changed, 300 insertions(+) create mode 100644 src/UnmanagedString.sln create mode 100644 src/UnmanagedString/EntryPoint.cs create mode 100644 src/UnmanagedString/Logger.cs create mode 100644 src/UnmanagedString/UnmanagedString.csproj diff --git a/src/UnmanagedString.sln b/src/UnmanagedString.sln new file mode 100644 index 0000000..f5de716 --- /dev/null +++ b/src/UnmanagedString.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnmanagedString", "UnmanagedString\UnmanagedString.csproj", "{9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DFBDD82-6DF9-4B44-8F28-94F337E50A8A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/src/UnmanagedString/EntryPoint.cs b/src/UnmanagedString/EntryPoint.cs new file mode 100644 index 0000000..b3ae4ca --- /dev/null +++ b/src/UnmanagedString/EntryPoint.cs @@ -0,0 +1,219 @@ +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Native; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet; +using AsmResolver.PE.DotNet.Cil; +using AsmResolver.PE.DotNet.Metadata.Tables.Rows; +using AsmResolver.PE.File.Headers; +using System.Security.Cryptography; +using System.Text; +using UnmanagedString; +using MethodDefinition = AsmResolver.DotNet.MethodDefinition; +using ModuleDefinition = AsmResolver.DotNet.ModuleDefinition; + +namespace SecureStringHandler +{ + public static class StringObfuscator + { + private static readonly byte[] Key = GenerateSecureKey(); // Generate a secure key for AES encryption + + public static void Main(string[] args) + { + try + { + // Check if the user provided the required argument + if (args.Length != 1) + { + Logger.Error("Usage: SecureStringHandler.exe "); + return; // Exit if no argument is provided + } + + var targetAssemblyPath = args[0]; + + // Ensure the specified file exists + if (!File.Exists(targetAssemblyPath)) + { + Logger.Error($"File not found: {targetAssemblyPath}"); + return; // Exit if the file doesn't exist + } + + var module = ModuleDefinition.FromFile(targetAssemblyPath); + var referenceImporter = new ReferenceImporter(module); + + // Import constructors for string handling (because we love strings!) + var stringSByteCtor = referenceImporter.ImportMethod(typeof(string).GetConstructor(new[] { typeof(sbyte*) })!); + var stringCharCtor = referenceImporter.ImportMethod(typeof(string).GetConstructor(new[] { typeof(char*) })!); + var stringSByteWithLengthCtor = referenceImporter.ImportMethod(typeof(string).GetConstructor(new[] { typeof(sbyte*), typeof(int), typeof(int) })!); + var stringCharWithLengthCtor = referenceImporter.ImportMethod(typeof(string).GetConstructor(new[] { typeof(char*), typeof(int), typeof(int) })!); + + Logger.Information("Obfuscation process starting..."); + + // Set module attributes to ensure proper execution (because we need to be fancy!) + module.Attributes &= ~DotNetDirectoryFlags.ILOnly; + var is32Bit = module.MachineType == MachineType.I386; + module.PEKind = is32Bit ? OptionalHeaderMagic.PE32 : OptionalHeaderMagic.PE32Plus; + module.MachineType = is32Bit ? MachineType.I386 : MachineType.Amd64; + if (is32Bit) module.Attributes |= DotNetDirectoryFlags.Bit32Required; + + var encodedStringsMap = new Dictionary(); + + // Loop through all types and their methods to find strings to obfuscate + foreach (var type in module.GetAllTypes()) + { + foreach (var method in type.Methods) + { + var instructions = method.CilMethodBody!.Instructions; + for (var index = 0; index < instructions.Count; ++index) + { + var instruction = instructions[index]; + + // Look for string literals to obfuscate + if (instruction.OpCode == CilOpCodes.Ldstr && instruction.Operand is string { Length: > 0 } stringContent) + { + var encryptedContent = EncryptString(stringContent); // Encrypt the string (because who doesn't love encryption?) + var useUnicode = !IsAsciiCompatible(stringContent); + var requiresNullTerminator = !stringContent.Contains('\0'); + + // Check if we've already created a native method for this encrypted string + if (!encodedStringsMap.TryGetValue(encryptedContent, out var nativeMethod)) + { + nativeMethod = GenerateNativeMethodForString(encryptedContent, module, is32Bit, useUnicode, requiresNullTerminator); + encodedStringsMap[encryptedContent] = nativeMethod; // Cache the method for future use + } + + instruction.ReplaceWith(CilOpCodes.Call, nativeMethod); // Replace with a call to our native method + + // Handle null terminators like a boss + if (requiresNullTerminator) + { + instructions.Insert(++index, new CilInstruction(CilOpCodes.Newobj, useUnicode ? stringCharCtor : stringSByteCtor)); + } + else + { + instructions.InsertRange(++index, new[] + { + CilInstruction.CreateLdcI4(0), // Load 0 for the null terminator + CilInstruction.CreateLdcI4(stringContent.Length), // Load the length of the string + new CilInstruction(CilOpCodes.Newobj, useUnicode ? stringCharWithLengthCtor : stringSByteWithLengthCtor) + }); + index += 2; // Adjusting index after inserting instructions + } + } + } + } + } + + module.Write($"{targetAssemblyPath}_obfuscated.dll"); // Save the obfuscated assembly + Logger.Success("Obfuscation completed successfully! Now it's even more secure!"); + } + catch (Exception ex) + { + Logger.Error($"An error occurred: {ex.Message} (Oops! Did I do that?)"); + } + finally + { + // Introduce a delay before closing the console (because good things come to those who wait!) + Thread.Sleep(3000); // Wait for 3 seconds + } + } + + private static string EncryptString(string plainText) + { + // Check for null or empty strings (we don't want to encrypt nothing!) + if (string.IsNullOrEmpty(plainText)) + { + throw new ArgumentException("Plain text cannot be null or empty.", nameof(plainText)); + } + + using (var aes = Aes.Create()) + { + aes.Key = Key; // Ensure this is 32 bytes for AES-256 + aes.GenerateIV(); // Generate a new IV for each encryption (because randomness is key!) + + using var encryptor = aes.CreateEncryptor(aes.Key, aes.IV); + using var ms = new MemoryStream(); + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + { + using var writer = new StreamWriter(cs); + writer.Write(plainText); // Write the plain text into the stream (magic happens here!) + } + + // Store the IV with the ciphertext for decryption (because we need to remember how we did it!) + return Convert.ToBase64String(aes.IV) + ":" + Convert.ToBase64String(ms.ToArray()); + } + } + + private static byte[] GenerateSecureKey() + { + using (var rng = RandomNumberGenerator.Create()) + { + var key = new byte[32]; // 256-bit key (the more, the merrier!) + rng.GetBytes(key); // Fill the key with random bytes + return key; + } + } + + private static MethodDefinition? GenerateNativeMethodForString(string stringContent, ModuleDefinition originalModule, bool isX86, bool useUnicode, bool addNullTerminator) + { + ArgumentNullException.ThrowIfNull(originalModule); + ArgumentNullException.ThrowIfNull(stringContent); + var factory = originalModule.CorLibTypeFactory; + var obfuscatedMethodName = Guid.NewGuid().ToString("N"); // Generate a unique method name (because we like to keep things interesting!) + + // Create a new method definition for the obfuscated string + var method = new MethodDefinition(obfuscatedMethodName, MethodAttributes.Public | MethodAttributes.Static, MethodSignature.CreateStatic(factory.SByte.MakePointerType())); + method.ImplAttributes |= MethodImplAttributes.Native | MethodImplAttributes.Unmanaged | MethodImplAttributes.PreserveSig; + method.Attributes |= MethodAttributes.PInvokeImpl; + originalModule.GetOrCreateModuleType().Methods.Add(method); + + // Handle null terminator if needed (because we care about string integrity!) + if (addNullTerminator) + { + stringContent += "\0"; // Handle null terminator + } + + // Convert the string to bytes based on encoding type + var stringBytes = useUnicode ? Encoding.Unicode.GetBytes(stringContent) : Encoding.ASCII.GetBytes(stringContent); + var prefixInstructions = isX86 ? new byte[] + { + 0x55, 0x89, 0xE5, 0xE8, 0x05, 0x00, 0x00, 0x00, + 0x83, 0xC0, 0x01, 0x5D, 0xC3, 0x58, 0x83, 0xC0, 0x0B, 0xEB, 0xF8 + } : new byte[] + { + 0x48, 0x8D, 0x05, 0x01, 0x00, 0x00, 0x00, 0xC3 + }; + + // Create the method body with the correct instructions + Span methodBody = new byte[prefixInstructions.Length + stringBytes.Length]; + prefixInstructions.CopyTo(methodBody); + stringBytes.CopyTo(methodBody[prefixInstructions.Length..]); + + var nativeBody = new NativeMethodBody(method) { Code = methodBody.ToArray() }; + Logger.Success($"Native method created: {obfuscatedMethodName} for string: {stringContent.TrimEnd()}"); + method.NativeMethodBody = nativeBody; + return method; // Return the created method (because sharing is caring!) + } + + private static bool IsAsciiCompatible(ReadOnlySpan text) + { + foreach (var character in text) + { + if (character > '\x7F') return false; // Non-ASCII character found, return false + } + return true; // All characters are ASCII, return true + } + + private static void ControlFlowObfuscation(MethodDefinition method) + { + var instructions = method.CilMethodBody!.Instructions; + using (var rng = RandomNumberGenerator.Create()) + { + byte[] randomBytes = new byte[4]; + rng.GetBytes(randomBytes); + int fakeBranchIndex = BitConverter.ToInt32(randomBytes, 0) % instructions.Count; // Generate a fake branch index (because why not?) + instructions.Insert(fakeBranchIndex, new CilInstruction(CilOpCodes.Nop)); // Insert a no-op instruction + instructions.Insert(fakeBranchIndex + 1, new CilInstruction(CilOpCodes.Br, instructions[fakeBranchIndex + 2])); // Create a fake branch + } + } + } +} \ No newline at end of file diff --git a/src/UnmanagedString/Logger.cs b/src/UnmanagedString/Logger.cs new file mode 100644 index 0000000..b53d0b1 --- /dev/null +++ b/src/UnmanagedString/Logger.cs @@ -0,0 +1,47 @@ +using Colorful; +using System.Drawing; +using Console = Colorful.Console; + +namespace UnmanagedString +{ + public static class Logger + { + private const string MessageStyle = "[{0}] [{1}] {2}"; // The format for log messages + + private static void Log(ReadOnlySpan message, string level, Color levelColor) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message)); // Ensure the message is not null + } + + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // Get current timestamp + // Explicitly define the type of the array (because C# loves strong typing!) + var replacements = new Formatter[] + { + new Formatter(timestamp, Color.Gray), // Timestamp in gray + new Formatter($" {level}", levelColor), // Log level in specified color + new Formatter(message.ToString(), Color.White) // Actual message in white + }; + + // Log the message with formatting (like magic, but with colors!) + Console.WriteLineFormatted(MessageStyle, Color.Gray, replacements); + } + + public static void Information(ReadOnlySpan message) => Log(message, "INFO", Color.LightGreen); // Informational message + public static void Success(ReadOnlySpan message) => Log(message, "SUCCESS", Color.Green); // Success message (yay!) + public static void Warning(ReadOnlySpan message) => Log(message, "WARNING", Color.Yellow); // Warning message (uh-oh!) + public static void Error(ReadOnlySpan message) => Log(message, "ERROR", Color.Red); // Error message (time to panic!) + public static void Skipped(ReadOnlySpan message) => Log(message, "SKIPPED", Color.DarkGray); // Skipped message (because we can!) + + public static void Exception(Exception ex) + { + if (ex == null) + { + throw new ArgumentNullException(nameof(ex)); // Ensure the exception is not null + } + + Log(ex.Message.AsSpan(), "EXCEPTION", Color.Red); // Log the exception message + } + } +} \ No newline at end of file diff --git a/src/UnmanagedString/UnmanagedString.csproj b/src/UnmanagedString/UnmanagedString.csproj new file mode 100644 index 0000000..523299c --- /dev/null +++ b/src/UnmanagedString/UnmanagedString.csproj @@ -0,0 +1,18 @@ + + + + Exe + net6.0-windows + enable + enable + + + + + + + + + + +