From f30ebe51665bc0ee1a7a489b513c5a9c7dde5908 Mon Sep 17 00:00:00 2001 From: goat <16760685+goaaats@users.noreply.github.com> Date: Wed, 17 Nov 2021 19:46:33 +0100 Subject: [PATCH] refactor(Injector): switch to file-scoped namespaces --- Dalamud.Injector/EntryPoint.cs | 513 +++++---- Dalamud.Injector/Injector.cs | 503 +++++---- Dalamud.Injector/NativeFunctions.cs | 1563 +++++++++++++-------------- 3 files changed, 1288 insertions(+), 1291 deletions(-) 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); }