diff --git a/Dalamud.Injector/EntryPoint.cs b/Dalamud.Injector/EntryPoint.cs
index 7f063d5b1..00c8c74c5 100644
--- a/Dalamud.Injector/EntryPoint.cs
+++ b/Dalamud.Injector/EntryPoint.cs
@@ -16,307 +16,306 @@ using Serilog.Events;
using static Dalamud.Injector.NativeFunctions;
-namespace Dalamud.Injector
+namespace Dalamud.Injector;
+
+///
+/// Entrypoint to the program.
+///
+public sealed class EntryPoint
{
///
- /// Entrypoint to the program.
+ /// A delegate used during initialization of the CLR from Dalamud.Injector.Boot.
///
- public sealed class EntryPoint
+ /// Count of arguments.
+ /// char** string arguments.
+ public delegate void MainDelegate(int argc, IntPtr argvPtr);
+
+ ///
+ /// Start the Dalamud injector.
+ ///
+ /// Count of arguments.
+ /// byte** string arguments.
+ public static void Main(int argc, IntPtr argvPtr)
{
- ///
- /// A delegate used during initialization of the CLR from Dalamud.Injector.Boot.
- ///
- /// Count of arguments.
- /// char** string arguments.
- public delegate void MainDelegate(int argc, IntPtr argvPtr);
+ InitUnhandledException();
+ InitLogging();
- ///
- /// Start the Dalamud injector.
- ///
- /// Count of arguments.
- /// byte** string arguments.
- public static void Main(int argc, IntPtr argvPtr)
+ var args = new string[argc];
+
+ unsafe
{
- InitUnhandledException();
- InitLogging();
-
- var args = new string[argc];
-
- unsafe
+ var argv = (IntPtr*)argvPtr;
+ for (var i = 0; i < argc; i++)
{
- var argv = (IntPtr*)argvPtr;
- for (var i = 0; i < argc; i++)
- {
- args[i] = Marshal.PtrToStringUni(argv[i]);
- }
+ args[i] = Marshal.PtrToStringUni(argv[i]);
}
-
- var cwd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
- if (cwd.FullName != Directory.GetCurrentDirectory())
- {
- Log.Debug($"Changing cwd to {cwd}");
- Directory.SetCurrentDirectory(cwd.FullName);
- }
-
- var process = GetProcess(args.ElementAtOrDefault(1));
- var startInfo = GetStartInfo(args.ElementAtOrDefault(2), process);
-
- // TODO: XL does not set this!!! we need to keep this line here for now, otherwise we crash in the Dalamud entrypoint
- startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
-
- // This seems to help with the STATUS_INTERNAL_ERROR condition
- Thread.Sleep(1000);
-
- Inject(process, startInfo);
-
- Thread.Sleep(1000);
}
- private static void InitUnhandledException()
+ var cwd = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory;
+ if (cwd.FullName != Directory.GetCurrentDirectory())
{
- AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
+ Log.Debug($"Changing cwd to {cwd}");
+ Directory.SetCurrentDirectory(cwd.FullName);
+ }
+
+ var process = GetProcess(args.ElementAtOrDefault(1));
+ var startInfo = GetStartInfo(args.ElementAtOrDefault(2), process);
+
+ // TODO: XL does not set this!!! we need to keep this line here for now, otherwise we crash in the Dalamud entrypoint
+ startInfo.WorkingDirectory = Directory.GetCurrentDirectory();
+
+ // This seems to help with the STATUS_INTERNAL_ERROR condition
+ Thread.Sleep(1000);
+
+ Inject(process, startInfo);
+
+ Thread.Sleep(1000);
+ }
+
+ private static void InitUnhandledException()
+ {
+ AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
+ {
+ if (Log.Logger == null)
{
- if (Log.Logger == null)
+ Console.WriteLine($"A fatal error has occurred: {eventArgs.ExceptionObject}");
+ }
+ else
+ {
+ var exObj = eventArgs.ExceptionObject;
+ if (exObj is Exception ex)
{
- Console.WriteLine($"A fatal error has occurred: {eventArgs.ExceptionObject}");
+ Log.Error(ex, "A fatal error has occurred.");
}
else
{
- var exObj = eventArgs.ExceptionObject;
- if (exObj is Exception ex)
- {
- Log.Error(ex, "A fatal error has occurred.");
- }
- else
- {
- Log.Error($"A fatal error has occurred: {eventArgs.ExceptionObject}");
- }
+ Log.Error($"A fatal error has occurred: {eventArgs.ExceptionObject}");
}
+ }
#if DEBUG
- var caption = "Debug Error";
- var message =
- $"Couldn't inject.\nMake sure that Dalamud was not injected into your target process " +
- $"as a release build before and that the target process can be accessed with VM_WRITE permissions.\n\n" +
- $"{eventArgs.ExceptionObject}";
+ var caption = "Debug Error";
+ var message =
+ $"Couldn't inject.\nMake sure that Dalamud was not injected into your target process " +
+ $"as a release build before and that the target process can be accessed with VM_WRITE permissions.\n\n" +
+ $"{eventArgs.ExceptionObject}";
#else
- var caption = "XIVLauncher Error";
- var message =
- "Failed to inject the XIVLauncher in-game addon.\nPlease try restarting your game and your PC.\n" +
- "If this keeps happening, please report this error.";
+ var caption = "XIVLauncher Error";
+ var message =
+ "Failed to inject the XIVLauncher in-game addon.\nPlease try restarting your game and your PC.\n" +
+ "If this keeps happening, please report this error.";
#endif
- _ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
+ _ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
- Environment.Exit(0);
- };
- }
+ Environment.Exit(0);
+ };
+ }
- private static void InitLogging()
- {
- var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ private static void InitLogging()
+ {
+ var baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
#if DEBUG
- var logPath = Path.Combine(baseDirectory, "dalamud.injector.log");
+ var logPath = Path.Combine(baseDirectory, "dalamud.injector.log");
#else
var logPath = Path.Combine(baseDirectory, "..", "..", "..", "dalamud.injector.log");
#endif
- var levelSwitch = new LoggingLevelSwitch();
+ var levelSwitch = new LoggingLevelSwitch();
#if DEBUG
- levelSwitch.MinimumLevel = LogEventLevel.Verbose;
+ levelSwitch.MinimumLevel = LogEventLevel.Verbose;
#else
levelSwitch.MinimumLevel = LogEventLevel.Information;
#endif
- CullLogFile(logPath, 1 * 1024 * 1024);
+ CullLogFile(logPath, 1 * 1024 * 1024);
- Log.Logger = new LoggerConfiguration()
- .WriteTo.Async(a => a.File(logPath))
- .MinimumLevel.ControlledBy(levelSwitch)
- .CreateLogger();
- }
+ Log.Logger = new LoggerConfiguration()
+ .WriteTo.Async(a => a.File(logPath))
+ .MinimumLevel.ControlledBy(levelSwitch)
+ .CreateLogger();
+ }
- private static void CullLogFile(string logPath, int cullingFileSize)
+ private static void CullLogFile(string logPath, int cullingFileSize)
+ {
+ try
{
- try
- {
- var bufferSize = 4096;
+ var bufferSize = 4096;
- var logFile = new FileInfo(logPath);
+ var logFile = new FileInfo(logPath);
- if (!logFile.Exists)
- logFile.Create();
+ if (!logFile.Exists)
+ logFile.Create();
- if (logFile.Length <= cullingFileSize)
- return;
-
- var amountToCull = logFile.Length - cullingFileSize;
-
- if (amountToCull < bufferSize)
- return;
-
- using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
- using var writer = new BinaryWriter(logFile.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite));
-
- reader.BaseStream.Seek(amountToCull, SeekOrigin.Begin);
-
- var read = -1;
- var total = 0;
- var buffer = new byte[bufferSize];
- while (read != 0)
- {
- read = reader.Read(buffer, 0, buffer.Length);
- writer.Write(buffer, 0, read);
- total += read;
- }
-
- writer.BaseStream.SetLength(total);
- }
- catch (Exception)
- {
- /*
- var caption = "XIVLauncher Error";
- var message = $"Log cull threw an exception: {ex.Message}\n{ex.StackTrace ?? string.Empty}";
- _ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
- */
- }
- }
-
- private static Process GetProcess(string arg)
- {
- Process process;
-
- var pid = -1;
- if (arg != default)
- {
- pid = int.Parse(arg);
- }
-
- switch (pid)
- {
- case -1:
- process = Process.GetProcessesByName("ffxiv_dx11").FirstOrDefault();
-
- if (process == default)
- {
- throw new Exception("Could not find process");
- }
-
- break;
- case -2:
- var exePath = "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe";
- var exeArgs = new StringBuilder()
- .Append("DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 ")
- .Append("DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 ")
- .Append("DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 ")
- .Append("DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 ")
- .Append("DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 ")
- .Append("DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 ")
- .Append("DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 ")
- .Append("DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 ")
- .Append("DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 ")
- .Append("SYS.Region=0 language=1 version=1.0.0.0 ")
- .Append("DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0").ToString();
- process = Process.Start(exePath, exeArgs);
- Thread.Sleep(1000);
- break;
- default:
- process = Process.GetProcessById(pid);
- break;
- }
-
- return process;
- }
-
- private static DalamudStartInfo GetStartInfo(string arg, Process process)
- {
- DalamudStartInfo startInfo;
-
- if (arg != default)
- {
- startInfo = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(arg)));
- }
- else
- {
- var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName);
- var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
- var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
-
- var gameVerStr = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver"));
- var gameVer = GameVersion.Parse(gameVerStr);
-
- startInfo = new DalamudStartInfo
- {
- WorkingDirectory = null,
- ConfigurationPath = Path.Combine(xivlauncherDir, "dalamudConfig.json"),
- PluginDirectory = Path.Combine(xivlauncherDir, "installedPlugins"),
- DefaultPluginDirectory = Path.Combine(xivlauncherDir, "devPlugins"),
- AssetDirectory = Path.Combine(xivlauncherDir, "dalamudAssets"),
- GameVersion = gameVer,
- Language = ClientLanguage.English,
- OptOutMbCollection = false,
- };
-
- Log.Debug(
- "Creating a new StartInfo with:\n" +
- $" WorkingDirectory: {startInfo.WorkingDirectory}\n" +
- $" ConfigurationPath: {startInfo.ConfigurationPath}\n" +
- $" PluginDirectory: {startInfo.PluginDirectory}\n" +
- $" DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" +
- $" AssetDirectory: {startInfo.AssetDirectory}\n" +
- $" GameVersion: {startInfo.GameVersion}\n" +
- $" Language: {startInfo.Language}\n" +
- $" OptOutMbCollection: {startInfo.OptOutMbCollection}");
-
- Log.Information("A Dalamud start info was not found in the program arguments. One has been generated for you.");
- Log.Information("Copy the following contents into the program arguments:");
-
- var startInfoJson = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo)));
- Log.Information(startInfoJson);
- }
-
- return startInfo;
- }
-
- private static void Inject(Process process, DalamudStartInfo startInfo)
- {
- var nethostName = "nethost.dll";
- var bootName = "Dalamud.Boot.dll";
-
- var nethostPath = Path.GetFullPath(nethostName);
- var bootPath = Path.GetFullPath(bootName);
-
- // ======================================================
-
- using var injector = new Injector(process);
-
- injector.LoadLibrary(nethostPath, out _);
- injector.LoadLibrary(bootPath, out var bootModule);
-
- // ======================================================
-
- var startInfoJson = JsonConvert.SerializeObject(startInfo);
- var startInfoBytes = Encoding.UTF8.GetBytes(startInfoJson);
-
- using var startInfoBuffer = new MemoryBufferHelper(process).CreatePrivateMemoryBuffer(startInfoBytes.Length + 0x8);
- var startInfoAddress = startInfoBuffer.Add(startInfoBytes);
-
- if (startInfoAddress == IntPtr.Zero)
- throw new Exception("Unable to allocate start info JSON");
-
- injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress);
- injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode);
-
- // ======================================================
-
- if (exitCode > 0)
- {
- Log.Error($"Dalamud.Boot::Initialize returned {exitCode}");
+ if (logFile.Length <= cullingFileSize)
return;
+
+ var amountToCull = logFile.Length - cullingFileSize;
+
+ if (amountToCull < bufferSize)
+ return;
+
+ using var reader = new BinaryReader(logFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite));
+ using var writer = new BinaryWriter(logFile.Open(FileMode.Open, FileAccess.Write, FileShare.ReadWrite));
+
+ reader.BaseStream.Seek(amountToCull, SeekOrigin.Begin);
+
+ var read = -1;
+ var total = 0;
+ var buffer = new byte[bufferSize];
+ while (read != 0)
+ {
+ read = reader.Read(buffer, 0, buffer.Length);
+ writer.Write(buffer, 0, read);
+ total += read;
}
- Log.Information("Done");
+ writer.BaseStream.SetLength(total);
+ }
+ catch (Exception)
+ {
+ /*
+ var caption = "XIVLauncher Error";
+ var message = $"Log cull threw an exception: {ex.Message}\n{ex.StackTrace ?? string.Empty}";
+ _ = MessageBoxW(IntPtr.Zero, message, caption, MessageBoxType.IconError | MessageBoxType.Ok);
+ */
}
}
+
+ private static Process GetProcess(string arg)
+ {
+ Process process;
+
+ var pid = -1;
+ if (arg != default)
+ {
+ pid = int.Parse(arg);
+ }
+
+ switch (pid)
+ {
+ case -1:
+ process = Process.GetProcessesByName("ffxiv_dx11").FirstOrDefault();
+
+ if (process == default)
+ {
+ throw new Exception("Could not find process");
+ }
+
+ break;
+ case -2:
+ var exePath = "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn\\game\\ffxiv_dx11.exe";
+ var exeArgs = new StringBuilder()
+ .Append("DEV.TestSID=0 DEV.UseSqPack=1 DEV.DataPathType=1 ")
+ .Append("DEV.LobbyHost01=127.0.0.1 DEV.LobbyPort01=54994 ")
+ .Append("DEV.LobbyHost02=127.0.0.1 DEV.LobbyPort02=54994 ")
+ .Append("DEV.LobbyHost03=127.0.0.1 DEV.LobbyPort03=54994 ")
+ .Append("DEV.LobbyHost04=127.0.0.1 DEV.LobbyPort04=54994 ")
+ .Append("DEV.LobbyHost05=127.0.0.1 DEV.LobbyPort05=54994 ")
+ .Append("DEV.LobbyHost06=127.0.0.1 DEV.LobbyPort06=54994 ")
+ .Append("DEV.LobbyHost07=127.0.0.1 DEV.LobbyPort07=54994 ")
+ .Append("DEV.LobbyHost08=127.0.0.1 DEV.LobbyPort08=54994 ")
+ .Append("SYS.Region=0 language=1 version=1.0.0.0 ")
+ .Append("DEV.MaxEntitledExpansionID=2 DEV.GMServerHost=127.0.0.1 DEV.GameQuitMessageBox=0").ToString();
+ process = Process.Start(exePath, exeArgs);
+ Thread.Sleep(1000);
+ break;
+ default:
+ process = Process.GetProcessById(pid);
+ break;
+ }
+
+ return process;
+ }
+
+ private static DalamudStartInfo GetStartInfo(string arg, Process process)
+ {
+ DalamudStartInfo startInfo;
+
+ if (arg != default)
+ {
+ startInfo = JsonConvert.DeserializeObject(Encoding.UTF8.GetString(Convert.FromBase64String(arg)));
+ }
+ else
+ {
+ var ffxivDir = Path.GetDirectoryName(process.MainModule.FileName);
+ var appDataDir = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
+ var xivlauncherDir = Path.Combine(appDataDir, "XIVLauncher");
+
+ var gameVerStr = File.ReadAllText(Path.Combine(ffxivDir, "ffxivgame.ver"));
+ var gameVer = GameVersion.Parse(gameVerStr);
+
+ startInfo = new DalamudStartInfo
+ {
+ WorkingDirectory = null,
+ ConfigurationPath = Path.Combine(xivlauncherDir, "dalamudConfig.json"),
+ PluginDirectory = Path.Combine(xivlauncherDir, "installedPlugins"),
+ DefaultPluginDirectory = Path.Combine(xivlauncherDir, "devPlugins"),
+ AssetDirectory = Path.Combine(xivlauncherDir, "dalamudAssets"),
+ GameVersion = gameVer,
+ Language = ClientLanguage.English,
+ OptOutMbCollection = false,
+ };
+
+ Log.Debug(
+ "Creating a new StartInfo with:\n" +
+ $" WorkingDirectory: {startInfo.WorkingDirectory}\n" +
+ $" ConfigurationPath: {startInfo.ConfigurationPath}\n" +
+ $" PluginDirectory: {startInfo.PluginDirectory}\n" +
+ $" DefaultPluginDirectory: {startInfo.DefaultPluginDirectory}\n" +
+ $" AssetDirectory: {startInfo.AssetDirectory}\n" +
+ $" GameVersion: {startInfo.GameVersion}\n" +
+ $" Language: {startInfo.Language}\n" +
+ $" OptOutMbCollection: {startInfo.OptOutMbCollection}");
+
+ Log.Information("A Dalamud start info was not found in the program arguments. One has been generated for you.");
+ Log.Information("Copy the following contents into the program arguments:");
+
+ var startInfoJson = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(startInfo)));
+ Log.Information(startInfoJson);
+ }
+
+ return startInfo;
+ }
+
+ private static void Inject(Process process, DalamudStartInfo startInfo)
+ {
+ var nethostName = "nethost.dll";
+ var bootName = "Dalamud.Boot.dll";
+
+ var nethostPath = Path.GetFullPath(nethostName);
+ var bootPath = Path.GetFullPath(bootName);
+
+ // ======================================================
+
+ using var injector = new Injector(process);
+
+ injector.LoadLibrary(nethostPath, out _);
+ injector.LoadLibrary(bootPath, out var bootModule);
+
+ // ======================================================
+
+ var startInfoJson = JsonConvert.SerializeObject(startInfo);
+ var startInfoBytes = Encoding.UTF8.GetBytes(startInfoJson);
+
+ using var startInfoBuffer = new MemoryBufferHelper(process).CreatePrivateMemoryBuffer(startInfoBytes.Length + 0x8);
+ var startInfoAddress = startInfoBuffer.Add(startInfoBytes);
+
+ if (startInfoAddress == IntPtr.Zero)
+ throw new Exception("Unable to allocate start info JSON");
+
+ injector.GetFunctionAddress(bootModule, "Initialize", out var initAddress);
+ injector.CallRemoteFunction(initAddress, startInfoAddress, out var exitCode);
+
+ // ======================================================
+
+ if (exitCode > 0)
+ {
+ Log.Error($"Dalamud.Boot::Initialize returned {exitCode}");
+ return;
+ }
+
+ Log.Information("Done");
+ }
}
diff --git a/Dalamud.Injector/Injector.cs b/Dalamud.Injector/Injector.cs
index 20a668047..11a497d12 100644
--- a/Dalamud.Injector/Injector.cs
+++ b/Dalamud.Injector/Injector.cs
@@ -16,344 +16,343 @@ using Serilog;
using static Dalamud.Injector.NativeFunctions;
using static Iced.Intel.AssemblerRegisters;
-namespace Dalamud.Injector
+namespace Dalamud.Injector;
+
+///
+/// This class implements injecting into a remote process. It is a highly stripped down version of the
+/// https://github.com/Reloaded-Project injector/assembler implementation due to issues with Lutris and
+/// Wine.
+///
+internal sealed class Injector : IDisposable
{
+ private readonly Process targetProcess;
+ private readonly ExternalMemory extMemory;
+ private readonly CircularBuffer circularBuffer;
+ private readonly PrivateMemoryBuffer memoryBuffer;
+
+ private IntPtr loadLibraryShellPtr;
+ private IntPtr loadLibraryRetPtr;
+
+ private IntPtr getProcAddressShellPtr;
+ private IntPtr getProcAddressRetPtr;
+
///
- /// This class implements injecting into a remote process. It is a highly stripped down version of the
- /// https://github.com/Reloaded-Project injector/assembler implementation due to issues with Lutris and
- /// Wine.
+ /// Initializes a new instance of the class.
///
- internal sealed class Injector : IDisposable
+ /// Process to inject.
+ public Injector(Process targetProcess)
{
- private readonly Process targetProcess;
- private readonly ExternalMemory extMemory;
- private readonly CircularBuffer circularBuffer;
- private readonly PrivateMemoryBuffer memoryBuffer;
+ this.targetProcess = targetProcess;
- private IntPtr loadLibraryShellPtr;
- private IntPtr loadLibraryRetPtr;
+ this.extMemory = new ExternalMemory(targetProcess);
+ this.circularBuffer = new CircularBuffer(4096, this.extMemory);
+ this.memoryBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096);
- private IntPtr getProcAddressShellPtr;
- private IntPtr getProcAddressRetPtr;
+ using var kernel32Module = this.GetProcessModule("KERNEL32.DLL");
+ var kernel32PeFile = new PeFile(kernel32Module.FileName);
+ var kernel32Exports = kernel32PeFile.ExportedFunctions;
- ///
- /// Initializes a new instance of the class.
- ///
- /// Process to inject.
- public Injector(Process targetProcess)
- {
- this.targetProcess = targetProcess;
+ this.SetupLoadLibrary(kernel32Module, kernel32Exports);
+ this.SetupGetProcAddress(kernel32Module, kernel32Exports);
+ }
- this.extMemory = new ExternalMemory(targetProcess);
- this.circularBuffer = new CircularBuffer(4096, this.extMemory);
- this.memoryBuffer = new MemoryBufferHelper(targetProcess).CreatePrivateMemoryBuffer(4096);
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~Injector() => this.Dispose();
- using var kernel32Module = this.GetProcessModule("KERNEL32.DLL");
- var kernel32PeFile = new PeFile(kernel32Module.FileName);
- var kernel32Exports = kernel32PeFile.ExportedFunctions;
+ ///
+ public void Dispose()
+ {
+ GC.SuppressFinalize(this);
- this.SetupLoadLibrary(kernel32Module, kernel32Exports);
- this.SetupGetProcAddress(kernel32Module, kernel32Exports);
- }
+ this.targetProcess?.Dispose();
+ this.circularBuffer?.Dispose();
+ this.memoryBuffer?.Dispose();
+ }
- ///
- /// Finalizes an instance of the class.
- ///
- ~Injector() => this.Dispose();
+ ///
+ /// Load a module by absolute file path.
+ ///
+ /// Absolute file path.
+ /// Address to the module.
+ public void LoadLibrary(string modulePath, out IntPtr address)
+ {
+ var lpParameter = this.WriteNullTerminatedUnicodeString(modulePath);
- ///
- public void Dispose()
- {
- GC.SuppressFinalize(this);
+ if (lpParameter == IntPtr.Zero)
+ throw new Exception("Unable to allocate LoadLibraryW parameter");
- this.targetProcess?.Dispose();
- this.circularBuffer?.Dispose();
- this.memoryBuffer?.Dispose();
- }
+ Log.Verbose($"CreateRemoteThread: call 0x{this.loadLibraryShellPtr.ToInt64():X} with 0x{lpParameter.ToInt64():X}");
- ///
- /// Load a module by absolute file path.
- ///
- /// Absolute file path.
- /// Address to the module.
- public void LoadLibrary(string modulePath, out IntPtr address)
- {
- var lpParameter = this.WriteNullTerminatedUnicodeString(modulePath);
+ var threadHandle = CreateRemoteThread(
+ this.targetProcess.Handle,
+ IntPtr.Zero,
+ UIntPtr.Zero,
+ this.loadLibraryShellPtr,
+ lpParameter,
+ CreateThreadFlags.RunImmediately,
+ out _);
- if (lpParameter == IntPtr.Zero)
- throw new Exception("Unable to allocate LoadLibraryW parameter");
+ _ = WaitForSingleObject(threadHandle, uint.MaxValue);
- Log.Verbose($"CreateRemoteThread: call 0x{this.loadLibraryShellPtr.ToInt64():X} with 0x{lpParameter.ToInt64():X}");
+ address = this.extMemory.Read(this.loadLibraryRetPtr);
- var threadHandle = CreateRemoteThread(
- this.targetProcess.Handle,
- IntPtr.Zero,
- UIntPtr.Zero,
- this.loadLibraryShellPtr,
- lpParameter,
- CreateThreadFlags.RunImmediately,
- out _);
+ if (address == IntPtr.Zero)
+ throw new Exception($"Error calling LoadLibraryW with {modulePath}");
+ }
- _ = WaitForSingleObject(threadHandle, uint.MaxValue);
+ ///
+ /// Get the address of an exported module function.
+ ///
+ /// Module address.
+ /// Name of the exported method.
+ /// Address to the function.
+ public void GetFunctionAddress(IntPtr module, string functionName, out IntPtr address)
+ {
+ var functionNamePtr = this.WriteNullTerminatedASCIIString(functionName);
+ var getProcAddressParams = new GetProcAddressParams(module, functionNamePtr);
+ var lpParameter = this.circularBuffer.Add(ref getProcAddressParams);
- address = this.extMemory.Read(this.loadLibraryRetPtr);
+ if (lpParameter == IntPtr.Zero)
+ throw new Exception("Unable to allocate GetProcAddress parameter ptr");
- if (address == IntPtr.Zero)
- throw new Exception($"Error calling LoadLibraryW with {modulePath}");
- }
+ var threadHandle = CreateRemoteThread(
+ this.targetProcess.Handle,
+ IntPtr.Zero,
+ UIntPtr.Zero,
+ this.getProcAddressShellPtr,
+ lpParameter,
+ CreateThreadFlags.RunImmediately,
+ out _);
- ///
- /// Get the address of an exported module function.
- ///
- /// Module address.
- /// Name of the exported method.
- /// Address to the function.
- public void GetFunctionAddress(IntPtr module, string functionName, out IntPtr address)
- {
- var functionNamePtr = this.WriteNullTerminatedASCIIString(functionName);
- var getProcAddressParams = new GetProcAddressParams(module, functionNamePtr);
- var lpParameter = this.circularBuffer.Add(ref getProcAddressParams);
+ _ = WaitForSingleObject(threadHandle, uint.MaxValue);
- if (lpParameter == IntPtr.Zero)
- throw new Exception("Unable to allocate GetProcAddress parameter ptr");
+ this.extMemory.Read(this.getProcAddressRetPtr, out address);
- var threadHandle = CreateRemoteThread(
- this.targetProcess.Handle,
- IntPtr.Zero,
- UIntPtr.Zero,
- this.getProcAddressShellPtr,
- lpParameter,
- CreateThreadFlags.RunImmediately,
- out _);
+ if (address == IntPtr.Zero)
+ throw new Exception($"Error calling GetProcAddress with {functionName}");
+ }
- _ = WaitForSingleObject(threadHandle, uint.MaxValue);
+ ///
+ /// Call a method in a remote process via CreateRemoteThread.
+ ///
+ /// Method address.
+ /// Parameter address.
+ /// Thread exit code.
+ public void CallRemoteFunction(IntPtr methodAddress, IntPtr parameterAddress, out uint exitCode)
+ {
+ // Create and initialize a thread at our address and parameter address.
+ var threadHandle = CreateRemoteThread(
+ this.targetProcess.Handle,
+ IntPtr.Zero,
+ UIntPtr.Zero,
+ methodAddress,
+ parameterAddress,
+ CreateThreadFlags.RunImmediately,
+ out _);
- this.extMemory.Read(this.getProcAddressRetPtr, out address);
+ _ = WaitForSingleObject(threadHandle, uint.MaxValue);
- if (address == IntPtr.Zero)
- throw new Exception($"Error calling GetProcAddress with {functionName}");
- }
+ GetExitCodeThread(threadHandle, out exitCode);
- ///
- /// Call a method in a remote process via CreateRemoteThread.
- ///
- /// Method address.
- /// Parameter address.
- /// Thread exit code.
- public void CallRemoteFunction(IntPtr methodAddress, IntPtr parameterAddress, out uint exitCode)
- {
- // Create and initialize a thread at our address and parameter address.
- var threadHandle = CreateRemoteThread(
- this.targetProcess.Handle,
- IntPtr.Zero,
- UIntPtr.Zero,
- methodAddress,
- parameterAddress,
- CreateThreadFlags.RunImmediately,
- out _);
+ CloseHandle(threadHandle);
+ }
- _ = WaitForSingleObject(threadHandle, uint.MaxValue);
+ private void SetupLoadLibrary(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
+ {
+ var offset = this.GetExportedFunctionOffset(kernel32Exports, "LoadLibraryW");
+ var functionAddr = kernel32Module.BaseAddress + (int)offset;
+ Log.Verbose($"LoadLibraryW: 0x{functionAddr.ToInt64():X}");
- GetExitCodeThread(threadHandle, out exitCode);
+ var functionPtr = this.memoryBuffer.Add(ref functionAddr);
+ Log.Verbose($"LoadLibraryPtr: 0x{functionPtr.ToInt64():X}");
- CloseHandle(threadHandle);
- }
+ if (functionPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate LoadLibraryW function ptr");
- private void SetupLoadLibrary(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
- {
- var offset = this.GetExportedFunctionOffset(kernel32Exports, "LoadLibraryW");
- var functionAddr = kernel32Module.BaseAddress + (int)offset;
- Log.Verbose($"LoadLibraryW: 0x{functionAddr.ToInt64():X}");
+ var dummy = IntPtr.Zero;
+ this.loadLibraryRetPtr = this.memoryBuffer.Add(ref dummy);
+ Log.Verbose($"LoadLibraryRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
- var functionPtr = this.memoryBuffer.Add(ref functionAddr);
- Log.Verbose($"LoadLibraryPtr: 0x{functionPtr.ToInt64():X}");
+ if (this.loadLibraryRetPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate LoadLibraryW return value");
- if (functionPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate LoadLibraryW function ptr");
+ var func = functionPtr.ToInt64();
+ var retVal = this.loadLibraryRetPtr.ToInt64();
- var dummy = IntPtr.Zero;
- this.loadLibraryRetPtr = this.memoryBuffer.Add(ref dummy);
- Log.Verbose($"LoadLibraryRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
+ var asm = new Assembler(64);
- if (this.loadLibraryRetPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate LoadLibraryW return value");
+ asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
+ asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] // CreateRemoteThread lpParameter with string already in ECX.
+ asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal], rax //
+ asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
+ asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
- var func = functionPtr.ToInt64();
- var retVal = this.loadLibraryRetPtr.ToInt64();
+ var bytes = this.Assemble(asm);
+ this.loadLibraryShellPtr = this.memoryBuffer.Add(bytes);
+ Log.Verbose($"LoadLibraryShellPtr: 0x{this.loadLibraryShellPtr.ToInt64():X}");
- var asm = new Assembler(64);
+ if (this.loadLibraryShellPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate LoadLibraryW shellcode");
- asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
- asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] // CreateRemoteThread lpParameter with string already in ECX.
- asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal], rax //
- asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
- asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
-
- var bytes = this.Assemble(asm);
- this.loadLibraryShellPtr = this.memoryBuffer.Add(bytes);
- Log.Verbose($"LoadLibraryShellPtr: 0x{this.loadLibraryShellPtr.ToInt64():X}");
-
- if (this.loadLibraryShellPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate LoadLibraryW shellcode");
-
- this.extMemory.ChangePermission(this.loadLibraryShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
+ this.extMemory.ChangePermission(this.loadLibraryShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
#if DEBUG
- var outFunctionPtr = this.extMemory.Read(functionPtr);
- Log.Verbose($"LoadLibraryPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
+ var outFunctionPtr = this.extMemory.Read(functionPtr);
+ Log.Verbose($"LoadLibraryPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
- var outRetPtr = this.extMemory.Read(this.loadLibraryRetPtr);
- Log.Verbose($"LoadLibraryRet: {this.GetResultMarker(dummy == outRetPtr)}");
+ var outRetPtr = this.extMemory.Read(this.loadLibraryRetPtr);
+ Log.Verbose($"LoadLibraryRet: {this.GetResultMarker(dummy == outRetPtr)}");
- this.extMemory.ReadRaw(this.loadLibraryShellPtr, out var outBytes, bytes.Length);
- Log.Verbose($"LoadLibraryShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
+ this.extMemory.ReadRaw(this.loadLibraryShellPtr, out var outBytes, bytes.Length);
+ Log.Verbose($"LoadLibraryShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
#endif
- }
+ }
- private void SetupGetProcAddress(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
- {
- var offset = this.GetExportedFunctionOffset(kernel32Exports, "GetProcAddress");
- var functionAddr = kernel32Module.BaseAddress + (int)offset;
- Log.Verbose($"GetProcAddress: 0x{functionAddr.ToInt64():X}");
+ private void SetupGetProcAddress(ProcessModule kernel32Module, ExportFunction[] kernel32Exports)
+ {
+ var offset = this.GetExportedFunctionOffset(kernel32Exports, "GetProcAddress");
+ var functionAddr = kernel32Module.BaseAddress + (int)offset;
+ Log.Verbose($"GetProcAddress: 0x{functionAddr.ToInt64():X}");
- var functionPtr = this.memoryBuffer.Add(ref functionAddr);
- Log.Verbose($"GetProcAddressPtr: 0x{functionPtr.ToInt64():X}");
+ var functionPtr = this.memoryBuffer.Add(ref functionAddr);
+ Log.Verbose($"GetProcAddressPtr: 0x{functionPtr.ToInt64():X}");
- if (functionPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate GetProcAddress function ptr");
+ if (functionPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate GetProcAddress function ptr");
- var dummy = IntPtr.Zero;
- this.getProcAddressRetPtr = this.memoryBuffer.Add(ref dummy);
- Log.Verbose($"GetProcAddressRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
+ var dummy = IntPtr.Zero;
+ this.getProcAddressRetPtr = this.memoryBuffer.Add(ref dummy);
+ Log.Verbose($"GetProcAddressRetPtr: 0x{this.loadLibraryRetPtr.ToInt64():X}");
- if (this.getProcAddressRetPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate GetProcAddress return value");
+ if (this.getProcAddressRetPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate GetProcAddress return value");
- var func = functionPtr.ToInt64();
- var retVal = this.getProcAddressRetPtr.ToInt64();
+ var func = functionPtr.ToInt64();
+ var retVal = this.getProcAddressRetPtr.ToInt64();
- var asm = new Assembler(64);
+ var asm = new Assembler(64);
- asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary +32 shadow space
- asm.mov(rdx, __qword_ptr[__qword_ptr[rcx + 8]]); // mov rdx, qword [qword rcx + 8] // lpProcName
- asm.mov(rcx, __qword_ptr[__qword_ptr[rcx + 0]]); // mov rcx, qword [qword rcx + 0] // hModule
- asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] //
- asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal] //
- asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
- asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
+ asm.sub(rsp, 40); // sub rsp, 40 // Re-align stack to 16 byte boundary +32 shadow space
+ asm.mov(rdx, __qword_ptr[__qword_ptr[rcx + 8]]); // mov rdx, qword [qword rcx + 8] // lpProcName
+ asm.mov(rcx, __qword_ptr[__qword_ptr[rcx + 0]]); // mov rcx, qword [qword rcx + 0] // hModule
+ asm.call(__qword_ptr[__qword_ptr[func]]); // call qword [qword func] //
+ asm.mov(__qword_ptr[__qword_ptr[retVal]], rax); // mov qword [qword retVal] //
+ asm.add(rsp, 40); // add rsp, 40 // Re-align stack to 16 byte boundary + shadow space.
+ asm.ret(); // ret // Restore stack ptr. (Callee cleanup)
- var bytes = this.Assemble(asm);
- this.getProcAddressShellPtr = this.memoryBuffer.Add(bytes);
- Log.Verbose($"GetProcAddressShellPtr: 0x{this.getProcAddressShellPtr.ToInt64():X}");
+ var bytes = this.Assemble(asm);
+ this.getProcAddressShellPtr = this.memoryBuffer.Add(bytes);
+ Log.Verbose($"GetProcAddressShellPtr: 0x{this.getProcAddressShellPtr.ToInt64():X}");
- if (this.getProcAddressShellPtr == IntPtr.Zero)
- throw new Exception("Unable to allocate GetProcAddress shellcode");
+ if (this.getProcAddressShellPtr == IntPtr.Zero)
+ throw new Exception("Unable to allocate GetProcAddress shellcode");
- this.extMemory.ChangePermission(this.getProcAddressShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
+ this.extMemory.ChangePermission(this.getProcAddressShellPtr, bytes.Length, Reloaded.Memory.Kernel32.Kernel32.MEM_PROTECTION.PAGE_EXECUTE_READWRITE);
#if DEBUG
- var outFunctionPtr = this.extMemory.Read(functionPtr);
- Log.Verbose($"GetProcAddressPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
+ var outFunctionPtr = this.extMemory.Read(functionPtr);
+ Log.Verbose($"GetProcAddressPtr: {this.GetResultMarker(outFunctionPtr == functionAddr)}");
- var outRetPtr = this.extMemory.Read(this.loadLibraryRetPtr);
- Log.Verbose($"GetProcAddressRet: {this.GetResultMarker(dummy == outRetPtr)}");
+ var outRetPtr = this.extMemory.Read(this.loadLibraryRetPtr);
+ Log.Verbose($"GetProcAddressRet: {this.GetResultMarker(dummy == outRetPtr)}");
- this.extMemory.ReadRaw(this.getProcAddressShellPtr, out var outBytes, bytes.Length);
- Log.Verbose($"GetProcAddressShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
+ this.extMemory.ReadRaw(this.getProcAddressShellPtr, out var outBytes, bytes.Length);
+ Log.Verbose($"GetProcAddressShellPtr: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))}");
#endif
+ }
+
+ private byte[] Assemble(Assembler assembler)
+ {
+ using var stream = new MemoryStream();
+ assembler.Assemble(new StreamCodeWriter(stream), 0);
+
+ stream.Position = 0;
+ var reader = new StreamCodeReader(stream);
+
+ int next;
+ var bytes = new byte[stream.Length];
+ while ((next = reader.ReadByte()) >= 0)
+ {
+ bytes[stream.Position - 1] = (byte)next;
}
- private byte[] Assemble(Assembler assembler)
+ return bytes;
+ }
+
+ private ProcessModule GetProcessModule(string moduleName)
+ {
+ var modules = this.targetProcess.Modules;
+ for (var i = 0; i < modules.Count; i++)
{
- using var stream = new MemoryStream();
- assembler.Assemble(new StreamCodeWriter(stream), 0);
-
- stream.Position = 0;
- var reader = new StreamCodeReader(stream);
-
- int next;
- var bytes = new byte[stream.Length];
- while ((next = reader.ReadByte()) >= 0)
+ var module = modules[i];
+ if (module.ModuleName.Equals(moduleName, StringComparison.InvariantCultureIgnoreCase))
{
- bytes[stream.Position - 1] = (byte)next;
+ return module;
}
-
- return bytes;
}
- private ProcessModule GetProcessModule(string moduleName)
- {
- var modules = this.targetProcess.Modules;
- for (var i = 0; i < modules.Count; i++)
- {
- var module = modules[i];
- if (module.ModuleName.Equals(moduleName, StringComparison.InvariantCultureIgnoreCase))
- {
- return module;
- }
- }
+ throw new Exception($"Failed to find {moduleName} in target process' modules");
+ }
- throw new Exception($"Failed to find {moduleName} in target process' modules");
- }
+ private uint GetExportedFunctionOffset(ExportFunction[] exportFunctions, string functionName)
+ {
+ var exportFunction = exportFunctions.FirstOrDefault(func => func.Name == functionName);
- private uint GetExportedFunctionOffset(ExportFunction[] exportFunctions, string functionName)
- {
- var exportFunction = exportFunctions.FirstOrDefault(func => func.Name == functionName);
+ if (exportFunction == default)
+ throw new Exception($"Failed to find exported function {functionName} in target module's exports");
- if (exportFunction == default)
- throw new Exception($"Failed to find exported function {functionName} in target module's exports");
+ return exportFunction.Address;
+ }
- return exportFunction.Address;
- }
+ private IntPtr WriteNullTerminatedASCIIString(string value)
+ {
+ var bytes = Encoding.ASCII.GetBytes(value + '\0');
+ var address = this.circularBuffer.Add(bytes);
- private IntPtr WriteNullTerminatedASCIIString(string value)
- {
- var bytes = Encoding.ASCII.GetBytes(value + '\0');
- var address = this.circularBuffer.Add(bytes);
-
- if (address == IntPtr.Zero)
- throw new Exception("Unable to write ASCII string to buffer");
+ if (address == IntPtr.Zero)
+ throw new Exception("Unable to write ASCII string to buffer");
#if DEBUG
- this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
- Log.Verbose($"WriteASCII: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
+ this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
+ Log.Verbose($"WriteASCII: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
#endif
- return address;
- }
+ return address;
+ }
- private IntPtr WriteNullTerminatedUnicodeString(string value)
- {
- var bytes = Encoding.Unicode.GetBytes(value + '\0');
- var address = this.circularBuffer.Add(bytes);
+ private IntPtr WriteNullTerminatedUnicodeString(string value)
+ {
+ var bytes = Encoding.Unicode.GetBytes(value + '\0');
+ var address = this.circularBuffer.Add(bytes);
- if (address == IntPtr.Zero)
- throw new Exception("Unable to write Unicode string to buffer");
+ if (address == IntPtr.Zero)
+ throw new Exception("Unable to write Unicode string to buffer");
#if DEBUG
- this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
- Log.Verbose($"WriteUnicode: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
+ this.extMemory.ReadRaw(address, out var outBytes, bytes.Length);
+ Log.Verbose($"WriteUnicode: {this.GetResultMarker(Enumerable.SequenceEqual(bytes, outBytes))} 0x{address.ToInt64():X} {value}");
#endif
- return address;
- }
+ return address;
+ }
#if DEBUG
- private string GetResultMarker(bool result) => result ? "✅" : "❌";
+ private string GetResultMarker(bool result) => result ? "✅" : "❌";
#endif
- [StructLayout(LayoutKind.Sequential)]
- private struct GetProcAddressParams
+ [StructLayout(LayoutKind.Sequential)]
+ private struct GetProcAddressParams
+ {
+ public GetProcAddressParams(IntPtr hModule, IntPtr lPProcName)
{
- public GetProcAddressParams(IntPtr hModule, IntPtr lPProcName)
- {
- this.HModule = hModule.ToInt64();
- this.LPProcName = lPProcName.ToInt64();
- }
-
- public long HModule { get; set; }
-
- public long LPProcName { get; set; }
+ this.HModule = hModule.ToInt64();
+ this.LPProcName = lPProcName.ToInt64();
}
+
+ public long HModule { get; set; }
+
+ public long LPProcName { get; set; }
}
}
diff --git a/Dalamud.Injector/NativeFunctions.cs b/Dalamud.Injector/NativeFunctions.cs
index b9ed9f33d..82d641181 100644
--- a/Dalamud.Injector/NativeFunctions.cs
+++ b/Dalamud.Injector/NativeFunctions.cs
@@ -1,837 +1,836 @@
using System;
using System.Runtime.InteropServices;
-namespace Dalamud.Injector
+namespace Dalamud.Injector;
+
+///
+/// Native user32 functions.
+///
+internal static partial class NativeFunctions
{
///
- /// Native user32 functions.
+ /// MB_* from winuser.
///
- internal static partial class NativeFunctions
+ public enum MessageBoxType : uint
{
///
- /// MB_* from winuser.
+ /// The default value for any of the various subtypes.
///
- public enum MessageBoxType : uint
- {
- ///
- /// The default value for any of the various subtypes.
- ///
- DefaultValue = 0x0,
+ DefaultValue = 0x0,
- // To indicate the buttons displayed in the message box, specify one of the following values.
-
- ///
- /// The message box contains three push buttons: Abort, Retry, and Ignore.
- ///
- AbortRetryIgnore = 0x2,
-
- ///
- /// The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead
- /// of MB_ABORTRETRYIGNORE.
- ///
- CancelTryContinue = 0x6,
-
- ///
- /// Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends
- /// a WM_HELP message to the owner.
- ///
- Help = 0x4000,
-
- ///
- /// The message box contains one push button: OK. This is the default.
- ///
- Ok = DefaultValue,
-
- ///
- /// The message box contains two push buttons: OK and Cancel.
- ///
- OkCancel = 0x1,
-
- ///
- /// The message box contains two push buttons: Retry and Cancel.
- ///
- RetryCancel = 0x5,
-
- ///
- /// The message box contains two push buttons: Yes and No.
- ///
- YesNo = 0x4,
-
- ///
- /// The message box contains three push buttons: Yes, No, and Cancel.
- ///
- YesNoCancel = 0x3,
-
- // To display an icon in the message box, specify one of the following values.
-
- ///
- /// An exclamation-point icon appears in the message box.
- ///
- IconExclamation = 0x30,
-
- ///
- /// An exclamation-point icon appears in the message box.
- ///
- IconWarning = IconExclamation,
-
- ///
- /// An icon consisting of a lowercase letter i in a circle appears in the message box.
- ///
- IconInformation = 0x40,
-
- ///
- /// An icon consisting of a lowercase letter i in a circle appears in the message box.
- ///
- IconAsterisk = IconInformation,
-
- ///
- /// A question-mark icon appears in the message box.
- /// The question-mark message icon is no longer recommended because it does not clearly represent a specific type
- /// of message and because the phrasing of a message as a question could apply to any message type. In addition,
- /// users can confuse the message symbol question mark with Help information. Therefore, do not use this question
- /// mark message symbol in your message boxes. The system continues to support its inclusion only for backward
- /// compatibility.
- ///
- IconQuestion = 0x20,
-
- ///
- /// A stop-sign icon appears in the message box.
- ///
- IconStop = 0x10,
-
- ///
- /// A stop-sign icon appears in the message box.
- ///
- IconError = IconStop,
-
- ///
- /// A stop-sign icon appears in the message box.
- ///
- IconHand = IconStop,
-
- // To indicate the default button, specify one of the following values.
-
- ///
- /// The first button is the default button.
- /// MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.
- ///
- DefButton1 = DefaultValue,
-
- ///
- /// The second button is the default button.
- ///
- DefButton2 = 0x100,
-
- ///
- /// The third button is the default button.
- ///
- DefButton3 = 0x200,
-
- ///
- /// The fourth button is the default button.
- ///
- DefButton4 = 0x300,
-
- // To indicate the modality of the dialog box, specify one of the following values.
-
- ///
- /// The user must respond to the message box before continuing work in the window identified by the hWnd parameter.
- /// However, the user can move to the windows of other threads and work in those windows. Depending on the hierarchy
- /// of windows in the application, the user may be able to move to other windows within the thread. All child windows
- /// of the parent of the message box are automatically disabled, but pop-up windows are not. MB_APPLMODAL is the
- /// default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified.
- ///
- ApplModal = DefaultValue,
-
- ///
- /// Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style.
- /// Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate
- /// attention (for example, running out of memory). This flag has no effect on the user's ability to interact with
- /// windows other than those associated with hWnd.
- ///
- SystemModal = 0x1000,
-
- ///
- /// Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the
- /// hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle
- /// available but still needs to prevent input to other windows in the calling thread without suspending other threads.
- ///
- TaskModal = 0x2000,
-
- // To specify other options, use one or more of the following values.
-
- ///
- /// Same as desktop of the interactive window station. For more information, see Window Stations. If the current
- /// input desktop is not the default desktop, MessageBox does not return until the user switches to the default
- /// desktop.
- ///
- DefaultDesktopOnly = 0x20000,
-
- ///
- /// The text is right-justified.
- ///
- Right = 0x80000,
-
- ///
- /// Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
- ///
- RtlReading = 0x100000,
-
- ///
- /// The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function
- /// for the message box.
- ///
- SetForeground = 0x10000,
-
- ///
- /// The message box is created with the WS_EX_TOPMOST window style.
- ///
- Topmost = 0x40000,
-
- ///
- /// The caller is a service notifying the user of an event. The function displays a message box on the current active
- /// desktop, even if there is no user logged on to the computer.
- ///
- ServiceNotification = 0x200000,
- }
+ // To indicate the buttons displayed in the message box, specify one of the following values.
///
- /// Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message,
- /// such as status or error information. The message box returns an integer value that indicates which button the user
- /// clicked.
+ /// The message box contains three push buttons: Abort, Retry, and Ignore.
///
- ///
- /// A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no
- /// owner window.
- ///
- ///
- /// The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage
- /// return and/or linefeed character between each line.
- ///
- ///
- /// The dialog box title. If this parameter is NULL, the default title is Error.
- ///
- /// The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups
- /// of flags.
- ///
- ///
- /// If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or
- /// the Cancel button is selected. If the message box has no Cancel button, pressing ESC will no effect - unless an
- /// MB_OK button is present. If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK.
- /// If the function fails, the return value is zero.To get extended error information, call GetLastError. If the function
- /// succeeds, the return value is one of the ID* enum values.
- ///
- [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type);
+ AbortRetryIgnore = 0x2,
+
+ ///
+ /// The message box contains three push buttons: Cancel, Try Again, Continue. Use this message box type instead
+ /// of MB_ABORTRETRYIGNORE.
+ ///
+ CancelTryContinue = 0x6,
+
+ ///
+ /// Adds a Help button to the message box. When the user clicks the Help button or presses F1, the system sends
+ /// a WM_HELP message to the owner.
+ ///
+ Help = 0x4000,
+
+ ///
+ /// The message box contains one push button: OK. This is the default.
+ ///
+ Ok = DefaultValue,
+
+ ///
+ /// The message box contains two push buttons: OK and Cancel.
+ ///
+ OkCancel = 0x1,
+
+ ///
+ /// The message box contains two push buttons: Retry and Cancel.
+ ///
+ RetryCancel = 0x5,
+
+ ///
+ /// The message box contains two push buttons: Yes and No.
+ ///
+ YesNo = 0x4,
+
+ ///
+ /// The message box contains three push buttons: Yes, No, and Cancel.
+ ///
+ YesNoCancel = 0x3,
+
+ // To display an icon in the message box, specify one of the following values.
+
+ ///
+ /// An exclamation-point icon appears in the message box.
+ ///
+ IconExclamation = 0x30,
+
+ ///
+ /// An exclamation-point icon appears in the message box.
+ ///
+ IconWarning = IconExclamation,
+
+ ///
+ /// An icon consisting of a lowercase letter i in a circle appears in the message box.
+ ///
+ IconInformation = 0x40,
+
+ ///
+ /// An icon consisting of a lowercase letter i in a circle appears in the message box.
+ ///
+ IconAsterisk = IconInformation,
+
+ ///
+ /// A question-mark icon appears in the message box.
+ /// The question-mark message icon is no longer recommended because it does not clearly represent a specific type
+ /// of message and because the phrasing of a message as a question could apply to any message type. In addition,
+ /// users can confuse the message symbol question mark with Help information. Therefore, do not use this question
+ /// mark message symbol in your message boxes. The system continues to support its inclusion only for backward
+ /// compatibility.
+ ///
+ IconQuestion = 0x20,
+
+ ///
+ /// A stop-sign icon appears in the message box.
+ ///
+ IconStop = 0x10,
+
+ ///
+ /// A stop-sign icon appears in the message box.
+ ///
+ IconError = IconStop,
+
+ ///
+ /// A stop-sign icon appears in the message box.
+ ///
+ IconHand = IconStop,
+
+ // To indicate the default button, specify one of the following values.
+
+ ///
+ /// The first button is the default button.
+ /// MB_DEFBUTTON1 is the default unless MB_DEFBUTTON2, MB_DEFBUTTON3, or MB_DEFBUTTON4 is specified.
+ ///
+ DefButton1 = DefaultValue,
+
+ ///
+ /// The second button is the default button.
+ ///
+ DefButton2 = 0x100,
+
+ ///
+ /// The third button is the default button.
+ ///
+ DefButton3 = 0x200,
+
+ ///
+ /// The fourth button is the default button.
+ ///
+ DefButton4 = 0x300,
+
+ // To indicate the modality of the dialog box, specify one of the following values.
+
+ ///
+ /// The user must respond to the message box before continuing work in the window identified by the hWnd parameter.
+ /// However, the user can move to the windows of other threads and work in those windows. Depending on the hierarchy
+ /// of windows in the application, the user may be able to move to other windows within the thread. All child windows
+ /// of the parent of the message box are automatically disabled, but pop-up windows are not. MB_APPLMODAL is the
+ /// default if neither MB_SYSTEMMODAL nor MB_TASKMODAL is specified.
+ ///
+ ApplModal = DefaultValue,
+
+ ///
+ /// Same as MB_APPLMODAL except that the message box has the WS_EX_TOPMOST style.
+ /// Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate
+ /// attention (for example, running out of memory). This flag has no effect on the user's ability to interact with
+ /// windows other than those associated with hWnd.
+ ///
+ SystemModal = 0x1000,
+
+ ///
+ /// Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the
+ /// hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle
+ /// available but still needs to prevent input to other windows in the calling thread without suspending other threads.
+ ///
+ TaskModal = 0x2000,
+
+ // To specify other options, use one or more of the following values.
+
+ ///
+ /// Same as desktop of the interactive window station. For more information, see Window Stations. If the current
+ /// input desktop is not the default desktop, MessageBox does not return until the user switches to the default
+ /// desktop.
+ ///
+ DefaultDesktopOnly = 0x20000,
+
+ ///
+ /// The text is right-justified.
+ ///
+ Right = 0x80000,
+
+ ///
+ /// Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
+ ///
+ RtlReading = 0x100000,
+
+ ///
+ /// The message box becomes the foreground window. Internally, the system calls the SetForegroundWindow function
+ /// for the message box.
+ ///
+ SetForeground = 0x10000,
+
+ ///
+ /// The message box is created with the WS_EX_TOPMOST window style.
+ ///
+ Topmost = 0x40000,
+
+ ///
+ /// The caller is a service notifying the user of an event. The function displays a message box on the current active
+ /// desktop, even if there is no user logged on to the computer.
+ ///
+ ServiceNotification = 0x200000,
}
///
- /// Native kernel32 functions.
+ /// Displays a modal dialog box that contains a system icon, a set of buttons, and a brief application-specific message,
+ /// such as status or error information. The message box returns an integer value that indicates which button the user
+ /// clicked.
///
- internal static partial class NativeFunctions
+ ///
+ /// A handle to the owner window of the message box to be created. If this parameter is NULL, the message box has no
+ /// owner window.
+ ///
+ ///
+ /// The message to be displayed. If the string consists of more than one line, you can separate the lines using a carriage
+ /// return and/or linefeed character between each line.
+ ///
+ ///
+ /// The dialog box title. If this parameter is NULL, the default title is Error.
+ ///
+ /// The contents and behavior of the dialog box. This parameter can be a combination of flags from the following groups
+ /// of flags.
+ ///
+ ///
+ /// If a message box has a Cancel button, the function returns the IDCANCEL value if either the ESC key is pressed or
+ /// the Cancel button is selected. If the message box has no Cancel button, pressing ESC will no effect - unless an
+ /// MB_OK button is present. If an MB_OK button is displayed and the user presses ESC, the return value will be IDOK.
+ /// If the function fails, the return value is zero.To get extended error information, call GetLastError. If the function
+ /// succeeds, the return value is one of the ID* enum values.
+ ///
+ [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, MessageBoxType type);
+}
+
+///
+/// Native kernel32 functions.
+///
+internal static partial class NativeFunctions
+{
+ ///
+ /// MEM_* from memoryapi.
+ ///
+ [Flags]
+ public enum AllocationType
{
///
- /// MEM_* from memoryapi.
+ /// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce
+ /// placeholders, lpAddress and dwSize must exactly match those of the placeholder.
///
- [Flags]
- public enum AllocationType
- {
- ///
- /// To coalesce two adjacent placeholders, specify MEM_RELEASE | MEM_COALESCE_PLACEHOLDERS. When you coalesce
- /// placeholders, lpAddress and dwSize must exactly match those of the placeholder.
- ///
- CoalescePlaceholders = 0x1,
-
- ///
- /// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using
- /// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify
- /// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
- ///
- PreservePlaceholder = 0x2,
-
- ///
- /// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved
- /// memory pages. The function also guarantees that when the caller later initially accesses the memory, the contents
- /// will be zero. Actual physical pages are not allocated unless/until the virtual addresses are actually accessed.
- /// To reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Attempting to commit
- /// a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL lpAddress fails unless the
- /// entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS. An attempt to commit
- /// a page that is already committed does not cause the function to fail. This means that you can commit pages without
- /// first determining the current commitment state of each page. If lpAddress specifies an address within an enclave,
- /// flAllocationType must be MEM_COMMIT.
- ///
- Commit = 0x1000,
-
- ///
- /// Reserves a range of the process's virtual address space without allocating any actual physical storage in memory
- /// or in the paging file on disk. You commit reserved pages by calling VirtualAllocEx again with MEM_COMMIT. To
- /// reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Other memory allocation
- /// functions, such as malloc and LocalAlloc, cannot use reserved memory until it has been released.
- ///
- Reserve = 0x2000,
-
- ///
- /// Decommits the specified region of committed pages. After the operation, the pages are in the reserved state.
- /// The function does not fail if you attempt to decommit an uncommitted page. This means that you can decommit
- /// a range of pages without first determining the current commitment state. The MEM_DECOMMIT value is not supported
- /// when the lpAddress parameter provides the base address for an enclave.
- ///
- Decommit = 0x4000,
-
- ///
- /// Releases the specified region of pages, or placeholder (for a placeholder, the address space is released and
- /// available for other allocations). After this operation, the pages are in the free state. If you specify this
- /// value, dwSize must be 0 (zero), and lpAddress must point to the base address returned by the VirtualAlloc function
- /// when the region is reserved. The function fails if either of these conditions is not met. If any pages in the
- /// region are committed currently, the function first decommits, and then releases them. The function does not
- /// fail if you attempt to release pages that are in different states, some reserved and some committed. This means
- /// that you can release a range of pages without first determining the current commitment state.
- ///
- Release = 0x8000,
-
- ///
- /// Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages
- /// should not be read from or written to the paging file. However, the memory block will be used again later, so
- /// it should not be decommitted. This value cannot be used with any other value. Using this value does not guarantee
- /// that the range operated on with MEM_RESET will contain zeros. If you want the range to contain zeros, decommit
- /// the memory and then recommit it. When you use MEM_RESET, the VirtualAllocEx function ignores the value of fProtect.
- /// However, you must still set fProtect to a valid protection value, such as PAGE_NOACCESS. VirtualAllocEx returns
- /// an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable
- /// if it is mapped to a paging file.
- ///
- Reset = 0x80000,
-
- ///
- /// MEM_RESET_UNDO should only be called on an address range to which MEM_RESET was successfully applied earlier.
- /// It indicates that the data in the specified memory range specified by lpAddress and dwSize is of interest to
- /// the caller and attempts to reverse the effects of MEM_RESET. If the function succeeds, that means all data in
- /// the specified address range is intact. If the function fails, at least some of the data in the address range
- /// has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on
- /// an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the
- /// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
- /// protection value, such as PAGE_NOACCESS.
- ///
- ResetUndo = 0x1000000,
-
- ///
- /// Reserves an address range that can be used to map Address Windowing Extensions (AWE) pages. This value must
- /// be used with MEM_RESERVE and no other values.
- ///
- Physical = 0x400000,
-
- ///
- /// Allocates memory at the highest possible address. This can be slower than regular allocations, especially when
- /// there are many allocations.
- ///
- TopDown = 0x100000,
-
- ///
- /// Causes the system to track pages that are written to in the allocated region. If you specify this value, you
- /// must also specify MEM_RESERVE. To retrieve the addresses of the pages that have been written to since the region
- /// was allocated or the write-tracking state was reset, call the GetWriteWatch function. To reset the write-tracking
- /// state, call GetWriteWatch or ResetWriteWatch. The write-tracking feature remains enabled for the memory region
- /// until the region is freed.
- ///
- WriteWatch = 0x200000,
-
- ///
- /// Allocates memory using large page support. The size and alignment must be a multiple of the large-page minimum.
- /// To obtain this value, use the GetLargePageMinimum function. If you specify this value, you must also specify
- /// MEM_RESERVE and MEM_COMMIT.
- ///
- LargePages = 0x20000000,
- }
+ CoalescePlaceholders = 0x1,
///
- /// Unprefixed flags from CreateRemoteThread.
+ /// Frees an allocation back to a placeholder (after you've replaced a placeholder with a private allocation using
+ /// VirtualAlloc2 or Virtual2AllocFromApp). To split a placeholder into two placeholders, specify
+ /// MEM_RELEASE | MEM_PRESERVE_PLACEHOLDER.
///
- [Flags]
- public enum CreateThreadFlags
- {
- ///
- /// The thread runs immediately after creation.
- ///
- RunImmediately = 0x0,
-
- ///
- /// The thread is created in a suspended state, and does not run until the ResumeThread function is called.
- ///
- CreateSuspended = 0x4,
-
- ///
- /// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size.
- ///
- StackSizeParamIsReservation = 0x10000,
- }
+ PreservePlaceholder = 0x2,
///
- /// PAGE_* from memoryapi.
+ /// Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved
+ /// memory pages. The function also guarantees that when the caller later initially accesses the memory, the contents
+ /// will be zero. Actual physical pages are not allocated unless/until the virtual addresses are actually accessed.
+ /// To reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Attempting to commit
+ /// a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL lpAddress fails unless the
+ /// entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS. An attempt to commit
+ /// a page that is already committed does not cause the function to fail. This means that you can commit pages without
+ /// first determining the current commitment state of each page. If lpAddress specifies an address within an enclave,
+ /// flAllocationType must be MEM_COMMIT.
///
- [Flags]
- public enum MemoryProtection
- {
- ///
- /// Enables execute access to the committed region of pages. An attempt to write to the committed region results
- /// in an access violation. This flag is not supported by the CreateFileMapping function.
- ///
- Execute = 0x10,
-
- ///
- /// Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region
- /// results in an access violation.
- ///
- ExecuteRead = 0x20,
-
- ///
- /// Enables execute, read-only, or read/write access to the committed region of pages.
- ///
- ExecuteReadWrite = 0x40,
-
- ///
- /// Enables execute, read-only, or copy-on-write access to a mapped view of a file mapping object. An attempt to
- /// write to a committed copy-on-write page results in a private copy of the page being made for the process. The
- /// private page is marked as PAGE_EXECUTE_READWRITE, and the change is written to the new page. This flag is not
- /// supported by the VirtualAlloc or VirtualAllocEx functions.
- ///
- ExecuteWriteCopy = 0x80,
-
- ///
- /// Disables all access to the committed region of pages. An attempt to read from, write to, or execute the committed
- /// region results in an access violation. This flag is not supported by the CreateFileMapping function.
- ///
- NoAccess = 0x01,
-
- ///
- /// Enables read-only access to the committed region of pages. An attempt to write to the committed region results
- /// in an access violation. If Data Execution Prevention is enabled, an attempt to execute code in the committed
- /// region results in an access violation.
- ///
- ReadOnly = 0x02,
-
- ///
- /// Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled,
- /// attempting to execute code in the committed region results in an access violation.
- ///
- ReadWrite = 0x04,
-
- ///
- /// Enables read-only or copy-on-write access to a mapped view of a file mapping object. An attempt to write to
- /// a committed copy-on-write page results in a private copy of the page being made for the process. The private
- /// page is marked as PAGE_READWRITE, and the change is written to the new page. If Data Execution Prevention is
- /// enabled, attempting to execute code in the committed region results in an access violation. This flag is not
- /// supported by the VirtualAlloc or VirtualAllocEx functions.
- ///
- WriteCopy = 0x08,
-
- ///
- /// Sets all locations in the pages as invalid targets for CFG. Used along with any execute page protection like
- /// PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY. Any indirect call to locations
- /// in those pages will fail CFG checks and the process will be terminated. The default behavior for executable
- /// pages allocated is to be marked valid call targets for CFG. This flag is not supported by the VirtualProtect
- /// or CreateFileMapping functions.
- ///
- TargetsInvalid = 0x40000000,
-
- ///
- /// Pages in the region will not have their CFG information updated while the protection changes for VirtualProtect.
- /// For example, if the pages in the region was allocated using PAGE_TARGETS_INVALID, then the invalid information
- /// will be maintained while the page protection changes. This flag is only valid when the protection changes to
- /// an executable type like PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY.
- /// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call
- /// targets for CFG.
- ///
- TargetsNoUpdate = TargetsInvalid,
-
- ///
- /// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a
- /// STATUS_GUARD_PAGE_VIOLATION exception and turn off the guard page status. Guard pages thus act as a one-time
- /// access alarm. For more information, see Creating Guard Pages. When an access attempt leads the system to turn
- /// off guard page status, the underlying page protection takes over. If a guard page exception occurs during a
- /// system service, the service typically returns a failure status indicator. This value cannot be used with
- /// PAGE_NOACCESS. This flag is not supported by the CreateFileMapping function.
- ///
- Guard = 0x100,
-
- ///
- /// Sets all pages to be non-cachable. Applications should not use this attribute except when explicitly required
- /// for a device. Using the interlocked functions with memory that is mapped with SEC_NOCACHE can result in an
- /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_NOCACHE flag cannot be used with the PAGE_GUARD, PAGE_NOACCESS,
- /// or PAGE_WRITECOMBINE flags. The PAGE_NOCACHE flag can be used only when allocating private memory with the
- /// VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable non-cached memory access for shared
- /// memory, specify the SEC_NOCACHE flag when calling the CreateFileMapping function.
- ///
- NoCache = 0x200,
-
- ///
- /// Sets all pages to be write-combined. Applications should not use this attribute except when explicitly required
- /// for a device. Using the interlocked functions with memory that is mapped as write-combined can result in an
- /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_WRITECOMBINE flag cannot be specified with the PAGE_NOACCESS,
- /// PAGE_GUARD, and PAGE_NOCACHE flags. The PAGE_WRITECOMBINE flag can be used only when allocating private memory
- /// with the VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable write-combined memory access
- /// for shared memory, specify the SEC_WRITECOMBINE flag when calling the CreateFileMapping function.
- ///
- WriteCombine = 0x400,
- }
+ Commit = 0x1000,
///
- /// PROCESS_* from processthreadsapi.
+ /// Reserves a range of the process's virtual address space without allocating any actual physical storage in memory
+ /// or in the paging file on disk. You commit reserved pages by calling VirtualAllocEx again with MEM_COMMIT. To
+ /// reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE. Other memory allocation
+ /// functions, such as malloc and LocalAlloc, cannot use reserved memory until it has been released.
///
- [Flags]
- public enum ProcessAccessFlags : uint
- {
- ///
- /// All possible access rights for a process object.
- ///
- AllAccess = 0x001F0FFF,
-
- ///
- /// Required to create a process.
- ///
- CreateProcess = 0x0080,
-
- ///
- /// Required to create a thread.
- ///
- CreateThread = 0x0002,
-
- ///
- /// Required to duplicate a handle using DuplicateHandle.
- ///
- DupHandle = 0x0040,
-
- ///
- /// Required to retrieve certain information about a process, such as its token, exit code,
- /// and priority class (see OpenProcessToken).
- ///
- QueryInformation = 0x0400,
-
- ///
- /// Required to retrieve certain information about a process(see GetExitCodeProcess, GetPriorityClass, IsProcessInJob,
- /// QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted
- /// PROCESS_QUERY_LIMITED_INFORMATION.
- ///
- QueryLimitedInformation = 0x1000,
-
- ///
- /// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
- ///
- SetInformation = 0x0200,
-
- ///
- /// Required to set memory limits using SetProcessWorkingSetSize.
- ///
- SetQuote = 0x0100,
-
- ///
- /// Required to suspend or resume a process.
- ///
- SuspendResume = 0x0800,
-
- ///
- /// Required to terminate a process using TerminateProcess.
- ///
- Terminate = 0x0001,
-
- ///
- /// Required to perform an operation on the address space of a process(see VirtualProtectEx and WriteProcessMemory).
- ///
- VmOperation = 0x0008,
-
- ///
- /// Required to read memory in a process using ReadProcessMemory.
- ///
- VmRead = 0x0010,
-
- ///
- /// Required to write to memory in a process using WriteProcessMemory.
- ///
- VmWrite = 0x0020,
-
- ///
- /// Required to wait for the process to terminate using the wait functions.
- ///
- Synchronize = 0x00100000,
- }
+ Reserve = 0x2000,
///
- /// WAIT_* from synchapi.
+ /// Decommits the specified region of committed pages. After the operation, the pages are in the reserved state.
+ /// The function does not fail if you attempt to decommit an uncommitted page. This means that you can decommit
+ /// a range of pages without first determining the current commitment state. The MEM_DECOMMIT value is not supported
+ /// when the lpAddress parameter provides the base address for an enclave.
///
- public enum WaitResult
- {
- ///
- /// The specified object is a mutex object that was not released by the thread that owned the mutex object
- /// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and
- /// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you
- /// should check it for consistency.
- ///
- Abandoned = 0x80,
-
- ///
- /// The state of the specified object is signaled.
- ///
- Object0 = 0x0,
-
- ///
- /// The time-out interval elapsed, and the object's state is nonsignaled.
- ///
- Timeout = 0x102,
-
- ///
- /// The function has failed. To get extended error information, call GetLastError.
- ///
- WAIT_FAILED = 0xFFFFFFF,
- }
+ Decommit = 0x4000,
///
- /// Closes an open object handle.
+ /// Releases the specified region of pages, or placeholder (for a placeholder, the address space is released and
+ /// available for other allocations). After this operation, the pages are in the free state. If you specify this
+ /// value, dwSize must be 0 (zero), and lpAddress must point to the base address returned by the VirtualAlloc function
+ /// when the region is reserved. The function fails if either of these conditions is not met. If any pages in the
+ /// region are committed currently, the function first decommits, and then releases them. The function does not
+ /// fail if you attempt to release pages that are in different states, some reserved and some committed. This means
+ /// that you can release a range of pages without first determining the current commitment state.
///
- ///
- /// A valid handle to an open object.
- ///
- ///
- /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error
- /// information, call GetLastError. If the application is running under a debugger, the function will throw an exception if it receives
- /// either a handle value that is not valid or a pseudo-handle value. This can happen if you close a handle twice, or if you call
- /// CloseHandle on a handle returned by the FindFirstFile function instead of calling the FindClose function.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool CloseHandle(IntPtr hObject);
+ Release = 0x8000,
///
- /// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function
- /// to create a thread that runs in the virtual address space of another process and optionally specify extended attributes.
+ /// Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages
+ /// should not be read from or written to the paging file. However, the memory block will be used again later, so
+ /// it should not be decommitted. This value cannot be used with any other value. Using this value does not guarantee
+ /// that the range operated on with MEM_RESET will contain zeros. If you want the range to contain zeros, decommit
+ /// the memory and then recommit it. When you use MEM_RESET, the VirtualAllocEx function ignores the value of fProtect.
+ /// However, you must still set fProtect to a valid protection value, such as PAGE_NOACCESS. VirtualAllocEx returns
+ /// an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable
+ /// if it is mapped to a paging file.
///
- ///
- /// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD,
- /// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without
- /// these rights on certain platforms. For more information, see Process Security and Access Rights.
- ///
- ///
- /// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether
- /// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor
- /// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from
- /// the primary token of the creator.
- ///
- ///
- /// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the
- /// new thread uses the default size for the executable. For more information, see Thread Stack Size.
- ///
- ///
- /// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the
- /// starting address of the thread in the remote process. The function must exist in the remote process. For more information,
- /// see ThreadProc.
- ///
- ///
- /// A pointer to a variable to be passed to the thread function.
- ///
- ///
- /// The flags that control the creation of the thread.
- ///
- ///
- /// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
- ///
- ///
- /// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is
- /// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress
- /// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and
- /// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process.
- /// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to
- /// invalid or missing dynamic-link libraries (DLL).
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern IntPtr CreateRemoteThread(
- IntPtr hProcess,
- IntPtr lpThreadAttributes,
- UIntPtr dwStackSize,
- IntPtr lpStartAddress,
- IntPtr lpParameter,
- CreateThreadFlags dwCreationFlags,
- out uint lpThreadId);
+ Reset = 0x80000,
///
- /// Retrieves the termination status of the specified thread.
+ /// MEM_RESET_UNDO should only be called on an address range to which MEM_RESET was successfully applied earlier.
+ /// It indicates that the data in the specified memory range specified by lpAddress and dwSize is of interest to
+ /// the caller and attempts to reverse the effects of MEM_RESET. If the function succeeds, that means all data in
+ /// the specified address range is intact. If the function fails, at least some of the data in the address range
+ /// has been replaced with zeroes. This value cannot be used with any other value. If MEM_RESET_UNDO is called on
+ /// an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the
+ /// VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid
+ /// protection value, such as PAGE_NOACCESS.
///
- ///
- /// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION
- /// access right.For more information, see Thread Security and Access Rights.
- ///
- ///
- /// A pointer to a variable to receive the thread termination status.
- ///
- ///
- /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get
- /// extended error information, call GetLastError.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- [return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
+ ResetUndo = 0x1000000,
///
- /// Opens an existing local process object.
+ /// Reserves an address range that can be used to map Address Windowing Extensions (AWE) pages. This value must
+ /// be used with MEM_RESERVE and no other values.
///
- ///
- /// The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or
- /// more of the process access rights. If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the
- /// contents of the security descriptor.
- ///
- ///
- /// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
- ///
- ///
- /// The identifier of the local process to be opened. If the specified process is the System Idle Process(0x00000000), the function fails and the
- /// last error code is ERROR_INVALID_PARAMETER.If the specified process is the System process or one of the Client Server Run-Time Subsystem(CSRSS)
- /// processes, this function fails and the last error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from
- /// opening them. If you are using GetCurrentProcessId as an argument to this function, consider using GetCurrentProcess instead of OpenProcess, for
- /// improved performance.
- ///
- ///
- /// If the function succeeds, the return value is an open handle to the specified process.
- /// If the function fails, the return value is NULL.To get extended error information, call GetLastError.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern IntPtr OpenProcess(
- ProcessAccessFlags dwDesiredAccess,
- bool bInheritHandle,
- int dwProcessId);
+ Physical = 0x400000,
///
- /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
- /// Reserves, commits, or changes the state of a region of memory within the virtual address space of a specified process.
- /// The function initializes the memory it allocates to zero. To specify the NUMA node for the physical memory, see
- /// VirtualAllocExNuma.
+ /// Allocates memory at the highest possible address. This can be slower than regular allocations, especially when
+ /// there are many allocations.
///
- ///
- /// The handle to a process. The function allocates memory within the virtual address space of this process. The handle
- /// must have the PROCESS_VM_OPERATION access right. For more information, see Process Security and Access Rights.
- ///
- ///
- /// The pointer that specifies a desired starting address for the region of pages that you want to allocate. If you
- /// are reserving memory, the function rounds this address down to the nearest multiple of the allocation granularity.
- /// If you are committing memory that is already reserved, the function rounds this address down to the nearest page
- /// boundary. To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo
- /// function. If lpAddress is NULL, the function determines where to allocate the region. If this address is within
- /// an enclave that you have not initialized by calling InitializeEnclave, VirtualAllocEx allocates a page of zeros
- /// for the enclave at that address. The page must be previously uncommitted, and will not be measured with the EEXTEND
- /// instruction of the Intel Software Guard Extensions programming model. If the address in within an enclave that you
- /// initialized, then the allocation operation fails with the ERROR_INVALID_ADDRESS error.
- ///
- ///
- /// The size of the region of memory to allocate, in bytes. If lpAddress is NULL, the function rounds dwSize up to the
- /// next page boundary. If lpAddress is not NULL, the function allocates all pages that contain one or more bytes in
- /// the range from lpAddress to lpAddress+dwSize. This means, for example, that a 2-byte range that straddles a page
- /// boundary causes the function to allocate both pages.
- ///
- ///
- /// The type of memory allocation. This parameter must contain one of the MEM_* enum values.
- ///
- ///
- /// The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify
- /// any one of the memory protection constants.
- ///
- ///
- /// If the function succeeds, the return value is the base address of the allocated region of pages. If the function
- /// fails, the return value is NULL.To get extended error information, call GetLastError.
- ///
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern IntPtr VirtualAllocEx(
- IntPtr hProcess,
- IntPtr lpAddress,
- int dwSize,
- AllocationType flAllocationType,
- MemoryProtection flProtect);
+ TopDown = 0x100000,
///
- /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfreeex.
- /// Releases, decommits, or releases and decommits a region of memory within the virtual address space of a specified
- /// process.
+ /// Causes the system to track pages that are written to in the allocated region. If you specify this value, you
+ /// must also specify MEM_RESERVE. To retrieve the addresses of the pages that have been written to since the region
+ /// was allocated or the write-tracking state was reset, call the GetWriteWatch function. To reset the write-tracking
+ /// state, call GetWriteWatch or ResetWriteWatch. The write-tracking feature remains enabled for the memory region
+ /// until the region is freed.
///
- ///
- /// A handle to a process. The function frees memory within the virtual address space of the process. The handle must
- /// have the PROCESS_VM_OPERATION access right.For more information, see Process Security and Access Rights.
- ///
- ///
- /// A pointer to the starting address of the region of memory to be freed. If the dwFreeType parameter is MEM_RELEASE,
- /// lpAddress must be the base address returned by the VirtualAllocEx function when the region is reserved.
- ///
- ///
- /// The size of the region of memory to free, in bytes. If the dwFreeType parameter is MEM_RELEASE, dwSize must be 0
- /// (zero). The function frees the entire region that is reserved in the initial allocation call to VirtualAllocEx.
- /// If dwFreeType is MEM_DECOMMIT, the function decommits all memory pages that contain one or more bytes in the range
- /// from the lpAddress parameter to (lpAddress+dwSize). This means, for example, that a 2-byte region of memory that
- /// straddles a page boundary causes both pages to be decommitted. If lpAddress is the base address returned by
- /// VirtualAllocEx and dwSize is 0 (zero), the function decommits the entire region that is allocated by VirtualAllocEx.
- /// After that, the entire region is in the reserved state.
- ///
- ///
- /// The type of free operation. This parameter must be one of the MEM_* enum values.
- ///
- ///
- /// If the function succeeds, the return value is a nonzero value. If the function fails, the return value is 0 (zero).
- /// To get extended error information, call GetLastError.
- ///
- [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
- public static extern bool VirtualFreeEx(
- IntPtr hProcess,
- IntPtr lpAddress,
- int dwSize,
- AllocationType dwFreeType);
+ WriteWatch = 0x200000,
///
- /// Waits until the specified object is in the signaled state or the time-out interval elapses. To enter an alertable wait
- /// state, use the WaitForSingleObjectEx function.To wait for multiple objects, use WaitForMultipleObjects.
+ /// Allocates memory using large page support. The size and alignment must be a multiple of the large-page minimum.
+ /// To obtain this value, use the GetLargePageMinimum function. If you specify this value, you must also specify
+ /// MEM_RESERVE and MEM_COMMIT.
///
- ///
- /// A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
- /// If this handle is closed while the wait is still pending, the function's behavior is undefined. The handle must have the
- /// SYNCHRONIZE access right. For more information, see Standard Access Rights.
- ///
- ///
- /// The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled
- /// or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled;
- /// it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
- ///
- ///
- /// If the function succeeds, the return value indicates the event that caused the function to return.
- /// It can be one of the WaitResult values.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
-
- ///
- /// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or
- /// the operation fails.
- ///
- ///
- /// A handle to the process memory to be modified. The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access
- /// to the process.
- ///
- ///
- /// A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the
- /// system verifies that all data in the base address and memory of the specified size is accessible for write access,
- /// and if it is not accessible, the function fails.
- ///
- ///
- /// A pointer to the buffer that contains data to be written in the address space of the specified process.
- ///
- ///
- /// The number of bytes to be written to the specified process.
- ///
- ///
- /// A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter
- /// is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.
- ///
- ///
- /// If the function succeeds, the return value is nonzero. If the function fails, the return value is 0 (zero). To get
- /// extended error information, call GetLastError.The function fails if the requested write operation crosses into an
- /// area of the process that is inaccessible.
- ///
- [DllImport("kernel32.dll", SetLastError = true)]
- public static extern bool WriteProcessMemory(
- IntPtr hProcess,
- IntPtr lpBaseAddress,
- byte[] lpBuffer,
- int dwSize,
- out IntPtr lpNumberOfBytesWritten);
+ LargePages = 0x20000000,
}
+
+ ///
+ /// Unprefixed flags from CreateRemoteThread.
+ ///
+ [Flags]
+ public enum CreateThreadFlags
+ {
+ ///
+ /// The thread runs immediately after creation.
+ ///
+ RunImmediately = 0x0,
+
+ ///
+ /// The thread is created in a suspended state, and does not run until the ResumeThread function is called.
+ ///
+ CreateSuspended = 0x4,
+
+ ///
+ /// The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size.
+ ///
+ StackSizeParamIsReservation = 0x10000,
+ }
+
+ ///
+ /// PAGE_* from memoryapi.
+ ///
+ [Flags]
+ public enum MemoryProtection
+ {
+ ///
+ /// Enables execute access to the committed region of pages. An attempt to write to the committed region results
+ /// in an access violation. This flag is not supported by the CreateFileMapping function.
+ ///
+ Execute = 0x10,
+
+ ///
+ /// Enables execute or read-only access to the committed region of pages. An attempt to write to the committed region
+ /// results in an access violation.
+ ///
+ ExecuteRead = 0x20,
+
+ ///
+ /// Enables execute, read-only, or read/write access to the committed region of pages.
+ ///
+ ExecuteReadWrite = 0x40,
+
+ ///
+ /// Enables execute, read-only, or copy-on-write access to a mapped view of a file mapping object. An attempt to
+ /// write to a committed copy-on-write page results in a private copy of the page being made for the process. The
+ /// private page is marked as PAGE_EXECUTE_READWRITE, and the change is written to the new page. This flag is not
+ /// supported by the VirtualAlloc or VirtualAllocEx functions.
+ ///
+ ExecuteWriteCopy = 0x80,
+
+ ///
+ /// Disables all access to the committed region of pages. An attempt to read from, write to, or execute the committed
+ /// region results in an access violation. This flag is not supported by the CreateFileMapping function.
+ ///
+ NoAccess = 0x01,
+
+ ///
+ /// Enables read-only access to the committed region of pages. An attempt to write to the committed region results
+ /// in an access violation. If Data Execution Prevention is enabled, an attempt to execute code in the committed
+ /// region results in an access violation.
+ ///
+ ReadOnly = 0x02,
+
+ ///
+ /// Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled,
+ /// attempting to execute code in the committed region results in an access violation.
+ ///
+ ReadWrite = 0x04,
+
+ ///
+ /// Enables read-only or copy-on-write access to a mapped view of a file mapping object. An attempt to write to
+ /// a committed copy-on-write page results in a private copy of the page being made for the process. The private
+ /// page is marked as PAGE_READWRITE, and the change is written to the new page. If Data Execution Prevention is
+ /// enabled, attempting to execute code in the committed region results in an access violation. This flag is not
+ /// supported by the VirtualAlloc or VirtualAllocEx functions.
+ ///
+ WriteCopy = 0x08,
+
+ ///
+ /// Sets all locations in the pages as invalid targets for CFG. Used along with any execute page protection like
+ /// PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY. Any indirect call to locations
+ /// in those pages will fail CFG checks and the process will be terminated. The default behavior for executable
+ /// pages allocated is to be marked valid call targets for CFG. This flag is not supported by the VirtualProtect
+ /// or CreateFileMapping functions.
+ ///
+ TargetsInvalid = 0x40000000,
+
+ ///
+ /// Pages in the region will not have their CFG information updated while the protection changes for VirtualProtect.
+ /// For example, if the pages in the region was allocated using PAGE_TARGETS_INVALID, then the invalid information
+ /// will be maintained while the page protection changes. This flag is only valid when the protection changes to
+ /// an executable type like PAGE_EXECUTE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE and PAGE_EXECUTE_WRITECOPY.
+ /// The default behavior for VirtualProtect protection change to executable is to mark all locations as valid call
+ /// targets for CFG.
+ ///
+ TargetsNoUpdate = TargetsInvalid,
+
+ ///
+ /// Pages in the region become guard pages. Any attempt to access a guard page causes the system to raise a
+ /// STATUS_GUARD_PAGE_VIOLATION exception and turn off the guard page status. Guard pages thus act as a one-time
+ /// access alarm. For more information, see Creating Guard Pages. When an access attempt leads the system to turn
+ /// off guard page status, the underlying page protection takes over. If a guard page exception occurs during a
+ /// system service, the service typically returns a failure status indicator. This value cannot be used with
+ /// PAGE_NOACCESS. This flag is not supported by the CreateFileMapping function.
+ ///
+ Guard = 0x100,
+
+ ///
+ /// Sets all pages to be non-cachable. Applications should not use this attribute except when explicitly required
+ /// for a device. Using the interlocked functions with memory that is mapped with SEC_NOCACHE can result in an
+ /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_NOCACHE flag cannot be used with the PAGE_GUARD, PAGE_NOACCESS,
+ /// or PAGE_WRITECOMBINE flags. The PAGE_NOCACHE flag can be used only when allocating private memory with the
+ /// VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable non-cached memory access for shared
+ /// memory, specify the SEC_NOCACHE flag when calling the CreateFileMapping function.
+ ///
+ NoCache = 0x200,
+
+ ///
+ /// Sets all pages to be write-combined. Applications should not use this attribute except when explicitly required
+ /// for a device. Using the interlocked functions with memory that is mapped as write-combined can result in an
+ /// EXCEPTION_ILLEGAL_INSTRUCTION exception. The PAGE_WRITECOMBINE flag cannot be specified with the PAGE_NOACCESS,
+ /// PAGE_GUARD, and PAGE_NOCACHE flags. The PAGE_WRITECOMBINE flag can be used only when allocating private memory
+ /// with the VirtualAlloc, VirtualAllocEx, or VirtualAllocExNuma functions. To enable write-combined memory access
+ /// for shared memory, specify the SEC_WRITECOMBINE flag when calling the CreateFileMapping function.
+ ///
+ WriteCombine = 0x400,
+ }
+
+ ///
+ /// PROCESS_* from processthreadsapi.
+ ///
+ [Flags]
+ public enum ProcessAccessFlags : uint
+ {
+ ///
+ /// All possible access rights for a process object.
+ ///
+ AllAccess = 0x001F0FFF,
+
+ ///
+ /// Required to create a process.
+ ///
+ CreateProcess = 0x0080,
+
+ ///
+ /// Required to create a thread.
+ ///
+ CreateThread = 0x0002,
+
+ ///
+ /// Required to duplicate a handle using DuplicateHandle.
+ ///
+ DupHandle = 0x0040,
+
+ ///
+ /// Required to retrieve certain information about a process, such as its token, exit code,
+ /// and priority class (see OpenProcessToken).
+ ///
+ QueryInformation = 0x0400,
+
+ ///
+ /// Required to retrieve certain information about a process(see GetExitCodeProcess, GetPriorityClass, IsProcessInJob,
+ /// QueryFullProcessImageName). A handle that has the PROCESS_QUERY_INFORMATION access right is automatically granted
+ /// PROCESS_QUERY_LIMITED_INFORMATION.
+ ///
+ QueryLimitedInformation = 0x1000,
+
+ ///
+ /// Required to set certain information about a process, such as its priority class (see SetPriorityClass).
+ ///
+ SetInformation = 0x0200,
+
+ ///
+ /// Required to set memory limits using SetProcessWorkingSetSize.
+ ///
+ SetQuote = 0x0100,
+
+ ///
+ /// Required to suspend or resume a process.
+ ///
+ SuspendResume = 0x0800,
+
+ ///
+ /// Required to terminate a process using TerminateProcess.
+ ///
+ Terminate = 0x0001,
+
+ ///
+ /// Required to perform an operation on the address space of a process(see VirtualProtectEx and WriteProcessMemory).
+ ///
+ VmOperation = 0x0008,
+
+ ///
+ /// Required to read memory in a process using ReadProcessMemory.
+ ///
+ VmRead = 0x0010,
+
+ ///
+ /// Required to write to memory in a process using WriteProcessMemory.
+ ///
+ VmWrite = 0x0020,
+
+ ///
+ /// Required to wait for the process to terminate using the wait functions.
+ ///
+ Synchronize = 0x00100000,
+ }
+
+ ///
+ /// WAIT_* from synchapi.
+ ///
+ public enum WaitResult
+ {
+ ///
+ /// The specified object is a mutex object that was not released by the thread that owned the mutex object
+ /// before the owning thread terminated.Ownership of the mutex object is granted to the calling thread and
+ /// the mutex state is set to nonsignaled. If the mutex was protecting persistent state information, you
+ /// should check it for consistency.
+ ///
+ Abandoned = 0x80,
+
+ ///
+ /// The state of the specified object is signaled.
+ ///
+ Object0 = 0x0,
+
+ ///
+ /// The time-out interval elapsed, and the object's state is nonsignaled.
+ ///
+ Timeout = 0x102,
+
+ ///
+ /// The function has failed. To get extended error information, call GetLastError.
+ ///
+ WAIT_FAILED = 0xFFFFFFF,
+ }
+
+ ///
+ /// Closes an open object handle.
+ ///
+ ///
+ /// A valid handle to an open object.
+ ///
+ ///
+ /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get extended error
+ /// information, call GetLastError. If the application is running under a debugger, the function will throw an exception if it receives
+ /// either a handle value that is not valid or a pseudo-handle value. This can happen if you close a handle twice, or if you call
+ /// CloseHandle on a handle returned by the FindFirstFile function instead of calling the FindClose function.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool CloseHandle(IntPtr hObject);
+
+ ///
+ /// Creates a thread that runs in the virtual address space of another process. Use the CreateRemoteThreadEx function
+ /// to create a thread that runs in the virtual address space of another process and optionally specify extended attributes.
+ ///
+ ///
+ /// A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD,
+ /// PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without
+ /// these rights on certain platforms. For more information, see Process Security and Access Rights.
+ ///
+ ///
+ /// A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether
+ /// child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor
+ /// and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from
+ /// the primary token of the creator.
+ ///
+ ///
+ /// The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the
+ /// new thread uses the default size for the executable. For more information, see Thread Stack Size.
+ ///
+ ///
+ /// A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the
+ /// starting address of the thread in the remote process. The function must exist in the remote process. For more information,
+ /// see ThreadProc.
+ ///
+ ///
+ /// A pointer to a variable to be passed to the thread function.
+ ///
+ ///
+ /// The flags that control the creation of the thread.
+ ///
+ ///
+ /// A pointer to a variable that receives the thread identifier. If this parameter is NULL, the thread identifier is not returned.
+ ///
+ ///
+ /// If the function succeeds, the return value is a handle to the new thread. If the function fails, the return value is
+ /// NULL.To get extended error information, call GetLastError. Note that CreateRemoteThread may succeed even if lpStartAddress
+ /// points to data, code, or is not accessible. If the start address is invalid when the thread runs, an exception occurs, and
+ /// the thread terminates. Thread termination due to a invalid start address is handled as an error exit for the thread's process.
+ /// This behavior is similar to the asynchronous nature of CreateProcess, where the process is created even if it refers to
+ /// invalid or missing dynamic-link libraries (DLL).
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr CreateRemoteThread(
+ IntPtr hProcess,
+ IntPtr lpThreadAttributes,
+ UIntPtr dwStackSize,
+ IntPtr lpStartAddress,
+ IntPtr lpParameter,
+ CreateThreadFlags dwCreationFlags,
+ out uint lpThreadId);
+
+ ///
+ /// Retrieves the termination status of the specified thread.
+ ///
+ ///
+ /// A handle to the thread. The handle must have the THREAD_QUERY_INFORMATION or THREAD_QUERY_LIMITED_INFORMATION
+ /// access right.For more information, see Thread Security and Access Rights.
+ ///
+ ///
+ /// A pointer to a variable to receive the thread termination status.
+ ///
+ ///
+ /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. To get
+ /// extended error information, call GetLastError.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ public static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
+
+ ///
+ /// Opens an existing local process object.
+ ///
+ ///
+ /// The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or
+ /// more of the process access rights. If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the
+ /// contents of the security descriptor.
+ ///
+ ///
+ /// If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
+ ///
+ ///
+ /// The identifier of the local process to be opened. If the specified process is the System Idle Process(0x00000000), the function fails and the
+ /// last error code is ERROR_INVALID_PARAMETER.If the specified process is the System process or one of the Client Server Run-Time Subsystem(CSRSS)
+ /// processes, this function fails and the last error code is ERROR_ACCESS_DENIED because their access restrictions prevent user-level code from
+ /// opening them. If you are using GetCurrentProcessId as an argument to this function, consider using GetCurrentProcess instead of OpenProcess, for
+ /// improved performance.
+ ///
+ ///
+ /// If the function succeeds, the return value is an open handle to the specified process.
+ /// If the function fails, the return value is NULL.To get extended error information, call GetLastError.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern IntPtr OpenProcess(
+ ProcessAccessFlags dwDesiredAccess,
+ bool bInheritHandle,
+ int dwProcessId);
+
+ ///
+ /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex.
+ /// Reserves, commits, or changes the state of a region of memory within the virtual address space of a specified process.
+ /// The function initializes the memory it allocates to zero. To specify the NUMA node for the physical memory, see
+ /// VirtualAllocExNuma.
+ ///
+ ///
+ /// The handle to a process. The function allocates memory within the virtual address space of this process. The handle
+ /// must have the PROCESS_VM_OPERATION access right. For more information, see Process Security and Access Rights.
+ ///
+ ///
+ /// The pointer that specifies a desired starting address for the region of pages that you want to allocate. If you
+ /// are reserving memory, the function rounds this address down to the nearest multiple of the allocation granularity.
+ /// If you are committing memory that is already reserved, the function rounds this address down to the nearest page
+ /// boundary. To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo
+ /// function. If lpAddress is NULL, the function determines where to allocate the region. If this address is within
+ /// an enclave that you have not initialized by calling InitializeEnclave, VirtualAllocEx allocates a page of zeros
+ /// for the enclave at that address. The page must be previously uncommitted, and will not be measured with the EEXTEND
+ /// instruction of the Intel Software Guard Extensions programming model. If the address in within an enclave that you
+ /// initialized, then the allocation operation fails with the ERROR_INVALID_ADDRESS error.
+ ///
+ ///
+ /// The size of the region of memory to allocate, in bytes. If lpAddress is NULL, the function rounds dwSize up to the
+ /// next page boundary. If lpAddress is not NULL, the function allocates all pages that contain one or more bytes in
+ /// the range from lpAddress to lpAddress+dwSize. This means, for example, that a 2-byte range that straddles a page
+ /// boundary causes the function to allocate both pages.
+ ///
+ ///
+ /// The type of memory allocation. This parameter must contain one of the MEM_* enum values.
+ ///
+ ///
+ /// The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify
+ /// any one of the memory protection constants.
+ ///
+ ///
+ /// If the function succeeds, the return value is the base address of the allocated region of pages. If the function
+ /// fails, the return value is NULL.To get extended error information, call GetLastError.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
+ public static extern IntPtr VirtualAllocEx(
+ IntPtr hProcess,
+ IntPtr lpAddress,
+ int dwSize,
+ AllocationType flAllocationType,
+ MemoryProtection flProtect);
+
+ ///
+ /// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfreeex.
+ /// Releases, decommits, or releases and decommits a region of memory within the virtual address space of a specified
+ /// process.
+ ///
+ ///
+ /// A handle to a process. The function frees memory within the virtual address space of the process. The handle must
+ /// have the PROCESS_VM_OPERATION access right.For more information, see Process Security and Access Rights.
+ ///
+ ///
+ /// A pointer to the starting address of the region of memory to be freed. If the dwFreeType parameter is MEM_RELEASE,
+ /// lpAddress must be the base address returned by the VirtualAllocEx function when the region is reserved.
+ ///
+ ///
+ /// The size of the region of memory to free, in bytes. If the dwFreeType parameter is MEM_RELEASE, dwSize must be 0
+ /// (zero). The function frees the entire region that is reserved in the initial allocation call to VirtualAllocEx.
+ /// If dwFreeType is MEM_DECOMMIT, the function decommits all memory pages that contain one or more bytes in the range
+ /// from the lpAddress parameter to (lpAddress+dwSize). This means, for example, that a 2-byte region of memory that
+ /// straddles a page boundary causes both pages to be decommitted. If lpAddress is the base address returned by
+ /// VirtualAllocEx and dwSize is 0 (zero), the function decommits the entire region that is allocated by VirtualAllocEx.
+ /// After that, the entire region is in the reserved state.
+ ///
+ ///
+ /// The type of free operation. This parameter must be one of the MEM_* enum values.
+ ///
+ ///
+ /// If the function succeeds, the return value is a nonzero value. If the function fails, the return value is 0 (zero).
+ /// To get extended error information, call GetLastError.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
+ public static extern bool VirtualFreeEx(
+ IntPtr hProcess,
+ IntPtr lpAddress,
+ int dwSize,
+ AllocationType dwFreeType);
+
+ ///
+ /// Waits until the specified object is in the signaled state or the time-out interval elapses. To enter an alertable wait
+ /// state, use the WaitForSingleObjectEx function.To wait for multiple objects, use WaitForMultipleObjects.
+ ///
+ ///
+ /// A handle to the object. For a list of the object types whose handles can be specified, see the following Remarks section.
+ /// If this handle is closed while the wait is still pending, the function's behavior is undefined. The handle must have the
+ /// SYNCHRONIZE access right. For more information, see Standard Access Rights.
+ ///
+ ///
+ /// The time-out interval, in milliseconds. If a nonzero value is specified, the function waits until the object is signaled
+ /// or the interval elapses. If dwMilliseconds is zero, the function does not enter a wait state if the object is not signaled;
+ /// it always returns immediately. If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
+ ///
+ ///
+ /// If the function succeeds, the return value indicates the event that caused the function to return.
+ /// It can be one of the WaitResult values.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
+
+ ///
+ /// Writes data to an area of memory in a specified process. The entire area to be written to must be accessible or
+ /// the operation fails.
+ ///
+ ///
+ /// A handle to the process memory to be modified. The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access
+ /// to the process.
+ ///
+ ///
+ /// A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the
+ /// system verifies that all data in the base address and memory of the specified size is accessible for write access,
+ /// and if it is not accessible, the function fails.
+ ///
+ ///
+ /// A pointer to the buffer that contains data to be written in the address space of the specified process.
+ ///
+ ///
+ /// The number of bytes to be written to the specified process.
+ ///
+ ///
+ /// A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter
+ /// is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.
+ ///
+ ///
+ /// If the function succeeds, the return value is nonzero. If the function fails, the return value is 0 (zero). To get
+ /// extended error information, call GetLastError.The function fails if the requested write operation crosses into an
+ /// area of the process that is inaccessible.
+ ///
+ [DllImport("kernel32.dll", SetLastError = true)]
+ public static extern bool WriteProcessMemory(
+ IntPtr hProcess,
+ IntPtr lpBaseAddress,
+ byte[] lpBuffer,
+ int dwSize,
+ out IntPtr lpNumberOfBytesWritten);
}