Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to start a new process without administrator privileges from a process with administrator privileges?

I am creating an automatic updater for an application. The application is started by the user, and runs without administrator privileges. The autoupdater is started with administrator privileges, and kills the application before it downloads the new files.

The problem comes when I want to start the updated application after the autoupdater is finished. If I use regular System.Diagnostics.Process.Start(file), the application starts with administrator privileges too, and it has to run on the current user to work as intended.

So, how can I make the autoupdater start the application as the current user instead of the administrator?

I have tried using the following:

var pSI = new ProcessStartInfo() {      UseShellExecute = false,      UserName = Environment.UserName,      FileName = file  }; System.Diagnostics.Process.Start(pSI); 

But this throws the error "Invalid user name or password". I have checked that the username is correct, and I understand that the password probably is invalid, as I have not included it. But it is not an option to ask the user to input his/her password, since the entire reason to start the application automatically is to make it easier for the user.

Any suggestions?

like image 331
Erlend D. Avatar asked Jun 23 '12 12:06

Erlend D.


People also ask

How do I run a program without administrative privileges to bypass?

run-app-as-non-admin.bat After that, to run any application without the administrator privileges, just select “Run as user without UAC privilege elevation” in the context menu of File Explorer. You can deploy this option to all computers in the domain by importing the registry parameters using GPO.

How do I enable users to run a program with administrator rights without entering the administrator password again?

If you're giving access to just the executable, right-click the executable and select “Properties” and “Security.” Select “Edit.” Either choose the user from the provided list and change the permissions to “Full Control” under Allow, or select “Add” to add a new user and give them Full Control access.

How do I bypass system administrator restrictions?

When the logon screen appears, click the 'Ease of access' button which is to the left of the Power button then switch ON the 'Sticky Keys' option. Then press the 'Shift' key five times quickly and CMD should open with Administrator Privileges.


2 Answers

What you are trying to achieve cannot be done very easily and is not supported. However, it is possible using a modicum of hacking. Aaron Margosis wrote an article describing one technique.

To quote the pertinent section, you will need to carry out these steps:

  1. Enable the SeIncreaseQuotaPrivilege in your current token
  2. Get an HWND representing the desktop shell (GetShellWindow)
  3. Get the Process ID (PID) of the process associated with that window (GetWindowThreadProcessId)
  4. Open that process (OpenProcess)
  5. Get the access token from that process (OpenProcessToken)
  6. Make a primary token with that token (DuplicateTokenEx)
  7. Start the new process with that primary token (CreateProcessWithTokenW)

The article contains a download link for some demo C++ source from which it should be simple enough to translate to C#.

like image 164
David Heffernan Avatar answered Sep 30 '22 18:09

David Heffernan


C# code for Aaron Margosis article:

        private static void RunAsDesktopUser(string fileName)     {         if (string.IsNullOrWhiteSpace(fileName))             throw new ArgumentException("Value cannot be null or whitespace.", nameof(fileName));          // To start process as shell user you will need to carry out these steps:         // 1. Enable the SeIncreaseQuotaPrivilege in your current token         // 2. Get an HWND representing the desktop shell (GetShellWindow)         // 3. Get the Process ID(PID) of the process associated with that window(GetWindowThreadProcessId)         // 4. Open that process(OpenProcess)         // 5. Get the access token from that process (OpenProcessToken)         // 6. Make a primary token with that token(DuplicateTokenEx)         // 7. Start the new process with that primary token(CreateProcessWithTokenW)          var hProcessToken = IntPtr.Zero;         // Enable SeIncreaseQuotaPrivilege in this process.  (This won't work if current process is not elevated.)         try         {             var process = GetCurrentProcess();             if (!OpenProcessToken(process, 0x0020, ref hProcessToken))                 return;              var tkp = new TOKEN_PRIVILEGES             {                 PrivilegeCount = 1,                 Privileges = new LUID_AND_ATTRIBUTES[1]             };              if (!LookupPrivilegeValue(null, "SeIncreaseQuotaPrivilege", ref tkp.Privileges[0].Luid))                 return;              tkp.Privileges[0].Attributes = 0x00000002;              if (!AdjustTokenPrivileges(hProcessToken, false, ref tkp, 0, IntPtr.Zero, IntPtr.Zero))                 return;         }         finally         {             CloseHandle(hProcessToken);         }          // Get an HWND representing the desktop shell.         // CAVEATS:  This will fail if the shell is not running (crashed or terminated), or the default shell has been         // replaced with a custom shell.  This also won't return what you probably want if Explorer has been terminated and         // restarted elevated.         var hwnd = GetShellWindow();         if (hwnd == IntPtr.Zero)             return;          var hShellProcess = IntPtr.Zero;         var hShellProcessToken = IntPtr.Zero;         var hPrimaryToken = IntPtr.Zero;         try         {             // Get the PID of the desktop shell process.             uint dwPID;             if (GetWindowThreadProcessId(hwnd, out dwPID) == 0)                 return;              // Open the desktop shell process in order to query it (get the token)             hShellProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, dwPID);             if (hShellProcess == IntPtr.Zero)                 return;              // Get the process token of the desktop shell.             if (!OpenProcessToken(hShellProcess, 0x0002, ref hShellProcessToken))                 return;              var dwTokenRights = 395U;              // Duplicate the shell's process token to get a primary token.             // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).             if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, IntPtr.Zero, SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenPrimary, out hPrimaryToken))                 return;              // Start the target process with the new token.             var si = new STARTUPINFO();             var pi = new PROCESS_INFORMATION();             if (!CreateProcessWithTokenW(hPrimaryToken, 0, fileName, "", 0, IntPtr.Zero, Path.GetDirectoryName(fileName), ref si, out pi))                 return;         }         finally         {             CloseHandle(hShellProcessToken);             CloseHandle(hPrimaryToken);             CloseHandle(hShellProcess);         }      }      #region Interop      private struct TOKEN_PRIVILEGES     {         public UInt32 PrivilegeCount;         [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]         public LUID_AND_ATTRIBUTES[] Privileges;     }      [StructLayout(LayoutKind.Sequential, Pack = 4)]     private struct LUID_AND_ATTRIBUTES     {         public LUID Luid;         public UInt32 Attributes;     }      [StructLayout(LayoutKind.Sequential)]     private struct LUID     {         public uint LowPart;         public int HighPart;     }      [Flags]     private enum ProcessAccessFlags : uint     {         All = 0x001F0FFF,         Terminate = 0x00000001,         CreateThread = 0x00000002,         VirtualMemoryOperation = 0x00000008,         VirtualMemoryRead = 0x00000010,         VirtualMemoryWrite = 0x00000020,         DuplicateHandle = 0x00000040,         CreateProcess = 0x000000080,         SetQuota = 0x00000100,         SetInformation = 0x00000200,         QueryInformation = 0x00000400,         QueryLimitedInformation = 0x00001000,         Synchronize = 0x00100000     }      private enum SECURITY_IMPERSONATION_LEVEL     {         SecurityAnonymous,         SecurityIdentification,         SecurityImpersonation,         SecurityDelegation     }      private enum TOKEN_TYPE     {         TokenPrimary = 1,         TokenImpersonation     }      [StructLayout(LayoutKind.Sequential)]     private struct PROCESS_INFORMATION     {         public IntPtr hProcess;         public IntPtr hThread;         public int dwProcessId;         public int dwThreadId;     }      [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]     private struct STARTUPINFO     {         public Int32 cb;         public string lpReserved;         public string lpDesktop;         public string lpTitle;         public Int32 dwX;         public Int32 dwY;         public Int32 dwXSize;         public Int32 dwYSize;         public Int32 dwXCountChars;         public Int32 dwYCountChars;         public Int32 dwFillAttribute;         public Int32 dwFlags;         public Int16 wShowWindow;         public Int16 cbReserved2;         public IntPtr lpReserved2;         public IntPtr hStdInput;         public IntPtr hStdOutput;         public IntPtr hStdError;     }      [DllImport("kernel32.dll", ExactSpelling = true)]     private static extern IntPtr GetCurrentProcess();      [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]     private static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);      [DllImport("advapi32.dll", SetLastError = true)]     private static extern bool LookupPrivilegeValue(string host, string name, ref LUID pluid);      [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]     private static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TOKEN_PRIVILEGES newst, int len, IntPtr prev, IntPtr relen);      [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]     [return: MarshalAs(UnmanagedType.Bool)]     private static extern bool CloseHandle(IntPtr hObject);       [DllImport("user32.dll")]     private static extern IntPtr GetShellWindow();      [DllImport("user32.dll", SetLastError = true)]     private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);      [DllImport("kernel32.dll", SetLastError = true)]     private static extern IntPtr OpenProcess(ProcessAccessFlags processAccess, bool bInheritHandle, uint processId);      [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]     private static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess, IntPtr lpTokenAttributes, SECURITY_IMPERSONATION_LEVEL impersonationLevel, TOKEN_TYPE tokenType, out IntPtr phNewToken);      [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]     private static extern bool CreateProcessWithTokenW(IntPtr hToken, int dwLogonFlags, string lpApplicationName, string lpCommandLine, int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory, [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);      #endregion 
like image 45
user3122201 Avatar answered Sep 30 '22 16:09

user3122201