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);
}