【问题标题】:How do I run a script with elevated UAC permissions using ctypes?如何使用 ctypes 运行具有提升的 UAC 权限的脚本?
【发布时间】:2022-01-14 21:48:47
【问题描述】:

我正在尝试通过 Python 3.x 为 Windows 10 命令行创建实用工具。由于它可以更好地将通用命令行命令格式化为更用户友好的菜单,因此我希望它在运行时需要通过 UAC 提升权限。

我正在使用here 描述的ctypes 方法,它确实具有Python 的可执行请求UAC 提升。

但是,由于我将要编写菜单等的很多东西都需要(或在没有这些提升权限的情况下受到严重限制),我希望脚本退出(最好通过sys.exit)如果它不需要没有找到。

在我提到的ctypes方法中,它应该运行如下;

  1. 它定义了一个函数is_admin(),获取ctypes.windll.shell32.IsUserAnAdmin()的值,如果为0,则返回false。

  2. 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确实弹出,但选择任何选项时,似乎似乎并不重要,因此上述条件无论如何。从一开始就在提升的命令提示符下手动运行脚本没有这个问题。

这是脚本没有重新加载的问题吗?如果没有,为什么它仍然退出?

【问题讨论】:

    标签: python python-3.x permissions windows-10 user-account-control


    【解决方案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 提示。

    • 如果接受 UAC 提示,则会创建一个全新进程(具有不同的进程 ID)并再次执行代码(从头开始),但这次使用管理员权限。现在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 并运行代码(最终代码块)。
    猜你喜欢
    • 1970-01-01
    • 2018-04-21
    • 2011-06-07
    • 1970-01-01
    • 2013-11-09
    • 2010-12-08
    • 1970-01-01
    • 2018-08-03
    相关资源
    最近更新 更多