public static Process LaunchGame(string workingDir, string exePath, string arguments, IDictionary <string, string> envVars) { Process process; var userName = Environment.UserName; var pExplicitAccess = new PInvoke.EXPLICIT_ACCESS(); PInvoke.BuildExplicitAccessWithName( ref pExplicitAccess, userName, PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE, PInvoke.GRANT_ACCESS, 0); if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var secDesc = new PInvoke.SECURITY_DESCRIPTOR(); if (!PInvoke.InitializeSecurityDescriptor(out secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf <PInvoke.SECURITY_DESCRIPTOR>()); Marshal.StructureToPtr <PInvoke.SECURITY_DESCRIPTOR>(secDesc, psecDesc, true); var lpProcessInformation = new PInvoke.PROCESS_INFORMATION(); var lpEnvironment = IntPtr.Zero; try { if (envVars.Count > 0) { string envstr = string.Join("\0", envVars.Select(entry => entry.Key + "=" + entry.Value)); lpEnvironment = Marshal.StringToHGlobalAnsi(envstr); } var lpProcessAttributes = new PInvoke.SECURITY_ATTRIBUTES { nLength = Marshal.SizeOf <PInvoke.SECURITY_ATTRIBUTES>(), lpSecurityDescriptor = psecDesc, bInheritHandle = false }; var lpStartupInfo = new PInvoke.STARTUPINFO { cb = Marshal.SizeOf <PInvoke.STARTUPINFO>() }; if (!PInvoke.CreateProcess( null, exePath + " " + arguments, ref lpProcessAttributes, IntPtr.Zero, false, PInvoke.CREATE_SUSPENDED, IntPtr.Zero, workingDir, ref lpStartupInfo, out lpProcessInformation)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } DisableSeDebug(lpProcessInformation.hProcess); process = new ExistingProcess(lpProcessInformation.hProcess); PInvoke.ResumeThread(lpProcessInformation.hThread); process.WaitForInputIdle(); if (PInvoke.GetSecurityInfo( PInvoke.GetCurrentProcess(), PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT, PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, IntPtr.Zero, IntPtr.Zero, out var pACL, IntPtr.Zero, IntPtr.Zero) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if (PInvoke.SetSecurityInfo( lpProcessInformation.hProcess, PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT, PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION, IntPtr.Zero, IntPtr.Zero, pACL, IntPtr.Zero) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } finally { Marshal.FreeHGlobal(psecDesc); if (!IntPtr.Equals(lpEnvironment, IntPtr.Zero)) { Marshal.FreeHGlobal(lpEnvironment); } PInvoke.CloseHandle(lpProcessInformation.hThread); } return(process); }
public static Process LaunchGame(string workingDir, string exePath, string arguments, IDictionary <string, string> envVars, DpiAwareness dpiAwareness, Action <Process> beforeResume) { Process process = null; var userName = Environment.UserName; var pExplicitAccess = new PInvoke.EXPLICIT_ACCESS(); PInvoke.BuildExplicitAccessWithName( ref pExplicitAccess, userName, PInvoke.STANDARD_RIGHTS_ALL | PInvoke.SPECIFIC_RIGHTS_ALL & ~PInvoke.PROCESS_VM_WRITE, PInvoke.GRANT_ACCESS, 0); if (PInvoke.SetEntriesInAcl(1, ref pExplicitAccess, IntPtr.Zero, out var newAcl) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var secDesc = new PInvoke.SECURITY_DESCRIPTOR(); if (!PInvoke.InitializeSecurityDescriptor(out secDesc, PInvoke.SECURITY_DESCRIPTOR_REVISION)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if (!PInvoke.SetSecurityDescriptorDacl(ref secDesc, true, newAcl, false)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } var psecDesc = Marshal.AllocHGlobal(Marshal.SizeOf <PInvoke.SECURITY_DESCRIPTOR>()); Marshal.StructureToPtr <PInvoke.SECURITY_DESCRIPTOR>(secDesc, psecDesc, true); var lpProcessInformation = new PInvoke.PROCESS_INFORMATION(); var lpEnvironment = IntPtr.Zero; try { if (envVars.Count > 0) { string envstr = string.Join("\0", envVars.Select(entry => entry.Key + "=" + entry.Value)); lpEnvironment = Marshal.StringToHGlobalAnsi(envstr); } var lpProcessAttributes = new PInvoke.SECURITY_ATTRIBUTES { nLength = Marshal.SizeOf <PInvoke.SECURITY_ATTRIBUTES>(), lpSecurityDescriptor = psecDesc, bInheritHandle = false }; var lpStartupInfo = new PInvoke.STARTUPINFO { cb = Marshal.SizeOf <PInvoke.STARTUPINFO>() }; var compatLayerPrev = Environment.GetEnvironmentVariable("__COMPAT_LAYER"); var compat = "RunAsInvoker "; compat += dpiAwareness switch { DpiAwareness.Aware => "HighDPIAware", DpiAwareness.Unaware => "DPIUnaware", _ => throw new ArgumentOutOfRangeException() }; Environment.SetEnvironmentVariable("__COMPAT_LAYER", compat); if (!PInvoke.CreateProcess( null, $"\"{exePath}\" {arguments}", ref lpProcessAttributes, IntPtr.Zero, false, PInvoke.CREATE_SUSPENDED, IntPtr.Zero, workingDir, ref lpStartupInfo, out lpProcessInformation)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } Environment.SetEnvironmentVariable("__COMPAT_LAYER", compatLayerPrev); DisableSeDebug(lpProcessInformation.hProcess); process = new ExistingProcess(lpProcessInformation.hProcess); beforeResume?.Invoke(process); PInvoke.ResumeThread(lpProcessInformation.hThread); // Ensure that the game main window is prepared try { do { process.WaitForInputIdle(); Thread.Sleep(100); } while (IntPtr.Zero == TryFindGameWindow(process)); } catch (InvalidOperationException) { throw new GameExitedException(); } if (PInvoke.GetSecurityInfo( PInvoke.GetCurrentProcess(), PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT, PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION, IntPtr.Zero, IntPtr.Zero, out var pACL, IntPtr.Zero, IntPtr.Zero) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } if (PInvoke.SetSecurityInfo( lpProcessInformation.hProcess, PInvoke.SE_OBJECT_TYPE.SE_KERNEL_OBJECT, PInvoke.SECURITY_INFORMATION.DACL_SECURITY_INFORMATION | PInvoke.SECURITY_INFORMATION.UNPROTECTED_DACL_SECURITY_INFORMATION, IntPtr.Zero, IntPtr.Zero, pACL, IntPtr.Zero) != 0) { throw new Win32Exception(Marshal.GetLastWin32Error()); } } catch (Exception ex) { Log.Error(ex, "[NativeAclFix] Uncaught error during initialization, trying to kill process"); try { process?.Kill(); } catch (Exception killEx) { Log.Error(killEx, "[NativeAclFix] Could not kill process"); } throw; } finally { Marshal.FreeHGlobal(psecDesc); if (!IntPtr.Equals(lpEnvironment, IntPtr.Zero)) { Marshal.FreeHGlobal(lpEnvironment); } PInvoke.CloseHandle(lpProcessInformation.hThread); } return(process); }