From 9965dc313a99491b3c54675883ec04244b9d5275 Mon Sep 17 00:00:00 2001 From: Mino Date: Wed, 8 Apr 2020 00:23:31 +0900 Subject: [PATCH] err... --- Dalamud.Bootstrap/BootstrapException.cs | 1 - Dalamud.Bootstrap/Bootstrapper.cs | 59 +++++ Dalamud.Bootstrap/{ => OS}/Windows/Process.cs | 124 ++++------ .../Windows/ProcessCreationOptions.cs | 2 +- .../{ => OS}/Windows/ProcessException.cs | 12 - Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs | 51 ++++ Dalamud.Bootstrap/OS/Windows/Raw/Kernel32.cs | 49 ++++ Dalamud.Bootstrap/OS/Windows/Raw/Ntdll.cs | 14 ++ Dalamud.Bootstrap/OS/Windows/Raw/Shell32.cs | 12 + .../OS/Windows/Raw/Structures.cs | 127 ++++++++++ Dalamud.Bootstrap/Windows/GameProcess.cs | 83 ------- Dalamud.Bootstrap/Windows/Win32.cs | 220 ------------------ Dalamud.Testing/BootstrapTests.cs | 29 +-- Dalamud.Testing/Dalamud.Testing.csproj | 4 + 14 files changed, 381 insertions(+), 406 deletions(-) rename Dalamud.Bootstrap/{ => OS}/Windows/Process.cs (64%) rename Dalamud.Bootstrap/{ => OS}/Windows/ProcessCreationOptions.cs (78%) rename Dalamud.Bootstrap/{ => OS}/Windows/ProcessException.cs (60%) create mode 100644 Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs create mode 100644 Dalamud.Bootstrap/OS/Windows/Raw/Kernel32.cs create mode 100644 Dalamud.Bootstrap/OS/Windows/Raw/Ntdll.cs create mode 100644 Dalamud.Bootstrap/OS/Windows/Raw/Shell32.cs create mode 100644 Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs delete mode 100644 Dalamud.Bootstrap/Windows/GameProcess.cs delete mode 100644 Dalamud.Bootstrap/Windows/Win32.cs diff --git a/Dalamud.Bootstrap/BootstrapException.cs b/Dalamud.Bootstrap/BootstrapException.cs index 9d9c23f88..354ac00a3 100644 --- a/Dalamud.Bootstrap/BootstrapException.cs +++ b/Dalamud.Bootstrap/BootstrapException.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; namespace Dalamud.Bootstrap { diff --git a/Dalamud.Bootstrap/Bootstrapper.cs b/Dalamud.Bootstrap/Bootstrapper.cs index 040168667..1d54a85ef 100644 --- a/Dalamud.Bootstrap/Bootstrapper.cs +++ b/Dalamud.Bootstrap/Bootstrapper.cs @@ -18,6 +18,11 @@ namespace Dalamud.Bootstrap m_options = options; } + public static void Test() + { + // + } + public void Launch(string exePath, string? commandLine) { commandLine = commandLine ?? ""; @@ -126,6 +131,60 @@ namespace Dalamud.Bootstrap throw new BootstrapException(exMessage, ex); } } + + /// + /// Recovers a key used in encrypting process arguments. + /// + /// A key recovered from the time when the process was created. + /// + /// This is possible because the key to encrypt arguments is just a high nibble value from GetTickCount() at the time when the process was created. + /// (Thanks Wintermute!) + /// + private uint GetArgumentEncryptionKey() + { + var createdTime = m_process.GetCreationTime(); + + // Get current tick + var currentDt = DateTime.Now; + var currentTick = Environment.TickCount; + + // We know that GetTickCount() is just a system uptime in milliseconds. + var delta = currentDt - createdTime; + var createdTick = (uint)currentTick - (uint)delta.TotalMilliseconds; + + // only the high nibble is used. + return createdTick & 0xFFFF_0000; + } + + /// + /// Reads command-line arguments from the game and decrypts them if necessary. + /// + /// + /// Command-line arguments that looks like this: + /// /DEV.TestSID =ABCD /UserPath =C:\Examples + /// + public string GetGameArguments() + { + var processArguments = m_process.GetProcessArguments(); + + // arg[0] is a path to exe(normally), arg[1] is actual stuff. + if (processArguments.Length < 2) + { + throw new ProcessException($"Process {m_process.GetProcessId()} only have {processArguments.Length} arguments. It must have atleast 2 arguments."); + } + + // We're interested in argument that contains session id + var argument = processArguments[1]; + + // If it's encrypted, we need to decrypt it first + if (EncryptedArgument.TryParse(argument, out var encryptedArgument)) + { + var key = GetArgumentEncryptionKey(); + argument = encryptedArgument.Decrypt(key); + } + + return argument; + } } internal sealed class PipePlatform : IPipePlatform diff --git a/Dalamud.Bootstrap/Windows/Process.cs b/Dalamud.Bootstrap/OS/Windows/Process.cs similarity index 64% rename from Dalamud.Bootstrap/Windows/Process.cs rename to Dalamud.Bootstrap/OS/Windows/Process.cs index 6479a3b30..668e6c0be 100644 --- a/Dalamud.Bootstrap/Windows/Process.cs +++ b/Dalamud.Bootstrap/OS/Windows/Process.cs @@ -1,18 +1,18 @@ +using Dalamud.Bootstrap.OS.Windows.Raw; using Dalamud.Bootstrap.Windows; using Microsoft.Win32.SafeHandles; using System; -using System.ComponentModel; using System.Runtime.InteropServices; using System.Text; -namespace Dalamud.Bootstrap +namespace Dalamud.Bootstrap.OS { /// /// A class that provides a wrapper over operations on Win32 process. /// - internal class Process : IDisposable + internal sealed class Process : IDisposable { - protected SafeProcessHandle Handle { get; set; } + private SafeProcessHandle m_handle; /// /// Creates a process object that can be used to manipulate process's internal state. @@ -20,65 +20,43 @@ namespace Dalamud.Bootstrap /// A process handle. Note that this functinon will take the ownership of the handle. public Process(SafeProcessHandle handle) { - Handle = handle; - } - - ~Process() - { - Dispose(false); + m_handle = handle; } public void Dispose() { - Dispose(true); - GC.SuppressFinalize(this); + m_handle?.Dispose(); + m_handle = null!; } - protected virtual void Dispose(bool disposing) - { - Handle?.Dispose(); - Handle = null!; - } - - public static Process Create(ProcessCreationOptions options) - { - // - - if (!Win32.CreateProcessW()) - { - ProcessException.ThrowLastOsError("Failed to create a new process."); - } - - // - // - } - - protected static SafeProcessHandle OpenHandle(uint pid, PROCESS_ACCESS_RIGHT access) - { - var handle = Win32.OpenProcess((uint)access, false, pid); - - if (handle.IsInvalid) - { - ProcessException.ThrowLastOsError(pid); - } - - return handle; - } - - public static Process Open(uint pid, PROCESS_ACCESS_RIGHT access) + public static Process Open(uint pid, PROCESS_ACCESS_RIGHTS access) { var handle = OpenHandle(pid, access); return new Process(handle); } - public uint GetPid() => Win32.GetProcessId(Handle); - - public void Terminate(uint exitCode = 0) + private static SafeProcessHandle OpenHandle(uint pid, PROCESS_ACCESS_RIGHTS access) { - if (!Win32.TerminateProcess(Handle, exitCode)) + var handle = Kernel32.OpenProcess((uint)access, false, pid); + + if (handle.IsInvalid) { - ProcessException.ThrowLastOsError(GetPid()); + ProcessException.ThrowLastOsError($"Could not open process {pid}"); + } + + return handle; + } + + private static uint GetProcessId(SafeProcessHandle handle) => Kernel32.GetProcessId(handle); + + public uint GetProcessId() => GetProcessId(m_handle); + + public void Terminate(int exitCode = 0) + { + if (!Kernel32.TerminateProcess(m_handle, exitCode)) + { + ProcessException.ThrowLastOsError($"Could not terminate process {GetProcessId()}"); } } @@ -88,26 +66,27 @@ namespace Dalamud.Bootstrap /// /// The number of bytes that is actually read. /// - protected int ReadMemory(IntPtr address, Span destination) + public int ReadMemory(IntPtr address, Span destination) { unsafe { fixed (byte* pDest = destination) { - if (!Win32.ReadProcessMemory(Handle, (void*)address, pDest, (IntPtr)destination.Length, out var bytesRead)) + if (!Kernel32.ReadProcessMemory(m_handle, address, pDest, (IntPtr)destination.Length, out var bytesRead)) { - ProcessException.ThrowLastOsError(GetPid()); + ProcessException.ThrowLastOsError($"Could not read process {GetProcessId()} memory at 0x{address.ToInt64():X8}"); } - // this is okay as the length of the span can't be longer than int.Max - return (int)bytesRead; + // This is okay because destination will never be longer than int.Max + return bytesRead.ToInt32(); } } } - protected void ReadMemoryExact(IntPtr address, Span destination) + public void ReadMemoryExact(IntPtr address, Span destination) { var totalBytesRead = 0; + while (totalBytesRead < destination.Length) { var bytesRead = ReadMemory(address + totalBytesRead, destination[totalBytesRead..]); @@ -115,7 +94,10 @@ namespace Dalamud.Bootstrap if (bytesRead == 0) { // prolly page fault; there's not much we can do here - ProcessException.ThrowLastOsError(GetPid()); + var readBeginAddr = address.ToInt64() + totalBytesRead; + var readEndAddr = address.ToInt64() + destination.Length; + + ProcessException.ThrowLastOsError($"Could not read process {GetProcessId()} memory at 0x{readBeginAddr:X8} .. 0x{readEndAddr:X8}; This likely means that page fault was hit."); } totalBytesRead += bytesRead; @@ -148,14 +130,11 @@ namespace Dalamud.Bootstrap unsafe { var info = new PROCESS_BASIC_INFORMATION(); - var status = Win32.NtQueryInformationProcess(Handle, PROCESSINFOCLASS.ProcessBasicInformation, &info, sizeof(PROCESS_BASIC_INFORMATION), (IntPtr*)IntPtr.Zero); + var status = Ntdll.NtQueryInformationProcess(m_handle, PROCESSINFOCLASS.ProcessBasicInformation, &info, sizeof(PROCESS_BASIC_INFORMATION), (IntPtr*)IntPtr.Zero); if (!status.Success) { - var message = $"A call to NtQueryInformationProcess failed. (Status: {status})"; - var pid = GetPid(); - - throw new ProcessException(message, pid); + throw new ProcessException($"Could not query information on process {GetProcessId()} (Status: {status})"); } return info.PebBaseAddress; @@ -186,17 +165,12 @@ namespace Dalamud.Bootstrap /// public DateTime GetCreationTime() { - unsafe + if (!Kernel32.GetProcessTimes(m_handle, out var creationTime, out var _, out var _, out var _)) { - FileTime creationTime, exitTime, kernelTime, userTime; - - if (!Win32.GetProcessTimes(Handle, &creationTime, &exitTime, &kernelTime, &userTime)) - { - ProcessException.ThrowLastOsError(GetPid()); - } - - return (DateTime)creationTime; + ProcessException.ThrowLastOsError($"Could not read process creation time from process {GetProcessId()}"); } + + return creationTime.ToDateTime(); } private string[] ParseCommandLineToArguments(ReadOnlySpan commandLine) @@ -208,12 +182,12 @@ namespace Dalamud.Bootstrap fixed (byte* pCommandLine = commandLine) { - argv = Win32.CommandLineToArgvW(pCommandLine, out argc); + argv = Shell32.CommandLineToArgvW(pCommandLine, out argc); } if (argv == null) { - ProcessException.ThrowLastOsError(GetPid()); + ProcessException.ThrowLastOsError($"Could not parse a command-line."); } try @@ -229,7 +203,7 @@ namespace Dalamud.Bootstrap } finally { - Win32.LocalFree(argv); + Kernel32.LocalFree(argv); } } } @@ -243,9 +217,9 @@ namespace Dalamud.Bootstrap // On success, receives the number of characters written to the buffer, not including the null-terminating character. var size = buffer.Capacity; - if (!Win32.QueryFullProcessImageNameW(Handle, 0, buffer, ref size)) + if (!Kernel32.QueryFullProcessImageNameW(m_handle, 0, buffer, ref size)) { - ProcessException.ThrowLastOsError(GetPid()); + ProcessException.ThrowLastOsError($"Could not read image path from process {GetProcessId()}"); } return buffer.ToString(); diff --git a/Dalamud.Bootstrap/Windows/ProcessCreationOptions.cs b/Dalamud.Bootstrap/OS/Windows/ProcessCreationOptions.cs similarity index 78% rename from Dalamud.Bootstrap/Windows/ProcessCreationOptions.cs rename to Dalamud.Bootstrap/OS/Windows/ProcessCreationOptions.cs index 7edd6fec0..e4092f179 100644 --- a/Dalamud.Bootstrap/Windows/ProcessCreationOptions.cs +++ b/Dalamud.Bootstrap/OS/Windows/ProcessCreationOptions.cs @@ -6,6 +6,6 @@ namespace Dalamud.Bootstrap.Windows public string? CommandLine { get; set; } = null; - public bool CreateSuspended { get; set; } = true; + public bool CreateSuspended { get; set; } } } diff --git a/Dalamud.Bootstrap/Windows/ProcessException.cs b/Dalamud.Bootstrap/OS/Windows/ProcessException.cs similarity index 60% rename from Dalamud.Bootstrap/Windows/ProcessException.cs rename to Dalamud.Bootstrap/OS/Windows/ProcessException.cs index b7baccdc9..87db59560 100644 --- a/Dalamud.Bootstrap/Windows/ProcessException.cs +++ b/Dalamud.Bootstrap/OS/Windows/ProcessException.cs @@ -8,24 +8,12 @@ namespace Dalamud.Bootstrap.Windows /// internal sealed class ProcessException : Exception { - public uint Pid { get; } - internal ProcessException() : base() { } internal ProcessException(string message) : base(message) { } internal ProcessException(string message, Exception innerException) : base(message, innerException) { } - internal ProcessException(string message, uint pid) : base(message) => Pid = pid; - - internal ProcessException(string message, uint pid, Exception innerException) : base(message, innerException) => Pid = pid; - - internal static void ThrowLastOsError(uint pid) - { - var inner = new Win32Exception(); - throw new ProcessException(inner.ToString(), pid, inner); - } - internal static void ThrowLastOsError(string message) { var inner = new Win32Exception(); diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs new file mode 100644 index 000000000..e181f276b --- /dev/null +++ b/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs @@ -0,0 +1,51 @@ +using System; + +namespace Dalamud.Bootstrap.OS.Windows.Raw +{ + // https://github.com/processhacker/processhacker/blob/c1a8c103f8afa1561dbac416f87523ea8f70b15e/phnt/include/ntpsapi.h#L96-L199 + internal enum PROCESSINFOCLASS : uint + { + ProcessBasicInformation = 0, + } + + // https://github.com/processhacker/processhacker/blob/0e9cf471e06a59cdb3a7c89f0b92b253a6a93999/phnt/include/ntpsapi.h#L5-L17 + [Flags] + internal enum PROCESS_ACCESS_RIGHTS : uint + { + PROCESS_TERMINATE = 0x1, + PROCESS_CREATE_THREAD = 0x2, + PROCESS_VM_OPERATION = 0x8, + PROCESS_VM_READ = 0x10, + PROCESS_VM_WRITE = 0x20, + PROCESS_DUP_HANDLE = 0x40, + PROCESS_CREATE_PROCESS = 0x80, + PROCESS_SET_QUOTA = 0x100, + PROCESS_SET_INFORMATION = 0x200, + PROCESS_QUERY_INFORMATION = 0x400, + PROCESS_SUSPEND_RESUME = 0x800, + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, + SYNCHRONIZE = 0x100000, + } + + [Flags] + internal enum PROCESS_CREATION_FLAGS : uint + { + CREATE_BREAKAWAY_FROM_JOB = 0x01000000, + CREATE_DEFAULT_ERROR_MODE = 0x04000000, + CREATE_NEW_CONSOLE = 0x00000010, + CREATE_NEW_PROCESS_GROUP = 0x00000200, + CREATE_NO_WINDOW = 0x08000000, + CREATE_PROTECTED_PROCESS = 0x00040000, + CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, + CREATE_SECURE_PROCESS = 0x00400000, + CREATE_SEPARATE_WOW_VDM = 0x00000800, + CREATE_SHARED_WOW_VDM = 0x00001000, + CREATE_SUSPENDED = 0x00000004, + CREATE_UNICODE_ENVIRONMENT = 0x00000400, + DEBUG_ONLY_THIS_PROCESS = 0x00000002, + DEBUG_PROCESS = 0x00000001, + DETACHED_PROCESS = 0x00000008, + EXTENDED_STARTUPINFO_PRESENT = 0x00080000, + INHERIT_PARENT_AFFINITY = 0x00010000, + } +} diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Kernel32.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Kernel32.cs new file mode 100644 index 000000000..ca346c995 --- /dev/null +++ b/Dalamud.Bootstrap/OS/Windows/Raw/Kernel32.cs @@ -0,0 +1,49 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace Dalamud.Bootstrap.OS.Windows.Raw +{ + internal static unsafe class Kernel32 + { + private const string Name = "kernel32"; + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern SafeProcessHandle OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool TerminateProcess(SafeProcessHandle hProcess, int uExitCode); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool ReadProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, void* lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesRead); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool WriteProcessMemory(SafeProcessHandle hProcess, IntPtr lpBaseAddress, void* lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesWritten); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern void* LocalFree(void* hMem); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi)] + public static extern uint GetProcessId(SafeProcessHandle hProcess); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool GetProcessTimes(SafeProcessHandle hProcess, out FILETIME lpCreationTime, out FILETIME lpExitTime, out FILETIME lpKernelTime, out FILETIME lpUserTime); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool QueryFullProcessImageNameW(SafeProcessHandle hProcess, uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpExeName, ref int lpdwSize); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateProcessW(void* lpApplicationName, void* lpCommandLine, SECURITY_ATTRIBUTES* lpProcessAttributes, SECURITY_ATTRIBUTES* lpThreadAttributes, uint bInheritHandles, uint dwCreationFlags, void* lpEnvironment, void* lpCurrentDirectory, void* lpStartupInfo, void* lpProcessInformation); + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CloseHandle(IntPtr hObject); + } +} diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Ntdll.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Ntdll.cs new file mode 100644 index 000000000..1f83e3de7 --- /dev/null +++ b/Dalamud.Bootstrap/OS/Windows/Raw/Ntdll.cs @@ -0,0 +1,14 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.Runtime.InteropServices; + +namespace Dalamud.Bootstrap.OS.Windows.Raw +{ + internal static unsafe class Ntdll + { + private const string Name = "ntdll"; + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true)] + public static extern NTSTATUS NtQueryInformationProcess(SafeProcessHandle processHandle, PROCESSINFOCLASS processInfoClass, void* processInformation, int processInformationLength, IntPtr* returnLength); + } +} diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Shell32.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Shell32.cs new file mode 100644 index 000000000..970017532 --- /dev/null +++ b/Dalamud.Bootstrap/OS/Windows/Raw/Shell32.cs @@ -0,0 +1,12 @@ +using System.Runtime.InteropServices; + +namespace Dalamud.Bootstrap.OS.Windows.Raw +{ + internal static unsafe class Shell32 + { + private const string Name = "shell32"; + + [DllImport(Name, CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true)] + public static extern char** CommandLineToArgvW(void* lpCmdLine, out int pNumArgs); + } +} diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs new file mode 100644 index 000000000..d2b1f2bc9 --- /dev/null +++ b/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs @@ -0,0 +1,127 @@ +using System; +using System.Runtime.InteropServices; + +namespace Dalamud.Bootstrap.OS.Windows.Raw +{ + [StructLayout(LayoutKind.Sequential)] + internal struct NTSTATUS + { + public uint Code; + + public NTSTATUS(uint value) + { + Code = value; + } + + /// + /// Equivalent to NT_SUCCESS + /// + public bool Success => Code <= 0x7FFFFFFF; + + /// + /// Equivalent to NT_INFORMATION + /// + public bool Information => 0x40000000 <= Code && Code <= 0x7FFFFFFF; + + /// + /// Equivalent to NT_WARNING + /// + public bool Warning => 0x80000000 <= Code && Code <= 0xBFFFFFFF; + + /// + /// Equivalent to NT_ERROR + /// + public bool Error => 0xC0000000 <= Code; + + public override string ToString() => $"{Code:X8}"; + + public static implicit operator uint(NTSTATUS status) => status.Code; + + public static implicit operator NTSTATUS(uint code) => new NTSTATUS(code); + } + + [StructLayout(LayoutKind.Sequential)] + internal struct FILETIME + { + public uint LowDateTime; + + public uint HighDateTime; + + public long FileTime => ((long)HighDateTime << 32) | LowDateTime; + + public DateTime ToDateTime() => DateTime.FromFileTime(FileTime); + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct UNICODE_STRING + { + public ushort Length; + public ushort MaximumLength; + public IntPtr Buffer; + } + + // https://github.com/processhacker/processhacker/blob/e43d7c0513ec5368c3309a58c9f2c2a3ca5de367/phnt/include/ntpsapi.h#L272 + [StructLayout(LayoutKind.Sequential)] + internal struct PROCESS_BASIC_INFORMATION + { + public NTSTATUS ExitStatus; + public IntPtr PebBaseAddress; + public IntPtr AffinityMask; + public IntPtr BasePriority; + public IntPtr UniqueProcessId; + public IntPtr InheritedFromUniqueProcessId; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PEB + { + // https://github.com/processhacker/processhacker/blob/238287786b80abad647b988e60f69090cab4c8fe/phnt/include/ntpebteb.h#L57-L218 + public byte InheritedAddressSpace; + public byte ReadImageFileExecOptions; + public byte BeingDebugged; + public byte BitField; + public IntPtr Mutant; + public IntPtr ImageBaseAddress; + public IntPtr Ldr; + public IntPtr ProcessParameters; + // ..snip.. we don't care about others + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RTL_USER_PROCESS_PARAMETERS + { + + public uint MaximumLength; + public uint LengthInitialized; + public uint Flags; + public uint DebugFlags; + public IntPtr ConsoleHandle; + public uint ConsoleFlags; + public IntPtr StandardInput; + public IntPtr StandardOutput; + public IntPtr StandardError; + public UNICODE_STRING CurrentDirectory_DosPath; + public IntPtr CurrentDirectory_Handle; + public UNICODE_STRING DllPath; + public UNICODE_STRING ImagePathName; + public UNICODE_STRING CommandLine; + // ..snip.. don't care + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct SECURITY_ATTRIBUTES + { + public uint Length; + + public SECURITY_DESCRIPTOR* SecurityDescriptor; + + [MarshalAs(UnmanagedType.Bool)] + public bool InheritHandle; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SECURITY_DESCRIPTOR + { + + } +} diff --git a/Dalamud.Bootstrap/Windows/GameProcess.cs b/Dalamud.Bootstrap/Windows/GameProcess.cs deleted file mode 100644 index b95af385f..000000000 --- a/Dalamud.Bootstrap/Windows/GameProcess.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using Dalamud.Bootstrap.SqexArg; -using Dalamud.Bootstrap.Windows; -using Microsoft.Win32.SafeHandles; - -namespace Dalamud.Bootstrap -{ - internal sealed class GameProcess : Process - { - public GameProcess(SafeProcessHandle handle) : base(handle) { } - - public static GameProcess Open(uint pid) - { - const PROCESS_ACCESS_RIGHT access = PROCESS_ACCESS_RIGHT.PROCESS_VM_OPERATION - | PROCESS_ACCESS_RIGHT.PROCESS_VM_READ - // | PROCESS_ACCESS_RIGHT.PROCESS_VM_WRITE // we don't need it for now - | PROCESS_ACCESS_RIGHT.PROCESS_QUERY_LIMITED_INFORMATION - | PROCESS_ACCESS_RIGHT.PROCESS_QUERY_INFORMATION - | PROCESS_ACCESS_RIGHT.PROCESS_CREATE_THREAD - | PROCESS_ACCESS_RIGHT.PROCESS_TERMINATE; - - // TODO: unfuck VM_WRITE? - - var handle = OpenHandle(pid, access); - - return new GameProcess(handle); - } - - /// - /// Recovers a key used in encrypting process arguments. - /// - /// A key recovered from the time when the process was created. - /// - /// This is possible because the key to encrypt arguments is just a high nibble value from GetTickCount() at the time when the process was created. - /// (Thanks Wintermute!) - /// - private uint GetArgumentEncryptionKey() - { - var createdTime = GetCreationTime(); - - // Get current tick - var currentDt = DateTime.Now; - var currentTick = Environment.TickCount; - - // We know that GetTickCount() is just a system uptime in milliseconds. - var delta = currentDt - createdTime; - var createdTick = (uint)currentTick - (uint)delta.TotalMilliseconds; - - // only the high nibble is used. - return createdTick & 0xFFFF_0000; - } - - /// - /// Reads command-line arguments from the game and decrypts them if necessary. - /// - /// - /// Command-line arguments that looks like this: - /// /DEV.TestSID =ABCD /UserPath =C:\Examples - /// - public string GetGameArguments() - { - var processArguments = GetProcessArguments(); - - // arg[0] is a path to exe(normally), arg[1] is actual stuff. - if (processArguments.Length < 2) - { - throw new ProcessException($"There are no process arguments to parse.", GetPid()); - } - - // We're interested in argument that contains session id - var argument = processArguments[1]; - - // If it's encrypted, we need to decrypt it first - if (EncryptedArgument.TryParse(argument, out var encryptedArgument)) - { - var key = GetArgumentEncryptionKey(); - argument = encryptedArgument.Decrypt(key); - } - - return argument; - } - } -} diff --git a/Dalamud.Bootstrap/Windows/Win32.cs b/Dalamud.Bootstrap/Windows/Win32.cs deleted file mode 100644 index bb207bba7..000000000 --- a/Dalamud.Bootstrap/Windows/Win32.cs +++ /dev/null @@ -1,220 +0,0 @@ -using Microsoft.Win32.SafeHandles; -using System; -using System.Runtime.InteropServices; -using System.Text; - -namespace Dalamud.Bootstrap.Windows -{ - internal static unsafe class Win32 - { - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - public static extern SafeProcessHandle OpenProcess(uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwProcessId); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool TerminateProcess(SafeProcessHandle hProcess, uint uExitCode); - - [DllImport("ntdll", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - public static extern NtStatus NtQueryInformationProcess(SafeProcessHandle processHandle, PROCESSINFOCLASS processInfoClass, void* processInformation, int processInformationLength, out IntPtr returnLength); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool ReadProcessMemory(SafeProcessHandle hProcess, void* lpBaseAddress, void* lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesRead); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool WriteProcessMemory(SafeProcessHandle hProcess, void* lpBaseAddress, void* lpBuffer, IntPtr nSize, out IntPtr lpNumberOfBytesWritten); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - public static extern void* LocalFree(void* hMem); - - [DllImport("shell32", CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true)] - public static extern char** CommandLineToArgvW(void* lpCmdLine, out int pNumArgs); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi)] - public static extern uint GetProcessId(SafeProcessHandle hProcess); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetProcessTimes(SafeProcessHandle hProcess, FileTime* lpCreationTime, FileTime* lpExitTime, FileTime* lpKernelTime, FileTime* lpUserTime); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool QueryFullProcessImageNameW(SafeProcessHandle hProcess, uint dwFlags, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpExeName, ref int lpdwSize); - - [DllImport("kernel32", CallingConvention = CallingConvention.Winapi, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool CreateProcessW(string lpApplicationName, StringBuilder lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles, PROCESS_CREATION_FLAG dwCreationFlags, lpEnvironment, string lpCurrentDirectory, lpStartupInfo, lpProcessInformation); - } - - [StructLayout(LayoutKind.Sequential)] - internal struct NtStatus - { - public uint Value { get; } - - public NtStatus(uint value) - { - Value = value; - } - - /// - /// Equivalent to NT_SUCCESS - /// - public bool Success => Value <= 0x7FFFFFFF; - - /// - /// Equivalent to NT_INFORMATION - /// - public bool Information => 0x40000000 <= Value && Value <= 0x7FFFFFFF; - - /// - /// Equivalent to NT_WARNING - /// - public bool Warning => 0x80000000 <= Value && Value <= 0xBFFFFFFF; - - /// - /// Equivalent to NT_ERROR - /// - public bool Error => 0xC0000000 <= Value; - - public override string ToString() => $"0x{Value:X8}"; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct FileTime - { - public uint LowDateTime; - - public uint HighDateTime; - - public static explicit operator DateTime(FileTime value) - { - var time = ((long)value.HighDateTime << 32) | value.LowDateTime; - - return DateTime.FromFileTime(time); - } - } - - [StructLayout(LayoutKind.Sequential)] - internal struct UNICODE_STRING - { - public ushort Length; - public ushort MaximumLength; - public IntPtr Buffer; - } - - internal enum PROCESSINFOCLASS : uint - { - ProcessBasicInformation = 0, - ProcessDebugPort = 7, - ProcessWow64Information = 26, - ProcessImageFileName = 27, - ProcessBreakOnTermination = 29, - ProcessSubsystemInformation = 75, - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PROCESS_BASIC_INFORMATION - { - // https://github.com/processhacker/processhacker/blob/e43d7c0513ec5368c3309a58c9f2c2a3ca5de367/phnt/include/ntpsapi.h#L272 - public NtStatus ExitStatus; - public IntPtr PebBaseAddress; - public IntPtr AffinityMask; - public IntPtr BasePriority; - public IntPtr UniqueProcessId; - public IntPtr InheritedFromUniqueProcessId; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct PEB - { - // https://github.com/processhacker/processhacker/blob/238287786b80abad647b988e60f69090cab4c8fe/phnt/include/ntpebteb.h#L57-L218 - public byte InheritedAddressSpace; - public byte ReadImageFileExecOptions; - public byte BeingDebugged; - public byte BitField; - public IntPtr Mutant; - public IntPtr ImageBaseAddress; - public IntPtr Ldr; - public IntPtr ProcessParameters; - // ..snip.. - } - - [StructLayout(LayoutKind.Sequential)] - internal struct RTL_USER_PROCESS_PARAMETERS - { - - public uint MaximumLength; - public uint LengthInitialized; - public uint Flags; - public uint DebugFlags; - public IntPtr ConsoleHandle; - public uint ConsoleFlags; - public IntPtr StandardInput; - public IntPtr StandardOutput; - public IntPtr StandardError; - public UNICODE_STRING CurrentDirectory_DosPath; - public IntPtr CurrentDirectory_Handle; - public UNICODE_STRING DllPath; - public UNICODE_STRING ImagePathName; - public UNICODE_STRING CommandLine; - // ..snip.. - } - - [Flags] - internal enum PROCESS_ACCESS_RIGHT : uint - { - PROCESS_TERMINATE = 0x1, - PROCESS_CREATE_THREAD = 0x2, - PROCESS_VM_OPERATION = 0x8, - PROCESS_VM_READ = 0x10, - PROCESS_VM_WRITE = 0x20, - PROCESS_DUP_HANDLE = 0x40, - PROCESS_CREATE_PROCESS = 0x80, - PROCESS_SET_QUOTA = 0x100, - PROCESS_SET_INFORMATION = 0x200, - PROCESS_QUERY_INFORMATION = 0x400, - PROCESS_SUSPEND_RESUME = 0x800, - PROCESS_QUERY_LIMITED_INFORMATION = 0x1000, - SYNCHRONIZE = 0x100000, - } - - [Flags] - internal enum PROCESS_CREATION_FLAG : uint - { - CREATE_BREAKAWAY_FROM_JOB = 0x01000000, - CREATE_DEFAULT_ERROR_MODE = 0x04000000, - CREATE_NEW_CONSOLE = 0x00000010, - CREATE_NEW_PROCESS_GROUP = 0x00000200, - CREATE_NO_WINDOW = 0x08000000, - CREATE_PROTECTED_PROCESS = 0x00040000, - CREATE_PRESERVE_CODE_AUTHZ_LEVEL = 0x02000000, - CREATE_SECURE_PROCESS = 0x00400000, - CREATE_SEPARATE_WOW_VDM = 0x00000800, - CREATE_SHARED_WOW_VDM = 0x00001000, - CREATE_SUSPENDED = 0x00000004, - CREATE_UNICODE_ENVIRONMENT = 0x00000400, - DEBUG_ONLY_THIS_PROCESS = 0x00000002, - DEBUG_PROCESS = 0x00000001, - DETACHED_PROCESS = 0x00000008, - EXTENDED_STARTUPINFO_PRESENT = 0x00080000, - INHERIT_PARENT_AFFINITY = 0x00010000, - } - - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct SECURITY_ATTRIBUTES - { - public uint Length; - - public SECURITY_DESCRIPTOR* SecurityDescriptor; - - [MarshalAs(UnmanagedType.Bool)] - public bool InheritHandle; - } - - [StructLayout(LayoutKind.Sequential)] - internal struct SECURITY_DESCRIPTOR - { - - } -} diff --git a/Dalamud.Testing/BootstrapTests.cs b/Dalamud.Testing/BootstrapTests.cs index f7aab3d3f..07c30b395 100644 --- a/Dalamud.Testing/BootstrapTests.cs +++ b/Dalamud.Testing/BootstrapTests.cs @@ -1,14 +1,15 @@ -using System; -using Xunit; - -namespace Dalamud.Testing -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} +using Dalamud.Bootstrap; +using Xunit; + +namespace Dalamud.Testing +{ + public class BootstrapTests + { + [Fact] + public void Test1() + { + Bootstrapper.Test(); + } + + } +} diff --git a/Dalamud.Testing/Dalamud.Testing.csproj b/Dalamud.Testing/Dalamud.Testing.csproj index e6f67d320..a041b0ba6 100644 --- a/Dalamud.Testing/Dalamud.Testing.csproj +++ b/Dalamud.Testing/Dalamud.Testing.csproj @@ -20,4 +20,8 @@ + + + +