diff --git a/Dalamud.Bootstrap/GameProcess.cs b/Dalamud.Bootstrap/GameProcess.cs
index 3ff3c0855..d0d864131 100644
--- a/Dalamud.Bootstrap/GameProcess.cs
+++ b/Dalamud.Bootstrap/GameProcess.cs
@@ -3,38 +3,305 @@ using Dalamud.Bootstrap.OS.Windows;
using Dalamud.Bootstrap.OS.Windows.Raw;
using Microsoft.Win32.SafeHandles;
using System;
+using System.Runtime.InteropServices;
namespace Dalamud.Bootstrap
{
public sealed partial class GameProcess : IDisposable
{
- private Process m_process;
+ private IntPtr m_handle;
- // maybe saved acl shit
-
- private GameProcess(Process process)
+ public GameProcess(IntPtr handle)
{
- m_process = process;
+ m_handle = handle;
+ }
+
+ ~GameProcess()
+ {
+ Dispose(false);
}
public void Dispose()
{
- m_process?.Dispose();
- m_process = null!;
+ Dispose(true);
+ GC.SuppressFinalize(true);
}
- // ///
- // ///
- // ///
- // /// A process handle.
- // private static void AllowHandleAccess(Process handle)
- // {
+ private void Dispose(bool disposing)
+ {
+ if (m_handle != IntPtr.Zero)
+ {
+ Kernel32.CloseHandle(m_handle);
- // }
+ m_handle = IntPtr.Zero;
+ }
+ }
- // private static void DenyHandleAccess(SafeProcessHandle handle)
- // {
+ private static IntPtr OpenProcessHandle(uint pid, uint access)
+ {
+ var handle = Kernel32.OpenProcess(access, false, pid);
- // }
+ if (handle == IntPtr.Zero)
+ {
+ ProcessException.ThrowLastOsError();
+ }
+
+ return handle;
+ }
+
+ public static GameProcess Open(uint pid)
+ {
+ //
+ var secHandle = OpenProcessHandle(pid, (uint)(PROCESS_ACCESS_RIGHTS.READ_CONTROL | PROCESS_ACCESS_RIGHTS.WRITE_DAC));
+ try
+ {
+ RelaxProcessHandle(secHandle, (_) =>
+ {
+
+ });
+ }
+ finally
+ {
+ Kernel32.CloseHandle(secHandle);
+ }
+ }
+
+ private static void RelaxProcessHandle(IntPtr handle, Action scope)
+ {
+ // relax shit
+ unsafe
+ {
+ SECURITY_DESCRIPTOR* pSecurityDescOrig;
+ ACL* pDaclOrig, pDaclRelaxed;
+
+ var error = Advapi32.GetSecurityInfo(
+ handle,
+ SE_OBJECT_TYPE.SE_KERNEL_OBJECT,
+ SECURITY_INFORMATION.DACL_SECURITY_INFORMATION,
+ null,
+ null,
+ &pDaclOrig,
+ null,
+ &pSecurityDescOrig
+ );
+
+ if (error != 0)
+ {
+ throw new ProcessException();
+ }
+
+ try
+ {
+ scope(handle);
+ }
+ finally
+ {
+ // Restore permission
+
+ Kernel32.LocalFree(pSecurityDescOrig);
+ }
+ }
+
+
+
+ }
+
+ public void GetSecurityInfo()
+ {
+
+ var error = Advapi32.GetSecurityInfo(m_handle, SE_OBJECT_TYPE.SE_KERNEL_OBJECT, SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, );
+
+ if (error != 0 /* ERROR_SUCCESS */)
+ {
+ throw new ProcessException($"Could not read a security info. (Error {error})");
+ }
+
+
+ }
+
+ private static void AllowDacl(Process process)
+ {
+
+ }
+
+ ///
+ /// Reads process memory.
+ ///
+ ///
+ /// A number of bytes that is actually read.
+ ///
+ ///
+ /// Thrown when failed to read memory.
+ ///
+ public int ReadMemory(IntPtr address, Span destination)
+ {
+ unsafe
+ {
+ fixed (byte* pDest = destination)
+ {
+ if (!Kernel32.ReadProcessMemory(Handle, address, pDest, (IntPtr)destination.Length, out var bytesRead))
+ {
+ ProcessException.ThrowLastOsError();
+ }
+
+ // This is okay because destination will never be longer than int.Max
+ return bytesRead.ToInt32();
+ }
+ }
+ }
+
+ ///
+ /// Reads the exact number of bytes required to fill the buffer.
+ ///
+ ///
+ /// Thrown when failed to read memory.
+ ///
+ public void ReadMemoryExact(IntPtr address, Span destination)
+ {
+ var totalBytesRead = 0;
+
+ while (totalBytesRead < destination.Length)
+ {
+ var bytesRead = ReadMemory(address + totalBytesRead, destination[totalBytesRead..]);
+
+ if (bytesRead == 0)
+ {
+ // prolly page fault; there's not much we can do here
+ var readBeginAddr = address.ToInt64() + totalBytesRead;
+ var readEndAddr = address.ToInt64() + destination.Length;
+
+ ProcessException.ThrowLastOsError();
+ }
+
+ totalBytesRead += bytesRead;
+ }
+ }
+
+
+ ///
+ /// Thrown when failed to read memory.
+ ///
+ public byte[] ReadMemoryExact(IntPtr address, int length)
+ {
+ var buffer = new byte[length];
+ ReadMemoryExact(address, buffer);
+
+ return buffer;
+ }
+
+
+ ///
+ /// Thrown when failed to read memory.
+ ///
+ public void ReadMemoryExact(IntPtr address, ref T value) where T : unmanaged
+ {
+ var span = MemoryMarshal.CreateSpan(ref value, 1); // span should never leave this function since it has unbounded lifetime.
+ var buffer = MemoryMarshal.AsBytes(span);
+
+ ReadMemoryExact(address, buffer);
+ }
+
+
+ private IntPtr GetPebAddress()
+ {
+ unsafe
+ {
+ PROCESS_BASIC_INFORMATION info = default;
+
+ var status = Ntdll.NtQueryInformationProcess(Handle, PROCESSINFOCLASS.ProcessBasicInformation, &info, sizeof(PROCESS_BASIC_INFORMATION), (IntPtr*)IntPtr.Zero);
+
+ if (!status.Success)
+ {
+ throw new ProcessException($"Could not query information on process. (Status: {status})");
+ }
+
+ return info.PebBaseAddress;
+ }
+ }
+
+ ///
+ /// Reads command-line arguments from the process.
+ ///
+ public string[] GetProcessArguments()
+ {
+ PEB peb = default;
+ RTL_USER_PROCESS_PARAMETERS procParam = default;
+
+ // Find where the command line is allocated
+ var pebAddr = GetPebAddress();
+ ReadMemoryExact(pebAddr, ref peb);
+ ReadMemoryExact(peb.ProcessParameters, ref procParam);
+
+ // Read the command line (which is utf16-like string)
+ var commandLine = ReadMemoryExact(procParam.CommandLine.Buffer, procParam.CommandLine.Length);
+
+ return ParseCommandLineToArguments(commandLine);
+ }
+
+ ///
+ /// Returns a time when the process was started.
+ ///
+ public DateTime GetCreationTime()
+ {
+ if (!Kernel32.GetProcessTimes(Handle, out var creationTime, out var _, out var _, out var _))
+ {
+ ProcessException.ThrowLastOsError();
+ }
+
+ return creationTime.ToDateTime();
+ }
+
+ private string[] ParseCommandLineToArguments(ReadOnlySpan commandLine)
+ {
+ unsafe
+ {
+ char** argv;
+ int argc;
+
+ fixed (byte* pCommandLine = commandLine)
+ {
+ argv = Shell32.CommandLineToArgvW(pCommandLine, out argc);
+ }
+
+ if (argv == null)
+ {
+ ProcessException.ThrowLastOsError();
+ }
+
+ // NOTE: argv must be deallocated via LocalFree when we're done
+ try
+ {
+ var arguments = new string[argc];
+
+ for (var i = 0; i < argc; i++)
+ {
+ arguments[i] = new string(argv[i]);
+ }
+
+ return arguments;
+ }
+ finally
+ {
+ Kernel32.LocalFree(argv);
+ }
+ }
+ }
+
+ public string GetImageFilePath()
+ {
+ var buffer = new StringBuilder(300);
+
+ // From docs:
+ // On input, specifies the size of the lpExeName buffer, in characters.
+ // On success, receives the number of characters written to the buffer, not including the null-terminating character.
+ var size = buffer.Capacity;
+
+ if (!Kernel32.QueryFullProcessImageNameW(Handle, 0, buffer, ref size))
+ {
+ ProcessException.ThrowLastOsError();
+ }
+
+ return buffer.ToString();
+ }
}
}
diff --git a/Dalamud.Bootstrap/OS/Windows/Process.cs b/Dalamud.Bootstrap/OS/Windows/Process.cs
deleted file mode 100644
index 082cade9c..000000000
--- a/Dalamud.Bootstrap/OS/Windows/Process.cs
+++ /dev/null
@@ -1,215 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using System.Text;
-using Dalamud.Bootstrap.OS.Windows.Raw;
-using Microsoft.Win32.SafeHandles;
-
-namespace Dalamud.Bootstrap.OS.Windows
-{
- ///
- /// Provides a thin wrapper around process API.
- ///
- internal sealed class Process : IDisposable
- {
- ///
- /// A process handle that can be passed to PInvoke. Note that it should never outlive a process object.
- /// This handle will be disposed when the process object is disposed.
- ///
- public IntPtr Handle { get; private set; }
-
- public Process(IntPtr handle)
- {
- Handle = handle;
- }
-
- public void Dispose()
- {
- if (Handle != IntPtr.Zero)
- {
- Kernel32.CloseHandle(Handle);
- }
-
- Handle = IntPtr.Zero;
- }
-
- ///
- /// Reads process memory.
- ///
- ///
- /// A number of bytes that is actually read.
- ///
- ///
- /// Thrown when failed to read memory.
- ///
- public int ReadMemory(IntPtr address, Span destination)
- {
- unsafe
- {
- fixed (byte* pDest = destination)
- {
- if (!Kernel32.ReadProcessMemory(Handle, address, pDest, (IntPtr)destination.Length, out var bytesRead))
- {
- ProcessException.ThrowLastOsError();
- }
-
- // This is okay because destination will never be longer than int.Max
- return bytesRead.ToInt32();
- }
- }
- }
-
- ///
- /// Reads the exact number of bytes required to fill the buffer.
- ///
- ///
- /// Thrown when failed to read memory.
- ///
- public void ReadMemoryExact(IntPtr address, Span destination)
- {
- var totalBytesRead = 0;
-
- while (totalBytesRead < destination.Length)
- {
- var bytesRead = ReadMemory(address + totalBytesRead, destination[totalBytesRead..]);
-
- if (bytesRead == 0)
- {
- // prolly page fault; there's not much we can do here
- var readBeginAddr = address.ToInt64() + totalBytesRead;
- var readEndAddr = address.ToInt64() + destination.Length;
-
- ProcessException.ThrowLastOsError();
- }
-
- totalBytesRead += bytesRead;
- }
- }
-
-
- ///
- /// Thrown when failed to read memory.
- ///
- public byte[] ReadMemoryExact(IntPtr address, int length)
- {
- var buffer = new byte[length];
- ReadMemoryExact(address, buffer);
-
- return buffer;
- }
-
-
- ///
- /// Thrown when failed to read memory.
- ///
- public void ReadMemoryExact(IntPtr address, ref T value) where T : unmanaged
- {
- var span = MemoryMarshal.CreateSpan(ref value, 1); // span should never leave this function since it has unbounded lifetime.
- var buffer = MemoryMarshal.AsBytes(span);
-
- ReadMemoryExact(address, buffer);
- }
-
-
- private IntPtr GetPebAddress()
- {
- unsafe
- {
- PROCESS_BASIC_INFORMATION info = default;
-
- var status = Ntdll.NtQueryInformationProcess(Handle, PROCESSINFOCLASS.ProcessBasicInformation, &info, sizeof(PROCESS_BASIC_INFORMATION), (IntPtr*)IntPtr.Zero);
-
- if (!status.Success)
- {
- throw new ProcessException($"Could not query information on process. (Status: {status})");
- }
-
- return info.PebBaseAddress;
- }
- }
-
- ///
- /// Reads command-line arguments from the process.
- ///
- public string[] GetProcessArguments()
- {
- PEB peb = default;
- RTL_USER_PROCESS_PARAMETERS procParam = default;
-
- // Find where the command line is allocated
- var pebAddr = GetPebAddress();
- ReadMemoryExact(pebAddr, ref peb);
- ReadMemoryExact(peb.ProcessParameters, ref procParam);
-
- // Read the command line (which is utf16-like string)
- var commandLine = ReadMemoryExact(procParam.CommandLine.Buffer, procParam.CommandLine.Length);
-
- return ParseCommandLineToArguments(commandLine);
- }
-
- ///
- /// Returns a time when the process was started.
- ///
- public DateTime GetCreationTime()
- {
- if (!Kernel32.GetProcessTimes(Handle, out var creationTime, out var _, out var _, out var _))
- {
- ProcessException.ThrowLastOsError();
- }
-
- return creationTime.ToDateTime();
- }
-
- private string[] ParseCommandLineToArguments(ReadOnlySpan commandLine)
- {
- unsafe
- {
- char** argv;
- int argc;
-
- fixed (byte* pCommandLine = commandLine)
- {
- argv = Shell32.CommandLineToArgvW(pCommandLine, out argc);
- }
-
- if (argv == null)
- {
- ProcessException.ThrowLastOsError();
- }
-
- // NOTE: argv must be deallocated via LocalFree when we're done
- try
- {
- var arguments = new string[argc];
-
- for (var i = 0; i < argc; i++)
- {
- arguments[i] = new string(argv[i]);
- }
-
- return arguments;
- }
- finally
- {
- Kernel32.LocalFree(argv);
- }
- }
- }
-
- public string GetImageFilePath()
- {
- var buffer = new StringBuilder(300);
-
- // From docs:
- // On input, specifies the size of the lpExeName buffer, in characters.
- // On success, receives the number of characters written to the buffer, not including the null-terminating character.
- var size = buffer.Capacity;
-
- if (!Kernel32.QueryFullProcessImageNameW(Handle, 0, buffer, ref size))
- {
- ProcessException.ThrowLastOsError();
- }
-
- return buffer.ToString();
- }
- }
-}
diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Advapi32.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Advapi32.cs
index f95a3dbf4..c36db8320 100644
--- a/Dalamud.Bootstrap/OS/Windows/Raw/Advapi32.cs
+++ b/Dalamud.Bootstrap/OS/Windows/Raw/Advapi32.cs
@@ -18,6 +18,9 @@ namespace Dalamud.Bootstrap.OS.Windows.Raw
public static extern void BuildExplicitAccessWithNameW(out EXPLICIT_ACCESS_W pExplicitAccess, string pTrusteeName, uint AccessPermissions, ACCESS_MODE AccessMode, uint Inheritance);
[DllImport(Name, CallingConvention = CallingConvention.Winapi)]
- public static extern uint GetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo);
+ public static extern uint GetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE ObjectType, SECURITY_INFORMATION SecurityInfo, SID** ppsidOwner, SID** ppsidGroup, ACL** ppDacl, ACL** ppSacl, SECURITY_DESCRIPTOR** ppSecurityDescriptor);
+
+ [DllImport(Name, CallingConvention = CallingConvention.Winapi)]
+ public static extern uint SetSecurityInfo(IntPtr handle, SE_OBJECT_TYPE _OBJECT_TYPE, SECURITY_INFORMATION SecurityInfo, SID* psidOwner, SID* psidGroup, ACL* pDacl, ACL* pSacl);
}
}
diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs
index d2f831795..ea1e59648 100644
--- a/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs
+++ b/Dalamud.Bootstrap/OS/Windows/Raw/Constants.cs
@@ -24,6 +24,8 @@ namespace Dalamud.Bootstrap.OS.Windows.Raw
PROCESS_QUERY_INFORMATION = 0x400,
PROCESS_SUSPEND_RESUME = 0x800,
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000,
+ READ_CONTROL = 0x20000,
+ WRITE_DAC = 0x40000,
SYNCHRONIZE = 0x100000,
}
@@ -124,4 +126,23 @@ namespace Dalamud.Bootstrap.OS.Windows.Raw
SE_REGISTRY_WOW64_32KEY,
SE_REGISTRY_WOW64_64KEY
}
+
+ internal enum SECURITY_INFORMATION : uint
+ {
+ // https://docs.rs/winapi/0.3.8/src/winapi/um/winnt.rs.html#2880
+ OWNER_SECURITY_INFORMATION = 0x00000001,
+ GROUP_SECURITY_INFORMATION = 0x00000002,
+ DACL_SECURITY_INFORMATION = 0x00000004,
+ SACL_SECURITY_INFORMATION = 0x00000008,
+ LABEL_SECURITY_INFORMATION = 0x00000010,
+ ATTRIBUTE_SECURITY_INFORMATION = 0x00000020,
+ SCOPE_SECURITY_INFORMATION = 0x00000040,
+ PROCESS_TRUST_LABEL_SECURITY_INFORMATION = 0x00000080,
+ ACCESS_FILTER_SECURITY_INFORMATION = 0x00000100,
+ BACKUP_SECURITY_INFORMATION = 0x00010000,
+ UNPROTECTED_SACL_SECURITY_INFORMATION = 0x10000000,
+ UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000,
+ PROTECTED_SACL_SECURITY_INFORMATION = 0x40000000,
+ PROTECTED_DACL_SECURITY_INFORMATION = 0x80000000,
+ }
}
diff --git a/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs b/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs
index 6492bf4d7..16cb4ccfe 100644
--- a/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs
+++ b/Dalamud.Bootstrap/OS/Windows/Raw/Structures.cs
@@ -131,7 +131,7 @@ namespace Dalamud.Bootstrap.OS.Windows.Raw
public IntPtr Dacl;
}
- [StructLayout(LayoutKind.Explicit)]
+ [StructLayout(LayoutKind.Sequential)]
internal unsafe struct TRUSTEE_W
{
public TRUSTEE_W* pMultipleTrustee;
@@ -144,19 +144,36 @@ namespace Dalamud.Bootstrap.OS.Windows.Raw
[StructLayout(LayoutKind.Sequential)]
internal struct EXPLICIT_ACCESS_W
{
- uint grfAccessPermissions;
- ACCESS_MODE grfAccessMode;
- uint grfInheritance;
- TRUSTEE_W Trustee;
+ public uint grfAccessPermissions;
+ public ACCESS_MODE grfAccessMode;
+ public uint grfInheritance;
+ public TRUSTEE_W Trustee;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SID
+ {
+ // NOTE: this structure actually has no fixed length and therefore should not be used directly.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
+ byte Revision;
+ byte SubAuthorityCount;
+ SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
+ uint SubAuthority;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct SID_IDENTIFIER_AUTHORITY
+ {
+ public unsafe fixed byte Value[6];
}
[StructLayout(LayoutKind.Sequential)]
internal struct ACL
{
- byte AclRevision;
- byte Sbz1;
- ushort AclSize;
- ushort AceCount;
- ushort Sbz2;
+ public byte AclRevision;
+ public byte Sbz1;
+ public ushort AclSize;
+ public ushort AceCount;
+ public ushort Sbz2;
}
}
diff --git a/Dalamud.Bootstrap/OS/Windows/RelaxedProcessHandle.cs b/Dalamud.Bootstrap/OS/Windows/RelaxedProcessHandle.cs
deleted file mode 100644
index f5f3e0b12..000000000
--- a/Dalamud.Bootstrap/OS/Windows/RelaxedProcessHandle.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System;
-using Dalamud.Bootstrap.OS.Windows.Raw;
-using Microsoft.Win32.SafeHandles;
-
-namespace Dalamud.Bootstrap.Windows
-{
- internal sealed class RelaxedProcessHandle : IDisposable
- {
- private SafeProcessHandle m_handle;
-
- private RelaxedProcessHandle(SafeProcessHandle handle)
- {
- m_handle = handle;
- }
-
- public void Dispose()
- {
-
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public static RelaxedProcessHandle Create(SafeProcessHandle handle, PROCESS_ACCESS_RIGHTS access)
- {
-
-
- return new RelaxedProcessHandle(handle);
- }
- }
-}
diff --git a/Dalamud.Bootstrap/OS/ProcessException.cs b/Dalamud.Bootstrap/ProcessException.cs
similarity index 100%
rename from Dalamud.Bootstrap/OS/ProcessException.cs
rename to Dalamud.Bootstrap/ProcessException.cs