当前位置: 首页 > news >正文

在 Windows 系统上降低 UAC 权限运行程序(从管理员权限降权到普通用户权限)

在 Windows 系统中,管理员权限和非管理员权限运行的程序之间不能使用 Windows 提供的通信机制进行通信。对于部分文件夹(ProgramData),管理员权限创建的文件是不能以非管理员权限修改和删除的。

然而,一个进程运行之后启动的子进程,会继承当前进程的 UAC 权限;于是有时我们会有降权运行的需要。本文将介绍 Windows 系统上降权运行的几种方法。


本文的降权运行指的是:

  1. 有一个 A 程序是以管理员权限运行的(典型的,如安装包);
  2. 有一个 B 程序会被 A 启动(我们期望降权运行的 B 程序)。

本文内容

    • 如何判断当前进程的 UAC 权限
    • 方法一:使用 runas 命令来运行程序(推荐)
    • 方法二:使用 explorer.exe 代理运行程序(推荐)
    • 方法三:在启动进程时传入用户名和密码
    • 方法四:使用 Shell 进程的 Access Token 来启动进程

如何判断当前进程的 UAC 权限

通过下面的代码,可以获得当前进程的 UAC 权限。

var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);

而如果要判断是否是管理员权限,则使用:

if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
    // 当前正在以管理员权限运行。
}

此代码如果在 .NET Core 中编写,需要额外安装 Windows 兼容包:Microsoft.Windows.Compatibility。+

方法一:使用 runas 命令来运行程序(推荐)

使用 runas 命令来运行,可以指定一个权限级别:

> runas /trustlevel:0x20000 "C:\Users\walterlv\Desktop\walterlv.exe"
var subProcessFileName = "C:\Users\walterlv\Desktop\walterlv.exe";
Process.Start("runas.exe", $"/trustlevel:0x20000 {subProcessFileName}");

关于 runas 的更多细节,可以参考我的另一篇博客:

  • Windows 下使用 runas 命令以指定的权限启动一个进程(非管理员、管理员) - 吕毅

方法二:使用 explorer.exe 代理运行程序(推荐)

因为绝大多数用户启动系统的时候,explorer.exe 进程都是处于运行状态,而如果启动一个新的 explorer.exe,都会自动激活当前正在运行的进程而不会启动新的。

于是我们可以委托默认以普通权限运行的 explorer.exe 来代理启动我们需要启动的子进程,这时启动的子进程便是与 explorer.exe 相同权限的,也就是降权运行了。

var subProcessFileName = "C:\Users\walterlv\Desktop\walterlv.exe";
Process.Start("explorer.exe", subProcessFileName);

通过以上代码,walterlv.exe 就会以与 explorer.exe 相同权限运行,也就是降权运行了。

下面的代码,如果发现自己是以管理员权限运行的,那么就降权重新运行自己,然后自己退出。

var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
{
    // 检测到当前进程是以管理员权限运行的,于是降权启动自己之后,把自己关掉。
    Process.Start("explorer.exe", Assembly.GetEntryAssembly().Location);
    Shutdown();
    return;
}

方法三:在启动进程时传入用户名和密码

ProcessStartInfo 中有 UserNamePassword 属性,设置此属性可以以此计算机上的另一个用户身份启动此进程。如果这个用户是普通用户,那么就会以普通权限运行此进程。

var processInfo = new ProcessStartInfo
{
    Verb = "runas",
    FileName = "walterlv.exe",
    UserName = "walterlv",
    Password = ReadPassword(),
    UseShellExecute = false,
    LoadUserProfile = true
};
Process.Start(processInfo);

上面的 ReadPassword 函数来自我的另一篇博客:如何让 .NET Core 命令行程序接受密码的输入而不显示密码明文 - walterlv。

然而,此方法最大的问题在于——产品级的程序,不可能也不应该知道用户的密码!所以实际上这样的方法并不实用。

方法四:使用 Shell 进程的 Access Token 来启动进程

此方法需要较多的 Windows API 调用,我没有尝试过这种方法,但是你可以自行尝试下面的链接:

  • c# - How do you de-elevate privileges for a child process - Stack Overflow

参考资料

  • c# starting process with lowered privileges from UAC admin level process - Stack Overflow
  • c# - How do you de-elevate privileges for a child process - Stack Overflow
  • c# - How do you de-elevate privileges for a child process - Stack Overflow
  • windows - Force a program to run without administrator privileges or UAC? - Super User
  • High elevation can be bad for your application: How to start a non-elevated process at the end of the installation - CodeProject
  • How to Enable Drag and Drop for an Elevated MFC Application on Vista/Windows 7 • Helge Klein

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 专栏素材
  • Visual Studio 如何能够不进行编译就调试 .NET/C# 项目(用于解决大项目编译缓慢的问题)
  • 仅反射加载(ReflectionOnlyLoadFrom)的 .NET 程序集,如何反射获取它的 Attribute 元数据呢?
  • 全局或为单独的项目添加自定义的 NuGet 源
  • 电脑总是意外从睡眠状态唤醒,可以找出原因然后解决
  • 我收集的各种公有 NuGet 源
  • 制作一个极简的 .NET 客户端应用自安装或自更新程序
  • 在 MSBuild 编译项目时阻止输出所有的警告信息
  • 编写 MSBuild 内联编译任务(Task)用于获取当前编译环境下的所有编译目标(Target)
  • 如何在 csproj 中用 C# 代码写一个内联的编译任务 Task
  • 安装和运行 .NET Core 版本的 PowerShell
  • 让你的 Windows 应用程序在任意路径也能够直接通过文件名执行
  • 如何为你的 Windows 应用程序关联一种或多种文件类型
  • 如何为你的 Windows 应用程序关联 URL 协议,以便在浏览器中也能打开你的应用
  • 四种方法获取可执行程序的文件路径(.NET Core / .NET Framework)
  • 「前端早读君006」移动开发必备:那些玩转H5的小技巧
  • Android组件 - 收藏集 - 掘金
  • Apache Pulsar 2.1 重磅发布
  • Create React App 使用
  • create-react-app做的留言板
  • Javascript Math对象和Date对象常用方法详解
  • js正则,这点儿就够用了
  • php ci框架整合银盛支付
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Spring Security中异常上抛机制及对于转型处理的一些感悟
  • STAR法则
  • Vue2 SSR 的优化之旅
  • 基于axios的vue插件,让http请求更简单
  • 简析gRPC client 连接管理
  • 解决iview多表头动态更改列元素发生的错误
  • 聊一聊前端的监控
  • 猫头鹰的深夜翻译:JDK9 NotNullOrElse方法
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 小程序测试方案初探
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • LIGO、Virgo第三轮探测告捷,同时探测到一对黑洞合并产生的引力波事件 ...
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • #pragma pack(1)
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (2)Java 简介
  • (3)选择元素——(14)接触DOM元素(Accessing DOM elements)
  • (7)STL算法之交换赋值
  • (8)STL算法之替换
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (编译到47%失败)to be deleted
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (分类)KNN算法- 参数调优
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (个人笔记质量不佳)SQL 左连接、右连接、内连接的区别
  • (一)UDP基本编程步骤
  • (转)关于多人操作数据的处理策略
  • ... 是什么 ?... 有什么用处?
  • ./configure、make、make install 命令