如何降低子进程的权限

发布于 2024-07-27 21:48:14 字数 434 浏览 5 评论 0原文

我知道如何从进程中启动具有管理员权限的进程:

proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";

其中 proc 是 System.Diagnostics.Process。 但如何做相反的事呢?

如果您所在的进程已经提升,如何在没有管理员权限的情况下启动新进程? 更准确地说,我们需要以与 Windows 资源管理器相同的权限级别启动新进程,因此如果禁用 UAC,则不会有任何变化,但如果启用 UAC,但我们的进程正在提升运行,我们需要在未提升的情况下执行某些操作因为我们正在创建一个虚拟驱动器,如果它是使用提升的权限创建的,并且 Windows 资源管理器在未提升的情况下运行,则它不会显示。

请随意将标题更改为更好的标题,我无法想出一个好的描述。

I know how to launch a process with Admin privileges from a process using:

proc.StartInfo.UseShellExecute = true;
proc.StartInfo.Verb = "runas";

where proc is a System.Diagnostics.Process. But how does one do the opposite?

If the process you're in is already elevated, how do you launch the new process without admin privileges? More accurately, we need to launch the new process with the same permission level as Windows Explorer, so no change if UAC is disabled, but if UAC is enabled, but our process is running elevated, we need to perform a certain operation un-elevated because we're creating a virtual drive and if it's created with elevated permissions and Windows explorer is running unelevated it won't show up.

Feel free to change the title to something better, I couldn't come up with a good description.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

夏日浅笑〃 2024-08-03 21:48:14

您的解决方案是使用 EXPLORER.exe 进程。

这个想法是使用 Windows 的文件资源管理器进程 explorer.exe (信息)。
假设我们要启动的进程位于 $TEMP\MyUnElevatedProcess.exe 上。

因此,对于 NSIS 代码,我将只写:(但可以用任何语言运行)

 Exec '"$WINDIR\explorer.exe" "$TEMP\MyUnElevatedProcess.exe"'

示例代码(使用 NSIS 安装程序

Exec '"$WINDIR\explorer.exe" "$TEMP\MyUnElevatedProcess.exe"'

***代码取自 http://mdb-blog.blogspot.com/2013/01/nsis -lunch-program-as-user-from-uac.html

The solution for you is to use EXPLORER.exe process.

The idea is to run the process in UN-ELEVATED mode, using windows's file explorer process explorer.exe (info).
Lets say the process that we want to launch is on $TEMP\MyUnElevatedProcess.exe.

So, for NSIS code, I will just write: (but can be run in ANY language)

 Exec '"$WINDIR\explorer.exe" "$TEMP\MyUnElevatedProcess.exe"'

Example code (using NSIS installer)

Exec '"$WINDIR\explorer.exe" "$TEMP\MyUnElevatedProcess.exe"'

***code taken from http://mdb-blog.blogspot.com/2013/01/nsis-lunch-program-as-user-from-uac.html

清眉祭 2024-08-03 21:48:14

我们最终使用了此代码项目文章中的示例:高海拔可能对您不利应用程序:如何在安装结束时启动非提升进程

到目前为止似乎有效,我收集它注入到 RunDll32.exe 中,我的 C++/Win32 相当弱,所以我也没有看实际实现的内容很多,只是使用。 确认它可以在 Vista 和 Win7 以及 x86 和 x64 中运行(至少对我们来说,x86 和 x64 需要不同的 dll,在安装时检查并使用正确的 dll)。

We ended up using the sample from this Code Project article: High elevation can be bad for your application: How to start a non-elevated process at the end of the installation

It seems to work so far, I gather it injects into RunDll32.exe, my C++/Win32 is fairly weak so I didn't look too much into the actual implementation, just it's use. Confirmed that it works in Vista and Win7 both x86 and x64 (at least for us, x86 and x64 require different dll's which is checked for at install time and the proper one is used).

挽清梦 2024-08-03 21:48:14

Raymond Chen 在他的博客中解决了这个问题:

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

GitHub 中搜索此代码的 C# 版本,我在 Microsoft 的 Node 中找到了以下实现适用于 Visual Studio 的 .js 工具存储库:SystemUtilities.csExecuteProcessUnElevated 函数)。

以防万一文件消失,文件内容如下:

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

Raymond Chen addressed this in his blog:

How can I launch an unelevated process from my elevated process and vice versa?

Searching in GitHub for a C# version of this code, I found the following implementation in Microsoft's Node.js tools for Visual Studio repository: SystemUtilities.cs (the ExecuteProcessUnElevated function).

Just in case the file disappears, here's the file's contents:

// 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);
        }
    }
}
一枫情书 2024-08-03 21:48:14

如果您想从提升的进程启动未提升的进程,您可以复制 shell 进程的访问令牌并使用它来启动新进程。

public static class UnelevatedProcessStarter
{
    public static void Start(string cmdArgs)
    {
        // 1. Get the shell
        var shell = NativeMethods.GetShellWindow();
        if (shell == IntPtr.Zero)
        {
            throw new Exception("Could not find shell window");
        }

        // 2. Copy the access token of the process
        NativeMethods.GetWindowThreadProcessId(shell, out uint shellProcessId);
        var hShellProcess = NativeMethods.OpenProcess(0x00000400 /* QueryInformation */, false, (int)shellProcessId);
        if (!NativeMethods.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out IntPtr hShellToken))
        {
            throw new Win32Exception();
        }

        // 3. Duplicate the acess token
        uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
        var securityAttributes = new SecurityAttributes();

        NativeMethods.DuplicateTokenEx(
            hShellToken,
            tokenAccess,
            ref securityAttributes,
            2 /* SecurityImpersonation */,
            1 /* TokenPrimary */,
            out IntPtr hToken);

        // 4. Create a new process with the copied token
        var si = new Startupinfo();
        si.cb = Marshal.SizeOf(si);

        if (!NativeMethods.CreateProcessWithTokenW(
            hToken,
            0x00000002 /* LogonNetcredentialsOnly */,
            null,
            cmdArgs,
            0x00000010 /* CreateNewConsole */,
            IntPtr.Zero,
            null,
            ref si,
            out ProcessInformation _))
        {
            throw new Win32Exception();
        }
    }

    public class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetShellWindow();
        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenProcessToken(IntPtr processHandle, UInt32 desiredAccess, out IntPtr tokenHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
            ref SecurityAttributes lpTokenAttributes,
            int impersonationLevel,
            int tokenType,
            out IntPtr phNewToken);
        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithTokenW(
            IntPtr hToken, int dwLogonFlags,
            string lpApplicationName, string lpCommandLine,
            int dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory, [In] ref Startupinfo lpStartupInfo, out ProcessInformation lpProcessInformation);
    }

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

    [StructLayout(LayoutKind.Sequential)]
    public struct SecurityAttributes
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public 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;
    }
}

If you want to start an unelevated process from an elevated one you could copy the access token of the shell process and use it to start a new process.

public static class UnelevatedProcessStarter
{
    public static void Start(string cmdArgs)
    {
        // 1. Get the shell
        var shell = NativeMethods.GetShellWindow();
        if (shell == IntPtr.Zero)
        {
            throw new Exception("Could not find shell window");
        }

        // 2. Copy the access token of the process
        NativeMethods.GetWindowThreadProcessId(shell, out uint shellProcessId);
        var hShellProcess = NativeMethods.OpenProcess(0x00000400 /* QueryInformation */, false, (int)shellProcessId);
        if (!NativeMethods.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out IntPtr hShellToken))
        {
            throw new Win32Exception();
        }

        // 3. Duplicate the acess token
        uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
        var securityAttributes = new SecurityAttributes();

        NativeMethods.DuplicateTokenEx(
            hShellToken,
            tokenAccess,
            ref securityAttributes,
            2 /* SecurityImpersonation */,
            1 /* TokenPrimary */,
            out IntPtr hToken);

        // 4. Create a new process with the copied token
        var si = new Startupinfo();
        si.cb = Marshal.SizeOf(si);

        if (!NativeMethods.CreateProcessWithTokenW(
            hToken,
            0x00000002 /* LogonNetcredentialsOnly */,
            null,
            cmdArgs,
            0x00000010 /* CreateNewConsole */,
            IntPtr.Zero,
            null,
            ref si,
            out ProcessInformation _))
        {
            throw new Win32Exception();
        }
    }

    public class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetShellWindow();
        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenProcessToken(IntPtr processHandle, UInt32 desiredAccess, out IntPtr tokenHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
            ref SecurityAttributes lpTokenAttributes,
            int impersonationLevel,
            int tokenType,
            out IntPtr phNewToken);
        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithTokenW(
            IntPtr hToken, int dwLogonFlags,
            string lpApplicationName, string lpCommandLine,
            int dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory, [In] ref Startupinfo lpStartupInfo, out ProcessInformation lpProcessInformation);
    }

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

    [StructLayout(LayoutKind.Sequential)]
    public struct SecurityAttributes
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public 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;
    }
}
方圜几里 2024-08-03 21:48:14

基于@NtFreX,我创建了另一个解决方案,

  1. 从当前身份获取令牌(似乎可以工作并且需要更少的代码), - 抱歉,这并不总是可以处理
  2. 从已经未提升的进程运行的句柄(因为在这种情况下,CreateProcessWithTokenW 失败,错误代码为 1314 - “客户端不拥有所需的权限”)并
  3. 返回进程 ID,以便调用代码可以等待它等。
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

public static class UnelevatedProcessStarter
{
    public static int Start(string cmdArgs)
    {
        // 1. Get the shell
        var shell = NativeMethods.GetShellWindow();
        if (shell == IntPtr.Zero)
        {
            throw new Exception("Could not find shell window");
        }

        // 2. Copy the access token of the process
        uint shellProcessId;
        NativeMethods.GetWindowThreadProcessId(shell, out shellProcessId);
        var hShellProcess = NativeMethods.OpenProcess(0x00000400 /* QueryInformation */, false, (int)shellProcessId);
        IntPtr hShellToken;
        if (!NativeMethods.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out hShellToken))
        {
            throw new Win32Exception();
        }

        // 3. Duplicate the access token
        uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
        var securityAttributes = new SecurityAttributes();
        IntPtr hToken;
        if (!NativeMethods.DuplicateTokenEx(
            hShellToken,
            tokenAccess,
            ref securityAttributes,
            2 /* SecurityImpersonation */,
            1 /* TokenPrimary */,
            out hToken))
        {
            throw new Win32Exception();
        }

        // 4. Create a new process with the copied token
        var si = new Startupinfo();
        si.cb = Marshal.SizeOf(si);
        ProcessInformation processInfo;
        if (!NativeMethods.CreateProcessWithTokenW(
            hToken,
            0x00000002 /* LogonNetcredentialsOnly */,
            null,
            cmdArgs,
            0x00000010 /* CreateNewConsole */,
            IntPtr.Zero,
            null,
            ref si,
            out processInfo))
        {
            // Can't do that when not elevated (see https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw)
            // -> start the process as usual
            if (Marshal.GetLastWin32Error() == 1314)
            {
                SecurityAttributes processSecurityAttributes = new SecurityAttributes();
                SecurityAttributes threadSecurityAttributes = new SecurityAttributes();
                if (!NativeMethods.CreateProcessAsUser(
                    IntPtr.Zero,
                    null,
                    cmdArgs,
                    ref processSecurityAttributes,
                    ref threadSecurityAttributes,
                    true,
                    0x00000010 /* CreateNewConsole */,
                    IntPtr.Zero,
                    null,
                    ref si,
                    out processInfo))
                {
                    throw new Win32Exception();
                }
            }
            else
            {
                throw new Win32Exception();
            }
        }

        return processInfo.dwProcessId;
    }

    public class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetShellWindow();
        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenProcessToken(IntPtr processHandle, UInt32 desiredAccess, out IntPtr tokenHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
            ref SecurityAttributes lpTokenAttributes,
            int impersonationLevel,
            int tokenType,
            out IntPtr phNewToken);
        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithTokenW(
            IntPtr hToken, int dwLogonFlags,
            string lpApplicationName, string lpCommandLine,
            int dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory,
            [In] ref Startupinfo lpStartupInfo,
            out ProcessInformation lpProcessInformation);

        [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
        public static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            ref SecurityAttributes lpProcessAttributes,
            ref SecurityAttributes lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref Startupinfo lpStartupInfo,
            out ProcessInformation lpProcessInformation);
    }

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

    [StructLayout(LayoutKind.Sequential)]
    public struct SecurityAttributes
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public 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;
    }
}

Based on @NtFreX I created another solution, that

  1. takes the token from the current identity (seems to work and needs less code), - sorry, that doesn't always work
  2. handles running from an already unelevated process (because in this case CreateProcessWithTokenW fails with error code 1314 - "A required privilege is not held by the client") and
  3. returns the process id so that the calling code can await it etc.
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Security.Principal;

public static class UnelevatedProcessStarter
{
    public static int Start(string cmdArgs)
    {
        // 1. Get the shell
        var shell = NativeMethods.GetShellWindow();
        if (shell == IntPtr.Zero)
        {
            throw new Exception("Could not find shell window");
        }

        // 2. Copy the access token of the process
        uint shellProcessId;
        NativeMethods.GetWindowThreadProcessId(shell, out shellProcessId);
        var hShellProcess = NativeMethods.OpenProcess(0x00000400 /* QueryInformation */, false, (int)shellProcessId);
        IntPtr hShellToken;
        if (!NativeMethods.OpenProcessToken(hShellProcess, 2 /* TOKEN_DUPLICATE */, out hShellToken))
        {
            throw new Win32Exception();
        }

        // 3. Duplicate the access token
        uint tokenAccess = 8 /*TOKEN_QUERY*/ | 1 /*TOKEN_ASSIGN_PRIMARY*/ | 2 /*TOKEN_DUPLICATE*/ | 0x80 /*TOKEN_ADJUST_DEFAULT*/ | 0x100 /*TOKEN_ADJUST_SESSIONID*/;
        var securityAttributes = new SecurityAttributes();
        IntPtr hToken;
        if (!NativeMethods.DuplicateTokenEx(
            hShellToken,
            tokenAccess,
            ref securityAttributes,
            2 /* SecurityImpersonation */,
            1 /* TokenPrimary */,
            out hToken))
        {
            throw new Win32Exception();
        }

        // 4. Create a new process with the copied token
        var si = new Startupinfo();
        si.cb = Marshal.SizeOf(si);
        ProcessInformation processInfo;
        if (!NativeMethods.CreateProcessWithTokenW(
            hToken,
            0x00000002 /* LogonNetcredentialsOnly */,
            null,
            cmdArgs,
            0x00000010 /* CreateNewConsole */,
            IntPtr.Zero,
            null,
            ref si,
            out processInfo))
        {
            // Can't do that when not elevated (see https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createprocesswithtokenw)
            // -> start the process as usual
            if (Marshal.GetLastWin32Error() == 1314)
            {
                SecurityAttributes processSecurityAttributes = new SecurityAttributes();
                SecurityAttributes threadSecurityAttributes = new SecurityAttributes();
                if (!NativeMethods.CreateProcessAsUser(
                    IntPtr.Zero,
                    null,
                    cmdArgs,
                    ref processSecurityAttributes,
                    ref threadSecurityAttributes,
                    true,
                    0x00000010 /* CreateNewConsole */,
                    IntPtr.Zero,
                    null,
                    ref si,
                    out processInfo))
                {
                    throw new Win32Exception();
                }
            }
            else
            {
                throw new Win32Exception();
            }
        }

        return processInfo.dwProcessId;
    }

    public class NativeMethods
    {
        [DllImport("user32.dll")]
        public static extern IntPtr GetShellWindow();
        [DllImport("user32.dll", SetLastError = true)]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(int processAccess, bool bInheritHandle, int processId);
        [DllImport("advapi32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool OpenProcessToken(IntPtr processHandle, UInt32 desiredAccess, out IntPtr tokenHandle);
        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool DuplicateTokenEx(IntPtr hExistingToken, uint dwDesiredAccess,
            ref SecurityAttributes lpTokenAttributes,
            int impersonationLevel,
            int tokenType,
            out IntPtr phNewToken);
        [DllImport("advapi32", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithTokenW(
            IntPtr hToken, int dwLogonFlags,
            string lpApplicationName, string lpCommandLine,
            int dwCreationFlags, IntPtr lpEnvironment,
            string lpCurrentDirectory,
            [In] ref Startupinfo lpStartupInfo,
            out ProcessInformation lpProcessInformation);

        [DllImport("advapi32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
        public static extern bool CreateProcessAsUser(
            IntPtr hToken,
            string lpApplicationName,
            string lpCommandLine,
            ref SecurityAttributes lpProcessAttributes,
            ref SecurityAttributes lpThreadAttributes,
            bool bInheritHandles,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            string lpCurrentDirectory,
            ref Startupinfo lpStartupInfo,
            out ProcessInformation lpProcessInformation);
    }

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

    [StructLayout(LayoutKind.Sequential)]
    public struct SecurityAttributes
    {
        public int nLength;
        public IntPtr lpSecurityDescriptor;
        public int bInheritHandle;
    }
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public 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;
    }
}
伴梦长久 2024-08-03 21:48:14

您可以使用 ProcessStartInfo.UserName 和 < a href="http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.password.aspx" rel="nofollow noreferrer">ProcessStartInfo.Password 指定所需的帐户您要运行的进程。

class Program
{
    static void Main(string[] args)
    {
        var psi = new ProcessStartInfo(@"c:\windows\system32\whoami.exe");
        var password = new SecureString();
        password.AppendChar('s');
        password.AppendChar('e');
        password.AppendChar('c');
        password.AppendChar('r');
        password.AppendChar('e');
        password.AppendChar('t');
        psi.Password = password;
        psi.UserName = "username";
        psi.UseShellExecute = false;
        psi.RedirectStandardOutput = true;

        var p = new Process();
        p.StartInfo = psi;
        p.Start();
        p.WaitForExit();

        Console.WriteLine(p.StandardOutput.ReadToEnd());
    }
}

You can use ProcessStartInfo.UserName and ProcessStartInfo.Password to specify the account you want your process to run under.

class Program
{
    static void Main(string[] args)
    {
        var psi = new ProcessStartInfo(@"c:\windows\system32\whoami.exe");
        var password = new SecureString();
        password.AppendChar('s');
        password.AppendChar('e');
        password.AppendChar('c');
        password.AppendChar('r');
        password.AppendChar('e');
        password.AppendChar('t');
        psi.Password = password;
        psi.UserName = "username";
        psi.UseShellExecute = false;
        psi.RedirectStandardOutput = true;

        var p = new Process();
        p.StartInfo = psi;
        p.Start();
        p.WaitForExit();

        Console.WriteLine(p.StandardOutput.ReadToEnd());
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文