.net windows-vista uac elevation

Cómo ejecutar NO elevado en Vista(.NET)



windows-vista uac (3)

Tengo una aplicación que debo ejecutar como administrador.

Una pequeña parte de esa aplicación es iniciar otras aplicaciones con Process.Start

Las aplicaciones iniciadas también se ejecutarán como administradores, pero prefiero verlas ejecutarse como el usuario "normal".

¿Cómo logro eso?

/ johan /


Desde: http://go.microsoft.com/fwlink/?LinkId=81232

Una pregunta frecuente es cómo iniciar una aplicación no elevada desde un proceso elevado o, más fundamentalmente, cómo iniciar un proceso utilizando mi token no elevado una vez que me estoy ejecutando elevado. Como no hay una manera directa de hacerlo, normalmente se puede evitar la situación iniciando la aplicación original como usuario estándar y solo elevando aquellas partes de la aplicación que requieren derechos administrativos. De esta forma, siempre hay un proceso no elevado que se puede usar para iniciar aplicaciones adicionales como el usuario de escritorio que está conectado actualmente. A veces, sin embargo, un proceso elevado necesita que otra aplicación se ejecute sin elevar. Esto se puede lograr utilizando el programador de tareas dentro de Windows Vista. El proceso elevado puede registrar una tarea para ejecutarse como el usuario de escritorio que está conectado actualmente.

Aquí hay un ejemplo de cómo programar el proceso no elevado (nuevamente desde el mismo enlace)

//--------------------------------------------------------------------- // This file is part of the Microsoft .NET Framework SDK Code Samples. // // Copyright (C) Microsoft Corporation. All rights reserved. // //This source code is intended only as a supplement to Microsoft //Development Tools and/or on-line documentation. See these other //materials for detailed information regarding Microsoft code samples. // //THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY //KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE //IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A //PARTICULAR PURPOSE. //--------------------------------------------------------------------- /**************************************************************************** * Main.cpp - Sample application for Task Scheduler V2 COMAPI * Component: Task Scheduler * Copyright (c) 2002 - 2003, Microsoft Corporation * This sample creates a task to that launches as the currently logged on deskup user. The task launches as soon as it is registered. * ****************************************************************************/ #include "stdafx.h" #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <comdef.h> #include <comutil.h> //Include Task header files - Included in Windows Vista Beta-2 SDK from MSDN #include <taskschd.h> #include <conio.h> #include <iostream> #include <time.h> using namespace std; #define CLEANUP / pRootFolder->Release();/ pTask->Release();/ CoUninitialize(); HRESULT CreateMyTask(LPCWSTR, wstring); void __cdecl wmain(int argc, wchar_t** argv) { wstring wstrExecutablePath; WCHAR taskName[20]; HRESULT result; if( argc < 2 ) { printf("/nUsage: LaunchApp yourapp.exe" ); return; } // Pick random number for task name srand((unsigned int) time(NULL)); wsprintf((LPWSTR)taskName, L"Launch %d", rand()); wstrExecutablePath = argv[1]; result = CreateMyTask(taskName, wstrExecutablePath); printf("/nReturn status:%d/n", result); } HRESULT CreateMyTask(LPCWSTR wszTaskName, wstring wstrExecutablePath) { // ------------------------------------------------------ // Initialize COM. TASK_STATE taskState; int i; HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if( FAILED(hr) ) { printf("/nCoInitializeEx failed: %x", hr ); return 1; } // Set general COM security levels. hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL); if( FAILED(hr) ) { printf("/nCoInitializeSecurity failed: %x", hr ); CoUninitialize(); return 1; } // ------------------------------------------------------ // Create an instance of the Task Service. ITaskService *pService = NULL; hr = CoCreateInstance( CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&pService ); if (FAILED(hr)) { printf("Failed to CoCreate an instance of the TaskService class: %x", hr); CoUninitialize(); return 1; } // Connect to the task service. hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t()); if( FAILED(hr) ) { printf("ITaskService::Connect failed: %x", hr ); pService->Release(); CoUninitialize(); return 1; } // ------------------------------------------------------ // Get the pointer to the root task folder. This folder will hold the // new task that is registered. ITaskFolder *pRootFolder = NULL; hr = pService->GetFolder( _bstr_t( L"//") , &pRootFolder ); if( FAILED(hr) ) { printf("Cannot get Root Folder pointer: %x", hr ); pService->Release(); CoUninitialize(); return 1; } // Check if the same task already exists. If the same task exists, remove it. hr = pRootFolder->DeleteTask( _bstr_t( wszTaskName), 0 ); // Create the task builder object to create the task. ITaskDefinition *pTask = NULL; hr = pService->NewTask( 0, &pTask ); pService->Release(); // COM clean up. Pointer is no longer used. if (FAILED(hr)) { printf("Failed to CoCreate an instance of the TaskService class: %x", hr); pRootFolder->Release(); CoUninitialize(); return 1; } // ------------------------------------------------------ // Get the trigger collection to insert the registration trigger. ITriggerCollection *pTriggerCollection = NULL; hr = pTask->get_Triggers( &pTriggerCollection ); if( FAILED(hr) ) { printf("/nCannot get trigger collection: %x", hr ); CLEANUP return 1; } // Add the registration trigger to the task. ITrigger *pTrigger = NULL; hr = pTriggerCollection->Create( TASK_TRIGGER_REGISTRATION, &pTrigger ); pTriggerCollection->Release(); // COM clean up. Pointer is no longer used. if( FAILED(hr) ) { printf("/nCannot add registration trigger to the Task %x", hr ); CLEANUP return 1; } pTrigger->Release(); // ------------------------------------------------------ // Add an Action to the task. IExecAction *pExecAction = NULL; IActionCollection *pActionCollection = NULL; // Get the task action collection pointer. hr = pTask->get_Actions( &pActionCollection ); if( FAILED(hr) ) { printf("/nCannot get Task collection pointer: %x", hr ); CLEANUP return 1; } // Create the action, specifying that it is an executable action. IAction *pAction = NULL; hr = pActionCollection->Create( TASK_ACTION_EXEC, &pAction ); pActionCollection->Release(); // COM clean up. Pointer is no longer used. if( FAILED(hr) ) { printf("/npActionCollection->Create failed: %x", hr ); CLEANUP return 1; } hr = pAction->QueryInterface( IID_IExecAction, (void**) &pExecAction ); pAction->Release(); if( FAILED(hr) ) { printf("/npAction->QueryInterface failed: %x", hr ); CLEANUP return 1; } // Set the path of the executable to the user supplied executable. hr = pExecAction->put_Path( _bstr_t( wstrExecutablePath.c_str() ) ); if( FAILED(hr) ) { printf("/nCannot set path of executable: %x", hr ); pExecAction->Release(); CLEANUP return 1; } hr = pExecAction->put_Arguments( _bstr_t( L"" ) ); if( FAILED(hr) ) { printf("/nCannot set arguments of executable: %x", hr ); pExecAction->Release(); CLEANUP return 1; } // ------------------------------------------------------ // Save the task in the root folder. IRegisteredTask *pRegisteredTask = NULL; hr = pRootFolder->RegisterTaskDefinition( _bstr_t( wszTaskName ), pTask, TASK_CREATE, _variant_t(_bstr_t( L"S-1-5-32-545")),//Well Known SID for //Builtin/Users group _variant_t(), TASK_LOGON_GROUP, _variant_t(L""), &pRegisteredTask); if( FAILED(hr) ) { printf("/nError saving the Task : %x", hr ); CLEANUP return 1; } printf("/n Success! Task successfully registered. " ); for (i=0; i<100; i++)//give 10 seconds for the task to start { pRegisteredTask->get_State(&taskState); if (taskState == TASK_STATE_RUNNING) { printf("/nTask is running/n"); break; } Sleep(100); } if (i>= 100) printf("Task didn''t start/n"); //Delete the task when done hr = pRootFolder->DeleteTask( _bstr_t( wszTaskName ), NULL); if( FAILED(hr) ) { printf("/nError deleting the Task : %x", hr ); CLEANUP return 1; } printf("/n Success! Task successfully deleted. " ); // Clean up. CLEANUP CoUninitialize(); return 0; }


Las API de WinSafer permiten que un proceso se inicie como un usuario limitado, normal o elevado.

Uso de la muestra:

CreateSaferProcess(@"calc.exe", "", SaferLevel.NormalUser);

Código fuente:

//http://odetocode.com/Blogs/scott/archive/2004/10/28/602.aspx public static void CreateSaferProcess(String fileName, String arguments, SaferLevel saferLevel) { IntPtr saferLevelHandle = IntPtr.Zero; //Create a SaferLevel handle to match what was requested if (!WinSafer.SaferCreateLevel( SaferLevelScope.User, saferLevel, SaferOpen.Open, out saferLevelHandle, IntPtr.Zero)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } try { //Generate the access token to use, based on the safer level handle. IntPtr hToken = IntPtr.Zero; if (!WinSafer.SaferComputeTokenFromLevel( saferLevelHandle, // SAFER Level handle IntPtr.Zero, // NULL is current thread token. out hToken, // Target token SaferTokenBehaviour.Default, // No flags IntPtr.Zero)) // Reserved { throw new Win32Exception(Marshal.GetLastWin32Error()); } try { //Now that we have a security token, we can lauch the process //using the standard CreateProcessAsUser API STARTUPINFO si = new STARTUPINFO(); si.cb = Marshal.SizeOf(si); si.lpDesktop = String.Empty; PROCESS_INFORMATION pi = new PROCESS_INFORMATION(); // Spin up the new process Boolean bResult = Windows.CreateProcessAsUser( hToken, fileName, arguments, IntPtr.Zero, //process attributes IntPtr.Zero, //thread attributes false, //inherit handles 0, //CREATE_NEW_CONSOLE IntPtr.Zero, //environment null, //current directory ref si, //startup info out pi); //process info if (!bResult) throw new Win32Exception(Marshal.GetLastWin32Error()); if (pi.hProcess != IntPtr.Zero) Windows.CloseHandle(pi.hProcess); if (pi.hThread != IntPtr.Zero) Windows.CloseHandle(pi.hThread); } finally { if (hToken != IntPtr.Zero) Windows.CloseHandle(hToken); } } finally { WinSafer.SaferCloseLevel(saferLevelHandle); } }

P / Declaraciones de invocación:

using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; namespace PInvoke { public class WinSafer { /// <summary> /// The SaferCreateLevel function opens a SAFER_LEVEL_HANDLE. /// </summary> /// <param name="scopeId">The scope of the level to be created.</param> /// <param name="levelId">The level of the handle to be opened.</param> /// <param name="openFlags">Must be SaferOpenFlags.Open</param> /// <param name="levelHandle">The returned SAFER_LEVEL_HANDLE. When you have finished using the handle, release it by calling the SaferCloseLevel function.</param> /// <param name="reserved">This parameter is reserved for future use. IntPtr.Zero</param> /// <returns></returns> [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferCreateLevel(SaferLevelScope scopeId, SaferLevel levelId, SaferOpen openFlags, out IntPtr levelHandle, IntPtr reserved); /// <summary> /// The SaferComputeTokenFromLevel function restricts a token using restrictions specified by a SAFER_LEVEL_HANDLE. /// </summary> /// <param name="levelHandle">SAFER_LEVEL_HANDLE that contains the restrictions to place on the input token. Do not pass handles with a LevelId of SAFER_LEVELID_FULLYTRUSTED or SAFER_LEVELID_DISALLOWED to this function. This is because SAFER_LEVELID_FULLYTRUSTED is unrestricted and SAFER_LEVELID_DISALLOWED does not contain a token.</param> /// <param name="inAccessToken">Token to be restricted. If this parameter is NULL, the token of the current thread will be used. If the current thread does not contain a token, the token of the current process is used.</param> /// <param name="outAccessToken">The resulting restricted token.</param> /// <param name="flags">Specifies the behavior of the method.</param> /// <param name="lpReserved">Reserved for future use. This parameter should be set to IntPtr.EmptyParam.</param> /// <returns></returns> [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferComputeTokenFromLevel(IntPtr levelHandle, IntPtr inAccessToken, out IntPtr outAccessToken, SaferTokenBehaviour flags, IntPtr lpReserved); /// <summary> /// The SaferCloseLevel function closes a SAFER_LEVEL_HANDLE that was opened by using the SaferIdentifyLevel function or the SaferCreateLevel function.</summary> /// <param name="levelHandle">The SAFER_LEVEL_HANDLE to be closed.</param> /// <returns>TRUE if the function succeeds; otherwise, FALSE. For extended error information, call GetLastWin32Error.</returns> [DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern bool SaferCloseLevel(IntPtr levelHandle); } //class WinSafer /// <summary> /// Specifies the behaviour of the SaferComputeTokenFromLevel method /// </summary> public enum SaferTokenBehaviour : uint { /// <summary></summary> Default = 0x0, /// <summary>If the OutAccessToken parameter is not more restrictive than the InAccessToken parameter, the OutAccessToken parameter returns NULL.</summary> NullIfEqual = 0x1, /// <summary></summary> CompareOnly = 0x2, /// <summary></summary> MakeInert = 0x4, /// <summary></summary> WantFlags = 0x8 } /// <summary> /// The level of the handle to be opened. /// </summary> public enum SaferLevel : uint { /// <summary>Software will not run, regardless of the user rights of the user.</summary> Disallowed = 0, /// <summary>Allows programs to execute with access only to resources granted to open well-known groups, blocking access to Administrator and Power User privileges and personally granted rights.</summary> Untrusted = 0x1000, /// <summary>Software cannot access certain resources, such as cryptographic keys and credentials, regardless of the user rights of the user.</summary> Constrained = 0x10000, /// <summary>Allows programs to execute as a user that does not have Administrator or Power User user rights. Software can access resources accessible by normal users.</summary> NormalUser = 0x20000, /// <summary>Software user rights are determined by the user rights of the user.</summary> FullyTrusted = 0x40000 } /// <summary> /// The scope of the level to be created. /// </summary> public enum SaferLevelScope : uint { /// <summary>The created level is scoped by computer.</summary> Machine = 1, /// <summary>The created level is scoped by user.</summary> User = 2 } public enum SaferOpen : uint { Open = 1 } } //namespace PInvoke


Tenía el mismo requisito y llegué a la solución para usar el servicio del programador de tareas de Windows.

Por lo tanto, primero agregue la biblioteca de Task Scheduler Managed Wrapper a su proyecto y use este código para crear una tarea, configúrela para ejecutarla como usuario limitado ( td.Principal.RunLevel = TaskRunLevel.LUA; ), registre la tarea, ejecute la tarea y después de terminar, elimine la tarea.

// Get the service on the local machine using (var ts = new TaskService()) { const string taskName = "foo"; // Create a new task definition and assign properties var td = ts.NewTask(); td.RegistrationInfo.Description = "start foo.exe as limited user"; // Create an action that will launch foo.exe, with argument bar in workingdir C:// td.Actions.Add(new ExecAction("C://foo.exe", "bar", "C://")); td.Settings.Priority = ProcessPriorityClass.Normal; // run with limited token td.Principal.RunLevel = TaskRunLevel.LUA; td.Settings.AllowDemandStart = true; td.Settings.DisallowStartIfOnBatteries = false; td.Settings.StopIfGoingOnBatteries = false; // Register the task in the root folder var ret = ts.RootFolder.RegisterTaskDefinition(taskName, td); var fooTask = ts.FindTask(taskName, true); if (null != fooTask ) { if (fooTask.Enabled) { fooTask.Run(); Thread.Sleep(TimeSpan.FromSeconds(1)); // find process and wait for Exit var processlist = Process.GetProcesses(); foreach(var theprocess in processlist) { if (theprocess.ProcessName != "foo") continue; theprocess.WaitForExit(); break; } } } // Remove the task we just created ts.RootFolder.DeleteTask(taskName); }