如何为子进程降低权限?
如何为子进程降低权限?
我知道如何从一个进程中使用管理员权限启动另一个进程:\n
proc.StartInfo.UseShellExecute = true; proc.StartInfo.Verb = "runas";
\n其中,proc是一个System.Diagnostics.Process。但是如何做相反的操作呢?\n如果您所在的进程已经具有提升的权限,如何以非管理员权限启动新进程呢?更准确地说,我们需要以与Windows资源管理器相同的权限级别启动新进程,因此,如果UAC被禁用,则不会有任何变化,但是如果UAC被启用,而我们的进程正在以提升的权限运行,我们需要以非提升的方式执行某个操作,因为我们正在创建一个虚拟驱动器,如果使用提升的权限创建,而Windows资源管理器正在以非提升的方式运行,那么它将不会显示出来。\n请随意更改标题为更好的描述,我想不出一个好的描述。
孩子进程降低权限的问题在Raymond Chen的博客中得到了解决。他在博客中提到了如何从提升的进程中启动一个非提升的进程,以及反之。通过在GitHub上搜索C#版本的代码,我在Microsoft的Node.js工具的Visual Studio存储库中找到了以下实现:SystemUtilities.cs(ExecuteProcessUnElevated函数)。
为了防止文件消失,以下是文件的内容:
//版权所有(c)Microsoft。版权所有。根据Apache License,Version 2.0许可。有关许可信息,请参阅项目根目录中的License.txt文件。 using System; using System.Runtime.InteropServices; namespace Microsoft.NodejsTools.SharedProject { ////// 用于访问窗口IShell*接口以使用它们来启动一个非提升的进程的实用程序 /// internal class SystemUtility { ////// 我们是提升的,应该启动非提升的进程。我们不能直接创建进程,否则它将变得提升。因此,为了解决这个问题,我们让资源管理器执行进程创建(资源管理器通常以非提升方式运行)。 /// internal static void ExecuteProcessUnElevated(string process, string args, string currentDirectory = "") { var shellWindows = (IShellWindows)new CShellWindows(); //获取桌面窗口 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); //获取资源管理器浏览器 var serviceGuid = SID_STopLevelBrowser; var interfaceGuid = typeof(IShellBrowser).GUID; var shellBrowser = (IShellBrowser)serviceProvider.QueryService(ref serviceGuid, ref interfaceGuid); //获取资源管理器调度 var dispatch = typeof(IDispatch).GUID; var folderView = (IShellFolderViewDual)shellBrowser.QueryActiveShellView().GetItemObject(SVGIO_BACKGROUND, ref dispatch); var shellDispatch = (IShellDispatch2)folderView.Application; //使用非提升的调度启动进程 shellDispatch.ShellExecute(process, args, currentDirectory, string.Empty, SW_SHOWNORMAL); } ////// 互操作定义 /// 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还发表了一篇后续文章,使用CreateProcess
而不是ShellExecute
来启动非提升的进程。
这个问题的出现是因为需要降低子进程的权限,而解决方法是使用来自Code Project文章的示例代码。该示例代码似乎在RunDll32.exe中进行注入,我对C++/Win32的了解有限,所以没有深入研究实际实现,只是使用了它。经确认,它在Vista和Win7的x86和x64上都能正常工作(至少对我们来说如此,x86和x64需要不同的dll,在安装时进行检查并使用正确的dll)。
如何将子进程的权限降低?
问题的原因是想要将子进程的权限降低。解决方法是使用explorer.exe进程来运行子进程,以非提升模式运行。代码示例是使用NSIS安装程序的代码,具体代码如下:
Exec '"$WINDIR\explorer.exe" "$TEMP\MyUnElevatedProcess.exe"'
该方法是将子进程放入临时文件夹,然后使用explorer.exe进程来运行该子进程,以非提升模式运行。然而,该方法无法捕获子进程的标准输出和标准错误输出。该方法也有一个问题,就是子进程会在Windir文件夹下运行,而不是临时文件夹。为了解决这个问题,可以将文件路径设置为相对于Windir文件夹的路径。
如果想在Modern UI安装程序的完成页面上使用复选框,可以使用以下两行代码:
!define MUI_FINISHPAGE_RUN "$WINDIR\explorer.exe" !define MUI_FINISHPAGE_RUN_PARAMETERS "$TEMP\MyUnElevatedProcess.exe"
需要注意的是,Shell通常使用explorer.exe来运行应用程序,但可以在注册表中更改Shell应用程序,因此最好不要将Shell应用程序硬编码到您用于启动应用程序的代码中。
总结起来,通过使用explorer.exe进程来运行子进程,可以实现将子进程的权限降低。然而,这种方法可能不适用于所有情况,并且可能在未来的Windows更新/版本中不再起作用。