如何启动流程升级

Elm*_*lmo 5 .net c# vb.net elevation process-elevation

我的应用程序按requestedExecutionLevel设置运行highestAvailable.

如何运行一个没有升级的流程?

我尝试了以下但它不起作用:

Process.Start(new ProcessStartInfo {FileName = "foo.exe", Verb = "open"})
Run Code Online (Sandbox Code Playgroud)

我尝试了以下信任级别来使用Win32 API启动我的进程,但它们都没有正常工作:

0
1260: This program is blocked by group policy. For more information, contact your system administrator.

0x1000
The application was unable to start correctly (0xc0000142). Click OK to close the application. 

0x10000
Process starts then hangs

0x20000
All options are not available

0x40000
Runs as admin
Run Code Online (Sandbox Code Playgroud)

如果我tskill foo从提升的应用程序运行,它会以正确的权限重新启动foo.

我需要的是一个解决方案,我不必指定信任级别.该过程应该自动以正确的信任级别开始,就像tskill工具foo.exe在正确的信任级别重新启动一样.用户选择并运行foo.exe,因此它可以是任何东西.

如果我可以以某种方式获得进程的信任级别,我可以轻松地执行此操作,因为foo.exe当我的应用程序可以捕获其信任级别时运行.

drf*_*drf 6

Win32安全管理功能提供了创建具有普通用户权限的受限令牌的功能; 使用令牌,您可以调用CreateProcessAsUser以使用该令牌运行该进程.以下是将cmd.exe作为普通用户运行的概念验证,无论该进程是否在提升的上下文中运行.

// Initialize variables.  
IntPtr hSaferLevel, hToken;
STARTUPINFO si = default(STARTUPINFO);
SECURITY_ATTRIBUTES processAttributes = default(SECURITY_ATTRIBUTES);
SECURITY_ATTRIBUTES threadAttributes = default(SECURITY_ATTRIBUTES);
PROCESS_INFORMATION pi;
si.cb = Marshal.SizeOf(si);

// The process to start (for demonstration, cmd.exe)
string ProcessName = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.System),
    "cmd.exe");

// Create the restricted token info
if (!SaferCreateLevel(
     SaferScopes.User,
     SaferLevels.NormalUser, // Program will execute as a normal user
     1, // required
     out hSaferLevel,
     IntPtr.Zero))
         throw new Win32Exception(Marshal.GetLastWin32Error());

// From the level create a token
if (!SaferComputeTokenFromLevel(
     hSaferLevel,
     IntPtr.Zero,
     out hToken,
     SaferComputeTokenFlags.None,
     IntPtr.Zero))
         throw new Win32Exception(Marshal.GetLastWin32Error());

// Run the process with the restricted token
if (!CreateProcessAsUser(
     hToken,
     ProcessName,
     null, ref processAttributes, ref threadAttributes,
     true, 0, IntPtr.Zero, null,
     ref si, out pi))
         throw new Win32Exception(Marshal.GetLastWin32Error());

 // Cleanup
 if (!CloseHandle(pi.hProcess))
     throw new Win32Exception(Marshal.GetLastWin32Error());
 if (!CloseHandle(pi.hThread))
     throw new Win32Exception(Marshal.GetLastWin32Error());
 if (!SaferCloseLevel(hSaferLevel))
     throw new Win32Exception(Marshal.GetLastWin32Error());
Run Code Online (Sandbox Code Playgroud)

此方法使用以下Win32函数:

  • SaferIdentifyLevel表示身份级别(有限,正常或升高).设置levelIdSAFER_LEVELID_NORMALUSER(0x20000)提供正常的用户级别.
  • SaferComputeTokenFromLevel为提供的级别创建令牌.传递NULL给InAccessToken参数使用当前线程的标识.
  • CreateProcessAsUser使用提供的令牌创建进程.由于会话已经是交互式的,因此大多数参数可以保持默认值.(第三个参数,lpCommandLine可以作为字符串提供,以指定命令行.)
  • CloseHandle(Kernel32)SaferCloseLevel释放分配的内存.

最后,P/Invoke代码如下(主要从pinvoke.net复制):

[Flags]
public enum SaferLevels : uint
{
    Disallowed = 0,
    Untrusted = 0x1000,
    Constrained = 0x10000,
    NormalUser = 0x20000,
    FullyTrusted = 0x40000
}

[Flags]
public enum SaferComputeTokenFlags : uint
{
    None = 0x0,
    NullIfEqual = 0x1,
    CompareOnly = 0x2,
    MakeIntert = 0x4,
    WantFlags = 0x8
}

[Flags]
public enum SaferScopes : uint
{
    Machine = 1,
    User = 2
}

[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
    public int nLength;
    public IntPtr lpSecurityDescriptor;
    public int bInheritHandle;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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;
}

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
    public IntPtr hProcess;
    public IntPtr hThread;
    public int dwProcessId;
    public int dwThreadId;
}


[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferComputeTokenFromLevel(IntPtr LevelHandle, IntPtr InAccessToken, out IntPtr OutAccessToken, int dwFlags, IntPtr lpReserved);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern bool CreateProcessAsUser(
    IntPtr hToken,
    string lpApplicationName,
    string lpCommandLine,
    ref SECURITY_ATTRIBUTES lpProcessAttributes,
    ref SECURITY_ATTRIBUTES lpThreadAttributes,
    bool bInheritHandles,
    uint dwCreationFlags,
    IntPtr lpEnvironment,
    string lpCurrentDirectory,
    ref STARTUPINFO lpStartupInfo,
    out PROCESS_INFORMATION lpProcessInformation); 

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCreateLevel(
    SaferScopes dwScopeId,
    SaferLevels dwLevelId,
    int OpenFlags,
    out IntPtr pLevelHandle,
    IntPtr lpReserved);

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferCloseLevel(
    IntPtr pLevelHandle);

[DllImport("advapi32", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool SaferComputeTokenFromLevel(
  IntPtr levelHandle,
  IntPtr inAccessToken,
  out IntPtr outAccessToken,
  SaferComputeTokenFlags dwFlags,
  IntPtr lpReserved
);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
Run Code Online (Sandbox Code Playgroud)


Rom*_*kov 6

我通过克隆Explorer的令牌获得了最佳结果,如下所示:

var shellWnd = WinAPI.GetShellWindow();
if (shellWnd == IntPtr.Zero)
    throw new Exception("Could not find shell window");

uint shellProcessId;
WinAPI.GetWindowThreadProcessId(shellWnd, out shellProcessId);

var hShellProcess = WinAPI.OpenProcess(0x00000400 /* QueryInformation */, false, shellProcessId);

var hShellToken = IntPtr.Zero;
if (!WinAPI.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out hShellToken))
    throw new Win32Exception();

uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
var hToken = IntPtr.Zero;
WinAPI.DuplicateTokenEx(hShellToken, tokenAccess, IntPtr.Zero, 2 /* SecurityImpersonation */, 1 /* TokenPrimary */, out hToken);

var pi = new WinAPI.PROCESS_INFORMATION();
var si = new WinAPI.STARTUPINFO();
si.cb = Marshal.SizeOf(si);
if (!WinAPI.CreateProcessWithTokenW(hToken, 0, null, cmdArgs, 0, IntPtr.Zero, null, ref si, out pi))
    throw new Win32Exception();
Run Code Online (Sandbox Code Playgroud)

替代方法

最初我选择了drf的优秀答案,但稍微扩展了一下.如果上面(克隆资源管理器的令牌)不符合您的喜好,请继续阅读,但最后会看到问题.

当使用所描述的drf方法时,该过程在没有管理访问权限的情况下启动,但它仍具有高完整性级别.典型的未升级过程具有中等完整性级别.

试试这个:使用Process Hacker以这种方式查看进程的属性; 您将看到PH认为该进程即使没有管理访问权限也会被提升.添加完整性列,您会看到它是"高".

修复非常简单:使用后SaferComputeTokenFromLevel,我们需要将令牌完整性级别更改为"中".执行此操作的代码可能如下所示(从MSDN示例转换):

// Get the Medium Integrity SID
if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid))
    throw new Win32Exception();

// Construct a structure describing the token integrity level
var TIL = new TOKEN_MANDATORY_LABEL();
TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */;
TIL.Label.Sid = pMediumIntegritySid;
pTIL = Marshal.AllocHGlobal(Marshal.SizeOf<TOKEN_MANDATORY_LABEL>());
Marshal.StructureToPtr(TIL, pTIL, false);

// Modify the token
if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL,
                                (uint) Marshal.SizeOf<TOKEN_MANDATORY_LABEL>()
                                + WinAPI.GetLengthSid(pMediumIntegritySid)))
    throw new Win32Exception();
Run Code Online (Sandbox Code Playgroud)

唉,这仍然没有真正彻底解决问题.该过程不具有管理访问权限; 它不会具有高完整性,但仍然会有一个标记为"提升"的标记.

这是否是一个问题,我不知道,但这可能是我最终克隆Explorer的令牌的原因,如本答案开头所述.


这是我的完整源代码(修改过的drf的答案),在它的所有P/Invoke荣耀中:

var hSaferLevel = IntPtr.Zero;
var hToken = IntPtr.Zero;
var pMediumIntegritySid = IntPtr.Zero;
var pTIL = IntPtr.Zero;
var pi = new WinAPI.PROCESS_INFORMATION();
try
{
    var si = new WinAPI.STARTUPINFO();
    si.cb = Marshal.SizeOf(si);
    var processAttributes = new WinAPI.SECURITY_ATTRIBUTES();
    var threadAttributes = new WinAPI.SECURITY_ATTRIBUTES();
    var args = CommandRunner.ArgsToCommandLine(Args);

    if (!WinAPI.SaferCreateLevel(WinAPI.SaferScopes.User, WinAPI.SaferLevels.NormalUser, 1, out hSaferLevel, IntPtr.Zero))
        throw new Win32Exception();

    if (!WinAPI.SaferComputeTokenFromLevel(hSaferLevel, IntPtr.Zero, out hToken, WinAPI.SaferComputeTokenFlags.None, IntPtr.Zero))
        throw new Win32Exception();

    if (!WinAPI.ConvertStringSidToSid("S-1-16-8192", out pMediumIntegritySid))
        throw new Win32Exception();
    var TIL = new TOKEN_MANDATORY_LABEL();
    TIL.Label.Attributes = 0x00000020 /* SE_GROUP_INTEGRITY */;
    TIL.Label.Sid = pMediumIntegritySid;
    pTIL = Marshal.AllocHGlobal(Marshal.SizeOf<TOKEN_MANDATORY_LABEL>());
    Marshal.StructureToPtr(TIL, pTIL, false);
    if (!WinAPI.SetTokenInformation(hToken, 25 /* TokenIntegrityLevel */, pTIL, (uint) Marshal.SizeOf<TOKEN_MANDATORY_LABEL>() + WinAPI.GetLengthSid(pMediumIntegritySid)))
        throw new Win32Exception();

    if (!WinAPI.CreateProcessAsUser(hToken, null, commandLine, ref processAttributes, ref threadAttributes, true, 0, IntPtr.Zero, null, ref si, out pi))
        throw new Win32Exception();
}
finally
{
    if (hToken != IntPtr.Zero && !WinAPI.CloseHandle(hToken))
        throw new Win32Exception();
    if (pMediumIntegritySid != IntPtr.Zero && WinAPI.LocalFree(pMediumIntegritySid) != IntPtr.Zero)
        throw new Win32Exception();
    if (pTIL != IntPtr.Zero)
        Marshal.FreeHGlobal(pTIL);
    if (pi.hProcess != IntPtr.Zero && !WinAPI.CloseHandle(pi.hProcess))
        throw new Win32Exception();
    if (pi.hThread != IntPtr.Zero && !WinAPI.CloseHandle(pi.hThread))
        throw new Win32Exception();
}
Run Code Online (Sandbox Code Playgroud)

除了drf的答案中列出的那些之外,这里还有你需要的P/Invoke定义:

[DllImport("advapi32.dll", SetLastError = true)]
public static extern Boolean SetTokenInformation(IntPtr TokenHandle, int TokenInformationClass,
    IntPtr TokenInformation, UInt32 TokenInformationLength);

[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);

[DllImport("advapi32.dll")]
public static extern uint GetLengthSid(IntPtr pSid);

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ConvertStringSidToSid(
    string StringSid,
    out IntPtr ptrSid);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr LocalFree(IntPtr hMem);
Run Code Online (Sandbox Code Playgroud)

  • `TOKEN_MANDATORY_LABEL` 的定义在哪里?`var args = CommandRunner.ArgsToCommandLine(Args);` 行的目的是什么?如果您的“完整源代码”实际上是完整源代码,那就太好了,这样我们就不必花费数小时从 drf 的答案中拼凑起来并在线查找缺失的定义。 (2认同)

Pau*_*aul 5

Raymond Chen 在他的博客中谈到了这一点:

如何从提升的流程启动未提升的流程,反之亦然?

在 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)