如何在窗口上运行具有提升权限的脚本

我正在编写一个 pyqt 应用程序,需要执行管理任务。我希望我的剧本开头能有更高的特权。我知道,这个问题在索马里或其他论坛上被问了很多次。但是人们建议的解决方案是看看这个 SO 问题 从 Python 脚本中请求 UAC 升级?

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

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."

它实际上请求允许提升,但打印行永远不会得到执行。有人可以帮助我成功地运行以上代码。先谢谢你。

221324 次浏览

在对 你把密码拿走的答案的评论中,有人说 ShellExecuteEx 不会将其 STDOUT 发送回原始 shell。所以您不会看到“ I am root now”,即使代码可能工作得很好。

不要打印东西,试着写到一个文件:

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"

然后看看档案。

谢谢你们的回复。我已经让我的脚本与普雷斯顿兰德斯早在2010年写的模块/脚本一起工作。经过两天的互联网浏览,我可以找到脚本,因为它是深藏在 pywin32邮件列表。使用这个脚本可以更容易地检查用户是否是管理员,如果不是,那么请求 UAC/admin 权限。它确实在单独的窗口中提供输出,以查明代码正在做什么。示例说明如何使用脚本中包含的代码。为了所有正在 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())

下面是一个使用 stdout 重定向的解决方案:

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()

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

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

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

这是一个只需要 ctype 模块的解决方案。支持 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, )

我可以确认 delphifirst 的解决方案是可行的,并且是运行具有更高特权的 Python 脚本问题的最简单、最简单的解决方案。

我创建了一个到 Python 可执行文件(python.exe)的快捷方式,然后通过在调用 python.exe 之后添加脚本的名称来修改这个快捷方式。接下来,我在快捷方式的“兼容性”选项卡上选择了“作为管理员运行”。在执行快捷方式时,您会得到一个提示,要求以管理员身份运行该脚本。

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

我希望我的解释能够帮助其他人更快地了解如何使用提升的特权运行 Python 脚本。

此外,如果你的工作目录与 lpDirectory 不同,你也可以使用 lpDirectory

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

如果改变路径不是一个理想的选择,那么它将派上用场 删除 python3.x 的 unicode

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

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

值得一提的是,如果您打算将应用程序打包为 PyInstaller并希望避免自己支持该特性,那么可以传递 --uac-admin--uac-uiaccess参数,以便在启动时请求 UAC 升级。

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

原型:

# 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())

这对我很有效:


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

  1. 制作一个批处理文件
  2. 使用引号添加 python.exe“(这里是 py 文件)”
  3. 保存批处理文件
  4. 右键单击,然后单击以管理员身份运行

用普亚克 它是由普雷斯顿兰德斯原管理脚本的更新 链接到 python 项目: https://pypi.org/project/pyuac/ https://github.com/Preston-Landers/pyuac 这招对我很管用

from pyuac import main_requires_admin


@main_requires_admin
def main():
print("Do stuff here that requires being run as an admin.")
# The window will disappear as soon as the program exits!
input("Press enter to close the window. >")


if __name__ == "__main__":
main()

JetBrains 的 温氏电梯(已签名的 evator.exe 和 launcher.exe 可用于 给你)允许生成一个请求提升特权的子进程,同时保持 stdin/stdout/stderr 的完整性:

import ctypes
import subprocess
import sys


if not ctypes.windll.shell32.IsUserAnAdmin():
print("not an admin, restarting...")
subprocess.run(["launcher.exe", sys.executable, *sys.argv])
else:
print("I'm an admin now.")
> python example.py
not an admin, restarting...
# UAC prompt is shown
I'm an admin now.