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
+
+
+
+
+
+
+
+
+
+
+