我有一个应用程序,我必须以管理员身份运行。
该应用程序的一小部分是使用Process.Start启动其他应用程序
启动的应用程序也将作为管理员运行,但我宁愿看到它们作为“普通”用户运行。
我如何做到这一点?
/约翰/
答案 0 :(得分:14)
WinSafer API允许以有限,正常或提升的用户身份启动流程。
样本使用:
CreateSaferProcess(@"calc.exe", "", SaferLevel.NormalUser);
源代码:
//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 / Invoke声明:
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
答案 1 :(得分:6)
来自:http://go.microsoft.com/fwlink/?LinkId=81232
一个经常被问到的问题是如何 从中发布一个未提升的应用程序 升级的过程,或更多 从根本上说,我如何推出 使用我未升级的令牌处理 一旦我高高举起。以来 没有直接的方法来做到这一点, 情况通常可以避免 启动原始应用程序 标准用户,只提升那些 部分应用程序 要求行政权利。这个 总是有一种不提升的方式 可用于启动的过程 其他应用程序作为 目前已登录桌面用户。 然而,有时会升高 过程需要得到另一个 应用程序运行不升级。这个 可以通过使用任务来完成 Windows Vista中的调度程序。该 提升过程可以注册任务 以当前登录的方式运行 桌面用户。
以下是如何安排未提升流程(同样来自同一链接)的示例
//---------------------------------------------------------------------
// 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;
}
答案 2 :(得分:2)
我有同样的要求,我来解决方案使用Windows的任务调度程序服务。
因此,首先添加the Task Scheduler Managed Wrapper
library to your project并使用此代码创建任务,将其配置为以受限用户(td.Principal.RunLevel = TaskRunLevel.LUA;
)运行,注册任务,运行任务以及之后完成,删除任务。
// 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);
}
答案 3 :(得分:2)
Raymond Chen在他的博客中谈到了这一点:
How can I launch an unelevated process from my elevated process and vice versa?
在GitHub中搜索此代码的C#版本,我在Microsoft的 Visual Studio的Node.js工具存储库中找到以下实现:SystemUtilities.cs(ExecuteProcessUnElevated
函数)。
以防万一文件消失了,这是文件的内容:
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Runtime.InteropServices;
namespace Microsoft.NodejsTools.SharedProject
{
/// <summary>
/// Utility for accessing window IShell* interfaces in order to use them to launch a process unelevated
/// </summary>
internal class SystemUtility
{
/// <summary>
/// We are elevated and should launch the process unelevated. We can't create the
/// process directly without it becoming elevated. So to workaround this, we have
/// explorer do the process creation (explorer is typically running unelevated).
/// </summary>
internal static void ExecuteProcessUnElevated(string process, string args, string currentDirectory = "")
{
var shellWindows = (IShellWindows)new CShellWindows();
// Get the desktop window
object loc = CSIDL_Desktop;
object unused = new object();
int hwnd;
var serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(ref loc, ref unused, SWC_DESKTOP, out hwnd, SWFO_NEEDDISPATCH);
// Get the shell browser
var serviceGuid = SID_STopLevelBrowser;
var interfaceGuid = typeof(IShellBrowser).GUID;
var shellBrowser = (IShellBrowser)serviceProvider.QueryService(ref serviceGuid, ref interfaceGuid);
// Get the shell dispatch
var dispatch = typeof(IDispatch).GUID;
var folderView = (IShellFolderViewDual)shellBrowser.QueryActiveShellView().GetItemObject(SVGIO_BACKGROUND, ref dispatch);
var shellDispatch = (IShellDispatch2)folderView.Application;
// Use the dispatch (which is unelevated) to launch the process for us
shellDispatch.ShellExecute(process, args, currentDirectory, string.Empty, SW_SHOWNORMAL);
}
/// <summary>
/// Interop definitions
/// </summary>
private const int CSIDL_Desktop = 0;
private const int SWC_DESKTOP = 8;
private const int SWFO_NEEDDISPATCH = 1;
private const int SW_SHOWNORMAL = 1;
private const int SVGIO_BACKGROUND = 0;
private readonly static Guid SID_STopLevelBrowser = new Guid("4C96BE40-915C-11CF-99D3-00AA004AE837");
[ComImport]
[Guid("9BA05972-F6A8-11CF-A442-00A0C90A8F39")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
private class CShellWindows
{
}
[ComImport]
[Guid("85CB6900-4D95-11CF-960C-0080C7F4EE85")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
private interface IShellWindows
{
[return: MarshalAs(UnmanagedType.IDispatch)]
object FindWindowSW([MarshalAs(UnmanagedType.Struct)] ref object pvarloc, [MarshalAs(UnmanagedType.Struct)] ref object pvarlocRoot, int swClass, out int pHWND, int swfwOptions);
}
[ComImport]
[Guid("6d5140c1-7436-11ce-8034-00aa006009fa")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IServiceProvider
{
[return: MarshalAs(UnmanagedType.Interface)]
object QueryService(ref Guid guidService, ref Guid riid);
}
[ComImport]
[Guid("000214E2-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellBrowser
{
void VTableGap01(); // GetWindow
void VTableGap02(); // ContextSensitiveHelp
void VTableGap03(); // InsertMenusSB
void VTableGap04(); // SetMenuSB
void VTableGap05(); // RemoveMenusSB
void VTableGap06(); // SetStatusTextSB
void VTableGap07(); // EnableModelessSB
void VTableGap08(); // TranslateAcceleratorSB
void VTableGap09(); // BrowseObject
void VTableGap10(); // GetViewStateStream
void VTableGap11(); // GetControlWindow
void VTableGap12(); // SendControlMsg
IShellView QueryActiveShellView();
}
[ComImport]
[Guid("000214E3-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellView
{
void VTableGap01(); // GetWindow
void VTableGap02(); // ContextSensitiveHelp
void VTableGap03(); // TranslateAcceleratorA
void VTableGap04(); // EnableModeless
void VTableGap05(); // UIActivate
void VTableGap06(); // Refresh
void VTableGap07(); // CreateViewWindow
void VTableGap08(); // DestroyViewWindow
void VTableGap09(); // GetCurrentInfo
void VTableGap10(); // AddPropertySheetPages
void VTableGap11(); // SaveViewState
void VTableGap12(); // SelectItem
[return: MarshalAs(UnmanagedType.Interface)]
object GetItemObject(UInt32 aspectOfView, ref Guid riid);
}
[ComImport]
[Guid("00020400-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
private interface IDispatch
{
}
[ComImport]
[Guid("E7A1AF80-4D96-11CF-960C-0080C7F4EE85")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
private interface IShellFolderViewDual
{
object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; }
}
[ComImport]
[Guid("A4C6892C-3BA9-11D2-9DEA-00C04FB16162")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IShellDispatch2
{
void ShellExecute([MarshalAs(UnmanagedType.BStr)] string File, [MarshalAs(UnmanagedType.Struct)] object vArgs, [MarshalAs(UnmanagedType.Struct)] object vDir, [MarshalAs(UnmanagedType.Struct)] object vOperation, [MarshalAs(UnmanagedType.Struct)] object vShow);
}
}
}