仅在需要时如何提升权限?

84

这个问题适用于Windows Vista操作系统!

我有一个应用程序,通常情况下不需要管理员权限就可以使用。但是有一个活动确实需要管理员权限,但我不想在大多数情况下都以较高的权限启动应用程序,因为用户大部分时间可能甚至不会使用该功能。

我考虑了一种方法,可以在某些事件(例如按下按钮)时提升应用程序的权限。例如:

如果用户单击此按钮,则会提示UAC对话框或请求同意。我应该如何做到这一点?

6个回答

59

我不认为可以提升当前正在运行的进程。据我所知,Windows Vista会在启动时授予进程管理员特权。如果您查看使用UAC的各种程序,您应该会发现每次需要执行管理操作时它们实际上都会启动一个单独的进程(任务管理器是其中之一, Paint.NET是另一个,后者实际上是一个.NET应用程序)。

解决此问题的典型方法是在启动提升的进程时指定命令行参数(abatishchev的建议就是其中一种方法),以便启动的进程只知道显示某个对话框,完成此操作后退出。因此,对于用户来说,几乎不会注意到已启动并退出了一个新进程,而是似乎打开了相同应用程序的新对话框(特别是如果您对提升后的进程的主窗口进行一些黑客改造,使其成为父进程的子进程)。如果您不需要提升访问权限的用户界面,那就更好了。

有关Vista上UAC的全面讨论,我建议您参阅这篇非常详细的文章(代码示例是用C++编写的,但我怀疑您无论如何都需要使用WinAPI和P/Invoke来完成大部分C#中的操作)。希望现在您至少知道正确的方法,尽管设计符合UAC标准的程序远非易事...


4
Windows 7 有什么变化,还是回答“不,使用新进程”依然适用吗?谢谢。 - Radim Vansa
2
很抱歉,对于Windows 7来说,我恐怕没有任何改变。就我所知,我是一个经常使用/开发Win7的用户/开发者。 - Noldorin
2
这正是任务管理器的工作原理。当您单击显示所有用户任务的按钮时,它会退出当前任务管理器,然后调用另一个具有管理员权限的任务管理器。 - Natalie Adams
3
从技术上讲,它首先打开了新的任务管理器。否则,是什么在进行开启呢? :-) - wizzwizz4

17

正如在这里所说:

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

这将使用管理员权限运行进程,以便您可以在注册表中执行任何所需操作,但是返回到您的应用程序时将恢复正常权限。


27
无法提升当前正在运行的进程。 - Jacob Proffitt
1
这不是真的。你可以更改进程的所有者,并为用户设置DACL和ACL值,使他们具有管理权限... - Nightforce2
1
运行时未提升的访问令牌将由函数CreateRestrictedToken分配一个受限制的令牌。UAC标识符用于验证人类交互,然后提升或启用运行为“500”组SID所需的SID/令牌组。您可以使用OpenProcessToken、GetTokenInformation、SetThreadToken、CreateWellknownSid、SetTokenInformation、AdjustTokenPrivileges(添加已启用的SID)和CheckTokenMembership(确保新令牌具有所需内容)来更改受限制的令牌。通过“IsTokenRestricted”交叉检查您的令牌,以确保您的代码按照预期工作。 - Nightforce2
1
注意:微软特意未公开此API以防止恶意软件的传播。因此,在他们公开之前或其他人泄露这个技巧之前,我们不会提供任何代码示例。有几种方法可以绕过这个问题。最常见的是“Runas”,这是他们想让人们使用的。这是我能给你的最好的指针,而不会具体说明。阅读有关Vista/W7 SDK API的详细信息和解释将有助于您了解UAC的工作原理,并为您提供如何处理它的良好思路。微软自己说过,这从来不是一个安全实现,而是对恶意软件开发者的一种威胁。 - Nightforce2
4
如果您没有管理员权限(即尚未提升权限),那么AdjustTokenPrivileges等方法会失败,因此这种方法只能在已获得管理员权限的情况下才能使用。 - Ben Schwehn
显示剩余3条评论

13

以下是MSDN KB文章981778,介绍了如何“自我提升”应用程序:

http://support.microsoft.com/kb/981778

它包含在Visual C++、Visual C#和Visual Basic.NET中可下载的示例。

这种方法可以避免启动单独的进程,但实际上是以升级的用户身份重新启动原始应用程序。尽管如此,在某些情况下仍然非常有用,例如在不适合将代码复制到单独的可执行文件中的情况下。

要取消提升,需要退出应用程序。


1
对于按钮来说很方便,但是当你想要同样的效果用在菜单项上时,会变得非常复杂混乱。 - Nyerguds
3
你可以在这里找到代码: https://code.msdn.microsoft.com/windowsapps/CSUACSelfElevation-644673d3 - Matthiee
答案的链接已失效,评论中的链接指向一个2608项长的列表。 - DonBoitnott
你可以在这里找到它。 - mirh

5

3
也许这个简单的例子对某些人很有用:

using System;
using System.Linq;
using System.Reflection;
using System.Diagnostics;
using System.Security.Principal;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    internal static class Program
    {
        private class Form1 : Form
        {
            internal Form1()
            {
                var button = new Button{ Dock = DockStyle.Fill };
                button.Click += (sender, args) => RunAsAdmin();
                Controls.Add(button);

                ElevatedAction();
            }
        }

        [STAThread]
        internal static void Main(string[] arguments)
        {
            if (arguments?.Contains("/run_elevated_action") == true)
            {
                ElevatedAction();
                return;
            }

            Application.Run(new Form1());
        }

        private static void RunAsAdmin()
        {
            var path = Assembly.GetExecutingAssembly().Location;
            using (var process = Process.Start(new ProcessStartInfo(path, "/run_elevated_action")
            {
                Verb = "runas"
            }))
            {
                process?.WaitForExit();
            }
        }

        private static void ElevatedAction()
        {
            MessageBox.Show($@"IsElevated: {IsElevated()}");
        }

        private static bool IsElevated()
        {
            using (var identity = WindowsIdentity.GetCurrent())
            {
                var principal = new WindowsPrincipal(identity);

                return principal.IsInRole(WindowsBuiltInRole.Administrator);
            }
        }

    }
}

1
我知道这是一篇旧文章,但是针对任何遇到MarcP建议的人做出回应。他引用的msdn帖子确实重新启动了所有代码示例中的应用程序。代码示例使用其他建议中已经提出的runas动词。
我下载了代码以确保,但这是来自原始msdn文章的内容:
4. 单击“是”以批准提升。然后,原始应用程序将以提升的管理员身份重新启动。
5. 关闭应用程序。

1
你能在问题中添加MSDN链接吗?我找不到叫MarcP的用户。 - Alf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接