【问题标题】:How to run script with elevated privilege on windows如何在 Windows 上以提升的权限运行脚本
【发布时间】:2013-11-09 10:36:43
【问题描述】:

我正在编写一个需要执行管理任务的 pyqt 应用程序。我宁愿以提升权限开始我的脚本。我知道这个问题在 SO 或其他论坛中被问过很多次。但是人们建议的解决方案是看看这个 SO question Request UAC elevation from within a Python script?

但是,我无法执行链接中给出的示例代码。我已将此代码放在主文件的顶部并尝试执行它。

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)
print "I am root now."

它实际上要求提升权限,但打印行永远不会被执行。有人可以帮助我成功运行上述代码。提前致谢。

【问题讨论】:

  • 删除sys.exit(0)并将print放在if块内
  • 谢谢。那行得通。如果您可以作为答案发布,我会接受。
  • 我的第一条评论有错误。 print语句的位置是对的,放到if块里面后,asadmin命令运行脚本时就不会执行了。

标签: python windows admin elevated-privileges


【解决方案1】:

谢谢大家的回复。我的脚本与 Preston Landers 早在 2010 年编写的模块/脚本一起工作。在浏览互联网两天后,我可以找到该脚本,因为它被深深地隐藏在 pywin32 邮件列表中。使用此脚本,可以更轻松地检查用户是否为管理员,如果不是,则请求 UAC/管理员权限。它确实在单独的窗口中提供输出,以找出代码在做什么。有关如何使用脚本中还包含的代码的示例。为了所有在 Windows 上寻找 UAC 的人的利益,请查看此代码。我希望它可以帮助寻找相同解决方案的人。它可以在你的主脚本中使用这样的东西:-

import admin
if not admin.isUserAdmin():
        admin.runAsAdmin()

实际代码是:-

#!/usr/bin/env python
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4

# (C) COPYRIGHT © Preston Landers 2010
# Released under the same license as Python 2.6.5


import sys, os, traceback, types

def isUserAdmin():

    if os.name == 'nt':
        import ctypes
        # WARNING: requires Windows XP SP2 or higher!
        try:
            return ctypes.windll.shell32.IsUserAnAdmin()
        except:
            traceback.print_exc()
            print "Admin check failed, assuming not an admin."
            return False
    elif os.name == 'posix':
        # Check for root on Posix
        return os.getuid() == 0
    else:
        raise RuntimeError, "Unsupported operating system for this module: %s" % (os.name,)

def runAsAdmin(cmdLine=None, wait=True):

    if os.name != 'nt':
        raise RuntimeError, "This function is only implemented on Windows."

    import win32api, win32con, win32event, win32process
    from win32com.shell.shell import ShellExecuteEx
    from win32com.shell import shellcon

    python_exe = sys.executable

    if cmdLine is None:
        cmdLine = [python_exe] + sys.argv
    elif type(cmdLine) not in (types.TupleType,types.ListType):
        raise ValueError, "cmdLine is not a sequence."
    cmd = '"%s"' % (cmdLine[0],)
    # XXX TODO: isn't there a function or something we can call to massage command line params?
    params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
    cmdDir = ''
    showCmd = win32con.SW_SHOWNORMAL
    #showCmd = win32con.SW_HIDE
    lpVerb = 'runas'  # causes UAC elevation prompt.

    # print "Running", cmd, params

    # ShellExecute() doesn't seem to allow us to fetch the PID or handle
    # of the process, so we can't get anything useful from it. Therefore
    # the more complex ShellExecuteEx() must be used.

    # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)

    procInfo = ShellExecuteEx(nShow=showCmd,
                              fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
                              lpVerb=lpVerb,
                              lpFile=cmd,
                              lpParameters=params)

    if wait:
        procHandle = procInfo['hProcess']    
        obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
        rc = win32process.GetExitCodeProcess(procHandle)
        #print "Process handle %s returned code %s" % (procHandle, rc)
    else:
        rc = None

    return rc

def test():
    rc = 0
    if not isUserAdmin():
        print "You're not an admin.", os.getpid(), "params: ", sys.argv
        #rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
        rc = runAsAdmin()
    else:
        print "You are an admin!", os.getpid(), "params: ", sys.argv
        rc = 0
    x = raw_input('Press Enter to exit.')
    return rc


if __name__ == "__main__":
    sys.exit(test())

【讨论】:

  • 非常感谢您的回答。我遇到的问题是我的 Qt GUI 在使用 shellexecuteEX 执行时没有完全显示,您使用 showcommand 的广泛命令帮助我使其工作。谢谢! :)
  • 谢谢,必须添加 nShow 和 fMask 参数才能使用 Qt gui。
  • 你能在 pywin32 邮件列表中发布代码来自的链接吗??
  • 这是在 github 上为 python3 gist.github.com/sylvainpelissier/… 分叉的相同脚本
  • @HrvojeT 谢谢你!我花了很长时间才弄清楚如何以管理员身份运行 python 进程
【解决方案2】:

answer you took the code from 的 cmets 中,有人说 ShellExecuteEx 不会将其 STDOUT 发布回原始 shell。所以你不会看到“我现在是 root”,即使代码可能工作正常。

不要打印东西,而是尝试写入文件:

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)
with open("somefilename.txt", "w") as out:
    print >> out, "i am root"

然后查看文件。

【讨论】:

  • 但它根本不会调出我的 pyqt 窗口。用 ZetCode zetcode.com/gui/pyqt4/firstprograms 测试了代码
  • 但这会提示您获得许可。有什么办法可以跳过这个
  • @deenbandhu 如果您可以跳过提示,那么曾经编写的每个病毒都会这样做,并且您会不断受到感染。这是一个永远不会消失的功能。
【解决方案3】:

我找到了一个非常简单的方法来解决这个问题。

  1. python.exe 创建快捷方式
  2. 将快捷方式目标更改为C:\xxx\...\python.exe your_script.py
  3. 点击快捷方式属性面板中的“高级...”,然后点击“以管理员身份运行”选项

我不确定这些选项的拼写是否正确,因为我使用的是中文版的Windows。

【讨论】:

  • 方式也可以解释为:以管理员身份运行python,然后执行脚本。然后自动所有脚本都将具有管理员访问权限。
【解决方案4】:

这是一个带有标准输出重定向的解决方案:

def elevate():
    import ctypes, win32com.shell.shell, win32event, win32process
    outpath = r'%s\%s.out' % (os.environ["TEMP"], os.path.basename(__file__))
    if ctypes.windll.shell32.IsUserAnAdmin():
        if os.path.isfile(outpath):
            sys.stderr = sys.stdout = open(outpath, 'w', 0)
        return
    with open(outpath, 'w+', 0) as outfile:
        hProc = win32com.shell.shell.ShellExecuteEx(lpFile=sys.executable, \
            lpVerb='runas', lpParameters=' '.join(sys.argv), fMask=64, nShow=0)['hProcess']
        while True:
            hr = win32event.WaitForSingleObject(hProc, 40)
            while True:
                line = outfile.readline()
                if not line: break
                sys.stdout.write(line)
            if hr != 0x102: break
    os.remove(outpath)
    sys.stderr = ''
    sys.exit(win32process.GetExitCodeProcess(hProc))

if __name__ == '__main__':
    elevate()
    main()

【讨论】:

  • 这导致了一个错误,python 3.8 ``` File ".\main.py", line 26, in elevate with open(outpath, 'w+', 0) as outfile: ValueError: can '没有无缓冲的文本 I/O```
【解决方案5】:

这是一个只需要 ctypes 模块的解决方案。支持pyinstaller包装的程序。

#!python
# coding: utf-8
import sys
import ctypes

def run_as_admin(argv=None, debug=False):
    shell32 = ctypes.windll.shell32
    if argv is None and shell32.IsUserAnAdmin():
        return True

    if argv is None:
        argv = sys.argv
    if hasattr(sys, '_MEIPASS'):
        # Support pyinstaller wrapped program.
        arguments = map(unicode, argv[1:])
    else:
        arguments = map(unicode, argv)
    argument_line = u' '.join(arguments)
    executable = unicode(sys.executable)
    if debug:
        print 'Command line: ', executable, argument_line
    ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1)
    if int(ret) <= 32:
        return False
    return None


if __name__ == '__main__':
    ret = run_as_admin()
    if ret is True:
        print 'I have admin privilege.'
        raw_input('Press ENTER to exit.')
    elif ret is None:
        print 'I am elevating to admin privilege.'
        raw_input('Press ENTER to exit.')
    else:
        print 'Error(ret=%d): cannot elevate privilege.' % (ret, )

【讨论】:

  • 您好,它确实要求提升,但是一旦我单击“是”,由于某种奇怪的原因,程序会运行两次。对此有何意见?
  • 它运行了两次,因为它第一次是以管理员身份启动程序的新实例。如果他的函数没有返回 True,你需要退出程序。或者,您可以稍微改变一下以启动一个不同的程序,使用这个只是一个“启动板”。
  • 谢谢!有没有办法在原始终端中获取新进程的输出而不打开额外的窗口?
【解决方案6】:
  1. 制作批处理文件
  2. 用引号添加python.exe“(这里是你的py文件)”
  3. 保存批处理文件
  4. 右键单击,然后单击以管理员身份运行

【讨论】:

    【解决方案7】:

    我可以确认 delphifirst 的解决方案有效,并且是解决以提升的权限运行 python 脚本问题的最简单、最简单的解决方案。

    我创建了 python 可执行文件 (python.exe) 的快捷方式,然后通过在调用 python.exe 后添加我的脚本名称来修改快捷方式。接下来,我在快捷方式的“兼容性选项卡”上选中了“以管理员身份运行”。执行快捷方式时,系统会提示您以管理员身份运行脚本的权限。

    我的特定 python 应用程序是一个安装程序。该程序允许安装和卸载另一个 python 应用程序。在我的例子中,我创建了两个快捷方式,一个名为“appname install”,另一个名为“appname uninstall”。两个快捷方式之间的唯一区别是 python 脚本名称后面的参数。在安装程序版本中,参数是“安装”。在卸载版本中,参数是“卸载”。安装程序脚本中的代码评估提供的参数并根据需要调用适当的函数(安装或卸载)。

    我希望我的解释能帮助其他人更快地弄清楚如何以提升的权限运行 python 脚本。

    【讨论】:

      【解决方案8】:

      如果你的工作目录不同于你可以使用 lpDirectory

          procInfo = ShellExecuteEx(nShow=showCmd,
                                lpVerb=lpVerb,
                                lpFile=cmd,
                                lpDirectory= unicode(direc),
                                lpParameters=params)
      

      如果更改路径不是一个理想的选择,将会派上用场 删除 python 3.X 的 unicode

      【讨论】:

        【解决方案9】:

        确保路径中有python,如果没有,win键+r,输入“%appdata%”(不带引号)打开本地目录,然后进入程序目录,打开python然后选择你的python版本目录.单击文件选项卡并选择复制路径并关闭文件资源管理器。

        然后再次执行 win 键 + r,输入 control 并回车。搜索环境变量。点击结果,你会得到一个窗口。在右下角单击环境变量。在系统端查找路径中,选择它并单击编辑。 在新窗口中,单击新建并将路径粘贴到那里。单击确定,然后在第一个窗口中应用。重新启动您的电脑。然后最后一次执行 win + r,输入 cmd 并执行 ctrl + shift + enter。授予权限并打开文件资源管理器,转到您的脚本并复制其路径。返回 cmd ,输入“python”并粘贴路径并回车。完成

        【讨论】:

          【解决方案10】:

          值得一提的是,如果您打算使用PyInstaller 打包您的应用程序并且明智地避免自己支持该功能,您可以传递--uac-admin--uac-uiaccess 参数以便在启动时请求UAC 提升。

          【讨论】:

            【解决方案11】:

            我想要一个更增强的版本,所以我最终得到了一个模块,它允许: 如果需要,UAC 请求,从非特权实例(使用 ipc 和网络端口)和其他一些糖果打印和记录。用法只是在脚本中插入 elevateme() :在非特权中,它侦听特权打印/日志,然后退出返回 false,在特权实例中,它立即返回 true。 支持pyinstaller。

            原型:

            # xlogger : a logger in the server/nonprivileged script
            # tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
            # redir : redirect stdout and stderr from privileged instance
            #errFile : redirect stderr to file from privileged instance
            def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):
            

            winadmin.py

            #!/usr/bin/env python
            # -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
            # vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4
            
            # (C) COPYRIGHT © Preston Landers 2010
            # (C) COPYRIGHT © Matteo Azzali 2020
            # Released under the same license as Python 2.6.5/3.7
            
            
            import sys, os
            from traceback import print_exc
            from multiprocessing.connection import Listener, Client
            import win32event #win32com.shell.shell, win32process
            import builtins as __builtin__ # python3
            
            # debug suffixes for remote printing
            dbz=["","","",""] #["J:","K:", "G:", "D:"]
            LOGTAG="LOGME:"
            
            wrconn = None
            
            
            #fake logger for message sending
            class fakelogger:
                def __init__(self, xlogger=None):
                    self.lg = xlogger
                def write(self, a):
                    global wrconn
                    if wrconn is not None:
                        wrconn.send(LOGTAG+a)
                    elif self.lg is not None:
                        self.lg.write(a)
                    else:
                        print(LOGTAG+a)
                    
            
            class Writer():
                wzconn=None
                counter = 0
                def __init__(self, tport=6000,authkey=b'secret password'):
                    global wrconn
                    if wrconn is None:
                        address = ('localhost', tport)
                        try:
                            wrconn = Client(address, authkey=authkey)
                        except:
                            wrconn = None
                        wzconn = wrconn
                        self.wrconn = wrconn
                    self.__class__.counter+=1
                    
                def __del__(self):
                    self.__class__.counter-=1
                    if self.__class__.counter == 0 and wrconn is not None:
                        import time
                        time.sleep(0.1) # slows deletion but is enough to print stderr
                        wrconn.send('close')
                        wrconn.close()
                
                def sendx(cls, mesg):
                    cls.wzconn.send(msg)
                    
                def sendw(self, mesg):
                    self.wrconn.send(msg)
                    
            
            #fake file to be passed as stdout and stderr
            class connFile():
                def __init__(self, thekind="out", tport=6000):
                    self.cnt = 0
                    self.old=""
                    self.vg=Writer(tport)
                    if thekind == "out":
                        self.kind=sys.__stdout__
                    else:
                        self.kind=sys.__stderr__
                    
                def write(self, *args, **kwargs):
                    global wrconn
                    global dbz
                    from io import StringIO # # Python2 use: from cStringIO import StringIO
                    mystdout = StringIO()
                    self.cnt+=1
                    __builtin__.print(*args, **kwargs, file=mystdout, end = '')
                    
                    #handles "\n" wherever it is, however usually is or string or \n
                    if "\n" not in mystdout.getvalue():
                        if mystdout.getvalue() != "\n":
                            #__builtin__.print("A:",mystdout.getvalue(), file=self.kind, end='')
                            self.old += mystdout.getvalue()
                        else:
                            #__builtin__.print("B:",mystdout.getvalue(), file=self.kind, end='')
                            if wrconn is not None:
                                wrconn.send(dbz[1]+self.old)
                            else:
                                __builtin__.print(dbz[2]+self.old+ mystdout.getvalue(), file=self.kind, end='')
                                self.kind.flush()
                            self.old=""
                    else:
                            vv = mystdout.getvalue().split("\n")
                            #__builtin__.print("V:",vv, file=self.kind, end='')
                            for el in vv[:-1]:
                                if wrconn is not None:
                                    wrconn.send(dbz[0]+self.old+el)
                                    self.old = ""
                                else:
                                    __builtin__.print(dbz[3]+self.old+ el+"\n", file=self.kind, end='')
                                    self.kind.flush()
                                    self.old=""
                            self.old=vv[-1]
            
                def open(self):
                    pass
                def close(self):
                    pass
                def flush(self):
                    pass
                    
                    
            def isUserAdmin():
                if os.name == 'nt':
                    import ctypes
                    # WARNING: requires Windows XP SP2 or higher!
                    try:
                        return ctypes.windll.shell32.IsUserAnAdmin()
                    except:
                        traceback.print_exc()
                        print ("Admin check failed, assuming not an admin.")
                        return False
                elif os.name == 'posix':
                    # Check for root on Posix
                    return os.getuid() == 0
                else:
                    print("Unsupported operating system for this module: %s" % (os.name,))
                    exit()
                    #raise (RuntimeError, "Unsupported operating system for this module: %s" % (os.name,))
            
            def runAsAdmin(cmdLine=None, wait=True, hidden=False):
            
                if os.name != 'nt':
                    raise (RuntimeError, "This function is only implemented on Windows.")
            
                import win32api, win32con, win32process
                from win32com.shell.shell import ShellExecuteEx
            
                python_exe = sys.executable
                arb=""
                if cmdLine is None:
                    cmdLine = [python_exe] + sys.argv
                elif not isinstance(cmdLine, (tuple, list)):
                    if isinstance(cmdLine, (str)):
                        arb=cmdLine
                        cmdLine = [python_exe] + sys.argv
                        print("original user", arb)
                    else:
                        raise( ValueError, "cmdLine is not a sequence.")
                cmd = '"%s"' % (cmdLine[0],)
            
                params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
                if len(arb) > 0:
                    params += " "+arb
                cmdDir = ''
                if hidden:
                    showCmd = win32con.SW_HIDE
                else:
                    showCmd = win32con.SW_SHOWNORMAL
                lpVerb = 'runas'  # causes UAC elevation prompt.
            
                # print "Running", cmd, params
            
                # ShellExecute() doesn't seem to allow us to fetch the PID or handle
                # of the process, so we can't get anything useful from it. Therefore
                # the more complex ShellExecuteEx() must be used.
            
                # procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)
            
                procInfo = ShellExecuteEx(nShow=showCmd,
                                          fMask=64,
                                          lpVerb=lpVerb,
                                          lpFile=cmd,
                                          lpParameters=params)
            
                if wait:
                    procHandle = procInfo['hProcess']    
                    obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
                    rc = win32process.GetExitCodeProcess(procHandle)
                    #print "Process handle %s returned code %s" % (procHandle, rc)
                else:
                    rc = procInfo['hProcess']
            
                return rc
            
            
            # xlogger : a logger in the server/nonprivileged script
            # tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
            # redir : redirect stdout and stderr from privileged instance
            #errFile : redirect stderr to file from privileged instance
            def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):
                global dbz
                if not isUserAdmin():
                    print ("You're not an admin.", os.getpid(), "params: ", sys.argv)
            
                    import getpass
                    uname = getpass.getuser()
                    
                    if (tport> 0):
                        address = ('localhost', tport)     # family is deduced to be 'AF_INET'
                        listener = Listener(address, authkey=b'secret password')
                    rc = runAsAdmin(uname, wait=False, hidden=True)
                    if (tport> 0):
                        hr = win32event.WaitForSingleObject(rc, 40)
                        conn = listener.accept()
                        print ('connection accepted from', listener.last_accepted)
                        sys.stdout.flush()
                        while True:
                            msg = conn.recv()
                            # do something with msg
                            if msg == 'close':
                                conn.close()
                                break
                            else:
                                if msg.startswith(dbz[0]+LOGTAG):
                                    if xlogger != None:
                                        xlogger.write(msg[len(LOGTAG):])
                                    else:
                                        print("Missing a logger")
                                else:
                                    print(msg)
                                sys.stdout.flush()
                        listener.close()
                    else: #no port connection, its silent
                        WaitForSingleObject(rc, INFINITE);
                    return False
                else:
                    #redirect prints stdout on  master, errors in error.txt
                    print("HIADM")
                    sys.stdout.flush()
                    if (tport > 0) and (redir):
                        vox= connFile(tport=tport)
                        sys.stdout=vox
                        if not errFile:
                            sys.stderr=vox
                        else:
                            vfrs=open("errFile.txt","w")
                            sys.stderr=vfrs
                        
                        #print("HI ADMIN")
                    return True
            
            
            def test():
                rc = 0
                if not isUserAdmin():
                    print ("You're not an admin.", os.getpid(), "params: ", sys.argv)
                    sys.stdout.flush()
                    #rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
                    rc = runAsAdmin()
                else:
                    print ("You are an admin!", os.getpid(), "params: ", sys.argv)
                    rc = 0
                x = raw_input('Press Enter to exit.')
                return rc
                
            if __name__ == "__main__":
                sys.exit(test())
            

            【讨论】:

              【解决方案12】:

              这对我有用:


              import win32com.client as client
              
              required_command = "cmd" # Enter your command here
              
              required_password = "Simple1" # Enter your password here
              
              def run_as(required_command, required_password):
                  shell = client.Dispatch("WScript.shell")
                  shell.Run(f"runas /user:administrator {required_command}")
                  time.sleep(1)
                  shell.SendKeys(f"{required_password}\r\n", 0)
              
              
              if __name__ = '__main__':
                  run_as(required_command, required_password)
              

              以下是我用于上述代码的参考: https://win32com.goermezer.de/microsoft/windows/controlling-applications-via-sendkeys.html https://www.oreilly.com/library/view/python-cookbook/0596001673/ch07s16.html

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2016-03-24
                • 1970-01-01
                • 2017-06-09
                • 2013-08-14
                • 1970-01-01
                • 1970-01-01
                • 2018-04-21
                相关资源
                最近更新 更多