如何使用 ctypes 运行具有提升的 UAC 权限的脚本?
Posted
技术标签:
【中文标题】如何使用 ctypes 运行具有提升的 UAC 权限的脚本?【英文标题】:How do I run a script with elevated UAC permissions using ctypes? 【发布时间】:2022-01-14 21:48:47 【问题描述】:我正在尝试通过 Python 3.x 为 Windows 10 命令行创建实用工具。由于它可以更好地将通用命令行命令格式化为更用户友好的菜单,因此我希望它在运行时需要通过 UAC 提升权限。
我正在使用here 描述的ctypes
方法,它确实具有Python 的可执行请求UAC 提升。
但是,由于我将要编写菜单等的很多东西都需要(或在没有这些提升权限的情况下受到严重限制),我希望脚本退出(最好通过sys.exit
)如果它不需要没有找到。
在我提到的ctypes
方法中,它应该运行如下;
它定义了一个函数is_admin()
,获取ctypes.windll.shell32.IsUserAnAdmin()
的值,如果为0,则返回false。
is_admin()
在条件下被调用,如果它为 false,它会尝试执行命令行命令以使用ShellExecuteW
和来自sys
的一些变量将脚本作为可执行文件重新运行;
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
在我的代码中,我添加了一个变量elevReq
,并将其设置为true;
if is_admin():
success("Already running as administrator!") # "success" and "warn" are defined earlier
elevReq = True
else:
warn("Requesting administrative permissions...", False)
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv[0]), None, 1)
elevReq = True
我用另一个条件来跟进它,检查elevReq
是否为真,is_admin()
是否为假,看看用户是否在 UAC 的弹出窗口中选择了“否”——如果是,它 应该抛出一个关于缺乏提升权限的错误,然后退出;
if elevReq and is_admin() == False:
error("[FATAL] Elevation was not given! Stopping...", True)
sys.exit(1)
我遇到的问题是给定的方法似乎实际上并没有提升 Python 的权限。 UAC确实弹出,但选择任何选项时,似乎似乎并不重要,因此上述条件无论如何。从一开始就在提升的命令提示符下手动运行脚本没有这个问题。
这是脚本没有重新加载的问题吗?如果没有,为什么它仍然退出?
【问题讨论】:
【参考方案1】:ShellExecute API call will spawn a new process,它不会提升当前正在运行的进程的权限。
我们来分析一下这段代码sn-p:
if is_admin():
main()
else:
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)
当你第一次在没有权限的情况下运行这个 Python 脚本时,这个 initial 进程将跳转到代码的最后一行,因为is_admin
返回False
。在那里,将显示 UAC 提示。
is_admin
应该返回True
并且应该调用main
。
如果 UAC 提示被拒绝,则不会创建新进程。
无论 UAC 响应如何,初始进程都会返回 return code,但其权限将保持不变。
如果您想自己尝试,请在文件末尾添加input()
,在接受 UAC 提示后您应该能够看到两个不同的窗口。
为避免您的代码被执行两次,请务必将所有内容保存在 main
函数中。如果您想根据返回代码采取行动,这仅对失败有意义(代码 32),那么进程应该优雅地结束,让新生成的进程完成它的工作。
【讨论】:
这就是问题所在;它似乎对这个新流程没有任何作用。我可以看到它是在任务管理器中制作的,但它似乎没有运行任何新的窗口或命令提示符/PowerShell 实例。我尝试在独立的命令提示符窗口和 Visual Studio Code 提供的内置 PowerShell 提示符中运行它。 所以,我只是尝试修改lpFile
和lpParameters
参数以运行cmd /k <python.exe> <script>
而不仅仅是python 和脚本,但是没有像@ 一样发出CMD 提示987654334@ 参数,这意味着新进程根本没有在视觉上呈现。这对我的代码至关重要,因为它应该是一个帮助使用提示的实用程序。
bump,我需要这方面的帮助,因为我看不出它不应该工作的原因。
尝试使用ShellExecuteEx
和nShow=SW_SHOW
,也许?这就是我用的,我可以看到我的新进程的提示。
我似乎找不到任何关于 ShellExecuteEx 的文档来实现它——我在微软方面找到的只是 ShellExecuteA、W 或 ExA 和 W(最后两个有很大不同)—— - 不管怎样,A 和 W 都有 nCmdShow
参数,所以我试了一下,但实际上在新进程中重新启动脚本仍然没有这样的运气。【参考方案2】:
import ctypes
import sys
import platform
def admin() -> "Admin Bool":
"""Requests UAC Admin on Windows with a prompt"""
if platform.system() == "Windows":
ctypes.windll.shell32.ShellExecuteW(
None,
'runas',
sys.executable,
' '.join(sys.argv),
None,
None
)
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
else:
raise OSError("admin() only works for windows.")
这将通过管理员提示请求管理员。
如果返回True
,则表示用户已授予管理员权限。否则,False
将被返回。
【讨论】:
这和我之前提到的方法是一样的,小不同的是,这一次,都是在同一个函数中。问题不在于 UAC 没有弹出,而是即使在弹出之后,似乎仍然没有授予权限。即使使用这样格式化的代码,它仍然会返回 False 并运行代码(最终代码块)。以上是关于如何使用 ctypes 运行具有提升的 UAC 权限的脚本?的主要内容,如果未能解决你的问题,请参考以下文章