【问题标题】:How do you de-elevate privileges for a child process您如何取消提升子进程的权限
【发布时间】:2010-11-13 12:05:21
【问题描述】:

我知道如何使用以下进程启动具有管理员权限的进程:

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

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

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

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

【问题讨论】:

  • 同样的问题:stackoverflow.com/questions/196949/… 答案看起来很吓人。
  • 不仅可怕,而且不正确。 UAC 提升是一种方法,没有记录的方法可以返回(注入 explorer.exe 或使用任务计划程序是很麻烦的,可能不是一个好主意)

标签: c# process uac elevation


【解决方案1】:

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

这个想法是在 UN-ELEVATED 模式下运行该进程,使用 Windows 的文件资源管理器进程 explorer.exe (info)。 假设我们要启动的进程在$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

【讨论】:

  • 这是我见过的启动未提升进程的最佳答案之一......遗憾的是,我无法使用它,因为我无法捕获 MyUnElevatedProcess.exe 的标准输出或标准错误。
  • 我正在尝试使用此解决方案,但应用程序在 Windir 文件夹而不是 Temp 文件夹下运行。有任何解决方案吗? (即文件相对于 windir 文件夹)
  • 要将其用于现代 UI 安装程序中完成页面上的复选框,请使用两行。第一行:!define MUI_FINISHPAGE_RUN "$WINDIR\explorer.exe",第二行:!define MUI_FINISHPAGE_RUN_PARAMETERS "$TEMP\MyUnElevatedProcess.exe"
  • 在链接页面上,a comment from a Microsoft employee(?) is:“很遗憾,Windows Shell 团队已回复说“Explorer.exe AppName.exe”的当前行为是一个错误,可能在未来的更新中不起作用/ Windows 版本。应用程序不应依赖它。”
  • 最后一个补充:如果您的参数包含空格,您可以将它们嵌入到 escaped 引号内 将参数嵌入到引号内(如果它们包含空格)$\" 喜欢:@ 987654331@.
【解决方案2】:

我们最终使用了这篇代码项目文章中的示例:High elevation can be bad for your application: How to start a non-elevated process at the end of the installation

到目前为止它似乎工作,我收集它注入到 RunDll32.exe,我的 C++/Win32 相当弱,所以我没有过多地研究实际的实现,只是它的使用。确认它适用于 x86 和 x64 的 Vista 和 Win7(至少对我们而言,x86 和 x64 需要不同的 dll,在安装时检查并使用正确的)。

【讨论】:

    【解决方案3】:

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

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

    GitHub 中搜索此代码的 C# 版本时,我在 Microsoft 的 Node.js 工具 for Visual Studio 存储库中找到了以下实现: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);
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果您想从提升的进程启动未提升的进程,您可以复制 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. Dublicate 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;
          }
      }
      

      【讨论】:

        【解决方案5】:

        您可以使用ProcessStartInfo.UserNameProcessStartInfo.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());
            }
        }
        

        【讨论】:

        • 如果用户名/密码未知,你会怎么做?需要为任何未知机器工作
        • @Davy8 Everyone 用户似乎在所有 Windows 机器上都可用。我试过了,但不知道为什么它仍然抱怨用户名/密码不正确。甚至Everyone 用户可能有一些隐藏密码?很奇怪。
        猜你喜欢
        • 1970-01-01
        • 2020-05-21
        • 1970-01-01
        • 2011-10-16
        • 2020-12-20
        • 2011-11-13
        • 2014-11-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多