如何使用 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 提示。

如果接受 UAC 提示,则会创建一个全新进程(具有不同的进程 ID)并再次执行代码(从头开始),但这次使用管理员权限。现在is_admin 应该返回True 并且应该调用main。 如果 UAC 提示被拒绝,则不会创建新进程。

无论 UAC 响应如何,初始进程都会返回 return code,但其权限将保持不变。

如果您想自己尝试,请在文件末尾添加input(),在接受 UAC 提示后您应该能够看到两个不同的窗口。

为避免您的代码被执行两次,请务必将所有内容保存在 main 函数中。如果您想根据返回代码采取行动,这仅对失败有意义(代码 32),那么进程应该优雅地结束,让新生成的进程完成它的工作。

【讨论】:

这就是问题所在;它似乎对这个新流程没有任何作用。我可以看到它是在任务管理器中制作的,但它似乎没有运行任何新的窗口或命令提示符/PowerShell 实例。我尝试在独立的命令提示符窗口和 Visual Studio Code 提供的内置 PowerShell 提示符中运行它。 所以,我只是尝试修改lpFilelpParameters 参数以运行cmd /k <python.exe> <script> 而不仅仅是python 和脚本,但是没有像@ 一样发出CMD 提示987654334@ 参数,这意味着新进程根本没有在视觉上呈现。这对我的代码至关重要,因为它应该是一个帮助使用提示的实用程序。 bump,我需要这方面的帮助,因为我看不出它不应该工作的原因。 尝试使用ShellExecuteExnShow=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 权限的脚本?的主要内容,如果未能解决你的问题,请参考以下文章

如何在启动时运行需要 UAC 提升的程序?

MSF内网渗透系列2-权限提升

东塔 | Windows UAC 本地提权复现

如何启用普通和 UAC 提升权限应用程序之间的拖放

[技术分享]借用UAC完成的提权思路分享

怎么绕过权限?