CLI tool for running Playbooks
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
6.9 KiB

1 year ago
  1. using Microsoft.Win32.SafeHandles;
  2. using System;
  3. using System.Diagnostics;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. namespace TrustedUninstaller.Shared
  7. {
  8. [StructLayout(LayoutKind.Sequential)]
  9. public struct SECURITY_ATTRIBUTES
  10. {
  11. public int nLength;
  12. public IntPtr lpSecurityDescriptor;
  13. public int bInheritHandle;
  14. }
  15. [StructLayout(LayoutKind.Sequential)]
  16. internal struct PROCESS_INFORMATION
  17. {
  18. public IntPtr hProcess;
  19. public IntPtr hThread;
  20. public int dwProcessId;
  21. public int dwThreadId;
  22. }
  23. // This also works with CharSet.Ansi as long as the calling function uses the same character set.
  24. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  25. public struct STARTUPINFO
  26. {
  27. public Int32 cb;
  28. public string lpReserved;
  29. public string lpDesktop;
  30. public string lpTitle;
  31. public Int32 dwX;
  32. public Int32 dwY;
  33. public Int32 dwXSize;
  34. public Int32 dwYSize;
  35. public Int32 dwXCountChars;
  36. public Int32 dwYCountChars;
  37. public Int32 dwFillAttribute;
  38. public Int32 dwFlags;
  39. public Int16 wShowWindow;
  40. public Int16 cbReserved2;
  41. public IntPtr lpReserved2;
  42. public IntPtr hStdInput;
  43. public IntPtr hStdOutput;
  44. public IntPtr hStdError;
  45. }
  46. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  47. public struct STARTUPINFOEX
  48. {
  49. public STARTUPINFO StartupInfo;
  50. public IntPtr lpAttributeList;
  51. }
  52. public class NativeProcess
  53. {
  54. [DllImport("kernel32.dll")]
  55. [return: MarshalAs(UnmanagedType.Bool)]
  56. private static extern bool CreateProcess(
  57. string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
  58. ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags,
  59. IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFOEX lpStartupInfo,
  60. out PROCESS_INFORMATION lpProcessInformation);
  61. [DllImport("kernel32.dll", SetLastError = true)]
  62. [return: MarshalAs(UnmanagedType.Bool)]
  63. private static extern bool UpdateProcThreadAttribute(
  64. IntPtr lpAttributeList, uint dwFlags, IntPtr Attribute, IntPtr lpValue,
  65. IntPtr cbSize, IntPtr lpPreviousValue, IntPtr lpReturnSize);
  66. [DllImport("kernel32.dll", SetLastError = true)]
  67. [return: MarshalAs(UnmanagedType.Bool)]
  68. private static extern bool InitializeProcThreadAttributeList(
  69. IntPtr lpAttributeList, int dwAttributeCount, int dwFlags, ref IntPtr lpSize);
  70. [DllImport("kernel32.dll", SetLastError = true)]
  71. [return: MarshalAs(UnmanagedType.Bool)]
  72. private static extern bool DeleteProcThreadAttributeList(IntPtr lpAttributeList);
  73. [DllImport("kernel32.dll", SetLastError = true)]
  74. private static extern bool CloseHandle(IntPtr hObject);
  75. private const uint EXTENDED_STARTUPINFO_PRESENT = 0x00080000;
  76. private const int PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = 0x00020000;
  77. private const int CREATE_NO_WINDOW = 0x08000000;
  78. private const int CREATE_NEW_CONSOLE = 0x00000010;
  79. [DllImport("kernel32.dll", SetLastError = true)]
  80. static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
  81. const UInt32 INFINITE = 0xFFFFFFFF;
  82. const UInt32 WAIT_ABANDONED = 0x00000080;
  83. const UInt32 WAIT_OBJECT_0 = 0x00000000;
  84. const UInt32 WAIT_TIMEOUT = 0x00000102;
  85. public static Process? Process;
  86. public static bool StartProcess(string path, int parent, string playbookDir)
  87. {
  88. Process = null;
  89. // Create a new process with a different parent process
  90. // https://stackoverflow.com/questions/10554913/how-to-call-createprocess-with-startupinfoex-from-c-sharp-and-re-parent-the-ch
  91. var pInfo = new PROCESS_INFORMATION();
  92. var sInfoEx = new STARTUPINFOEX();
  93. sInfoEx.StartupInfo.cb = Marshal.SizeOf(sInfoEx);
  94. var lpValue = IntPtr.Zero;
  95. try
  96. {
  97. var lpSize = IntPtr.Zero;
  98. var success = InitializeProcThreadAttributeList(IntPtr.Zero, 1, 0, ref lpSize);
  99. if (success || lpSize == IntPtr.Zero)
  100. {
  101. return false;
  102. }
  103. sInfoEx.lpAttributeList = Marshal.AllocHGlobal(lpSize);
  104. success = InitializeProcThreadAttributeList(sInfoEx.lpAttributeList, 1, 0, ref lpSize);
  105. if (!success)
  106. {
  107. return false;
  108. }
  109. var parentHandle = Process.GetProcessById(parent).Handle;
  110. // This value should persist until the attribute list is destroyed using the DeleteProcThreadAttributeList function
  111. lpValue = Marshal.AllocHGlobal(IntPtr.Size);
  112. Marshal.WriteIntPtr(lpValue, parentHandle);
  113. success = UpdateProcThreadAttribute(
  114. sInfoEx.lpAttributeList,
  115. 0,
  116. (IntPtr)PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
  117. lpValue,
  118. (IntPtr)IntPtr.Size,
  119. IntPtr.Zero,
  120. IntPtr.Zero);
  121. if (!success)
  122. {
  123. return false;
  124. }
  125. var pSec = new SECURITY_ATTRIBUTES();
  126. var tSec = new SECURITY_ATTRIBUTES();
  127. pSec.nLength = Marshal.SizeOf(pSec);
  128. tSec.nLength = Marshal.SizeOf(tSec);
  129. CreateProcess(null,
  130. $"\"{path}\" \"{playbookDir}\"",
  131. ref pSec,
  132. ref tSec,
  133. false,
  134. EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW,
  135. IntPtr.Zero,
  136. null,
  137. ref sInfoEx,
  138. out pInfo);
  139. //WaitForSingleObject(pInfo.hProcess, INFINITE);
  140. Process = Process.GetProcessById(pInfo.dwProcessId);
  141. return true;
  142. }
  143. finally
  144. {
  145. // Free the attribute list
  146. if (sInfoEx.lpAttributeList != IntPtr.Zero)
  147. {
  148. DeleteProcThreadAttributeList(sInfoEx.lpAttributeList);
  149. Marshal.FreeHGlobal(sInfoEx.lpAttributeList);
  150. }
  151. Marshal.FreeHGlobal(lpValue);
  152. // Close process and thread handles
  153. if (pInfo.hProcess != IntPtr.Zero)
  154. {
  155. CloseHandle(pInfo.hProcess);
  156. }
  157. if (pInfo.hThread != IntPtr.Zero)
  158. {
  159. CloseHandle(pInfo.hThread);
  160. }
  161. }
  162. }
  163. }
  164. }