/ThirdParty/ApplicationLoader.cs

http://applicationloader.codeplex.com · C# · 199 lines · 132 code · 37 blank · 30 comment · 4 complexity · d4e208d398a72d5553b579824e45a3a7 MD5 · raw file

  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using System.Security;
  5. namespace ThirdParty {
  6. /// <summary>
  7. /// Class that launches applications with elevated privileges
  8. /// </summary>
  9. /// <see cref="http://www.codeproject.com/KB/vista-security/SubvertingVistaUAC.aspx?msg=3953060"/>
  10. /// <remarks>Pero Mati? is the original author of this class, full credit to him
  11. /// Licensed under CPOL http://www.codeproject.com/info/cpol10.aspx
  12. /// </remarks>
  13. public class ApplicationLoader {
  14. #region Structures
  15. [StructLayout(LayoutKind.Sequential)]
  16. public struct SECURITY_ATTRIBUTES {
  17. public int Length;
  18. public IntPtr lpSecurityDescriptor;
  19. public bool bInheritHandle;
  20. }
  21. [StructLayout(LayoutKind.Sequential)]
  22. public struct STARTUPINFO {
  23. public int cb;
  24. public String lpReserved;
  25. public String lpDesktop;
  26. public String lpTitle;
  27. public uint dwX;
  28. public uint dwY;
  29. public uint dwXSize;
  30. public uint dwYSize;
  31. public uint dwXCountChars;
  32. public uint dwYCountChars;
  33. public uint dwFillAttribute;
  34. public uint dwFlags;
  35. public short wShowWindow;
  36. public short cbReserved2;
  37. public IntPtr lpReserved2;
  38. public IntPtr hStdInput;
  39. public IntPtr hStdOutput;
  40. public IntPtr hStdError;
  41. }
  42. [StructLayout(LayoutKind.Sequential)]
  43. public struct PROCESS_INFORMATION {
  44. public IntPtr hProcess;
  45. public IntPtr hThread;
  46. public uint dwProcessId;
  47. public uint dwThreadId;
  48. }
  49. #endregion
  50. #region Enumerations
  51. enum TOKEN_TYPE : int {
  52. TokenPrimary = 1,
  53. TokenImpersonation = 2
  54. }
  55. enum SECURITY_IMPERSONATION_LEVEL : int {
  56. SecurityAnonymous = 0,
  57. SecurityIdentification = 1,
  58. SecurityImpersonation = 2,
  59. SecurityDelegation = 3,
  60. }
  61. #endregion
  62. #region Constants
  63. public const int TOKEN_DUPLICATE = 0x0002;
  64. public const uint MAXIMUM_ALLOWED = 0x2000000;
  65. public const int CREATE_NEW_CONSOLE = 0x00000010;
  66. public const int IDLE_PRIORITY_CLASS = 0x40;
  67. public const int NORMAL_PRIORITY_CLASS = 0x20;
  68. public const int HIGH_PRIORITY_CLASS = 0x80;
  69. public const int REALTIME_PRIORITY_CLASS = 0x100;
  70. #endregion
  71. #region Win32 API Imports
  72. [DllImport("kernel32.dll", SetLastError = true)]
  73. private static extern bool CloseHandle(IntPtr hSnapshot);
  74. [DllImport("kernel32.dll")]
  75. static extern uint WTSGetActiveConsoleSessionId();
  76. [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  77. public extern static bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
  78. ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle, int dwCreationFlags, IntPtr lpEnvironment,
  79. String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);
  80. [DllImport("kernel32.dll")]
  81. static extern bool ProcessIdToSessionId(uint dwProcessId, ref uint pSessionId);
  82. [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
  83. public extern static bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
  84. ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
  85. int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);
  86. [DllImport("kernel32.dll")]
  87. static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId);
  88. [DllImport("advapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
  89. static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle);
  90. #endregion
  91. /// <summary>
  92. /// Launches the given application with full admin rights, and in addition bypasses the Vista/Win7 UAC prompt
  93. /// </summary>
  94. /// <param name="applicationName">The name of the application to launch</param>
  95. /// <param name="procInfo">Process information regarding the launched application that gets returned to the caller</param>
  96. /// <returns></returns>
  97. public static bool StartProcessAndBypassUAC(String applicationName, out PROCESS_INFORMATION procInfo) {
  98. uint winlogonPid = 0;
  99. IntPtr hUserTokenDup = IntPtr.Zero, hPToken = IntPtr.Zero, hProcess = IntPtr.Zero;
  100. procInfo = new PROCESS_INFORMATION();
  101. // obtain the currently active session id; every logged on user in the system has a unique session id
  102. uint dwSessionId = GetActiveSessionId();
  103. // obtain the process id of the winlogon process that is running within the currently active session
  104. Process[] processes = Process.GetProcessesByName("winlogon");
  105. foreach (Process p in processes) {
  106. if ((uint)p.SessionId == dwSessionId) {
  107. winlogonPid = (uint)p.Id;
  108. }
  109. }
  110. // obtain a handle to the winlogon process
  111. hProcess = OpenProcess(MAXIMUM_ALLOWED, false, winlogonPid);
  112. // obtain a handle to the access token of the winlogon process
  113. if (!OpenProcessToken(hProcess, TOKEN_DUPLICATE, ref hPToken)) {
  114. CloseHandle(hProcess);
  115. return false;
  116. }
  117. // Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
  118. // I would prefer to not have to use a security attribute variable and to just
  119. // simply pass null and inherit (by default) the security attributes
  120. // of the existing token. However, in C# structures are value types and therefore
  121. // cannot be assigned the null value.
  122. SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
  123. sa.Length = Marshal.SizeOf(sa);
  124. // copy the access token of the winlogon process; the newly created token will be a primary token
  125. if (!DuplicateTokenEx(hPToken, MAXIMUM_ALLOWED, ref sa, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)TOKEN_TYPE.TokenPrimary, ref hUserTokenDup)) {
  126. CloseHandle(hProcess);
  127. CloseHandle(hPToken);
  128. return false;
  129. }
  130. // By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
  131. // the window station has a desktop that is invisible and the process is incapable of receiving
  132. // user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
  133. // interaction with the new process.
  134. STARTUPINFO si = new STARTUPINFO();
  135. si.cb = (int)Marshal.SizeOf(si);
  136. si.lpDesktop = @"winsta0\default"; // interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop
  137. // flags that specify the priority and creation method of the process
  138. int dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
  139. // create a new process in the current user's logon session
  140. bool result = CreateProcessAsUser(hUserTokenDup, // client's access token
  141. null, // file to execute
  142. applicationName, // command line
  143. ref sa, // pointer to process SECURITY_ATTRIBUTES
  144. ref sa, // pointer to thread SECURITY_ATTRIBUTES
  145. false, // handles are not inheritable
  146. dwCreationFlags, // creation flags
  147. IntPtr.Zero, // pointer to new environment block
  148. null, // name of current directory
  149. ref si, // pointer to STARTUPINFO structure
  150. out procInfo // receives information about new process
  151. );
  152. // invalidate the handles
  153. CloseHandle(hProcess);
  154. CloseHandle(hPToken);
  155. CloseHandle(hUserTokenDup);
  156. return result; // return the result
  157. }
  158. public static uint GetActiveSessionId() {
  159. return WTSGetActiveConsoleSessionId();
  160. }
  161. }
  162. }