如何用Python脚本重启远程桌面?权限拒绝问题排查
解决使用win32api重启远程机器时的"Access is denied"权限问题
看到你遇到了远程重启时的权限拒绝错误,这个问题其实很常见——本地进程的权限调整根本管不到远程机器,下面我给你拆解原因和解决办法:
为什么会出现"Access is denied"?
你代码里的AdjustPrivilege(SE_SHUTDOWN_NAME)只是给当前本地Python进程添加了本地关机权限,但重启远程机器本质是要在目标机器上执行关机操作,这个本地权限完全不适用。而且同一网络不代表自动拥有远程机器的权限,Windows远程关机需要满足几个核心条件:
- 你用来访问远程机器的账户,必须是目标机器的本地管理员或域管理员;
- 目标机器的组策略必须允许该账户执行"从远程系统强制关机"操作;
- 代码里没有传递远程机器的管理员凭证,默认用当前本地用户身份去访问,而这个用户在远程机器上没有权限。
具体解决方案
第一步:配置远程机器的权限
先在目标机器上做以下设置:
- 按下
Win+R输入gpedit.msc打开组策略编辑器; - 导航到计算机配置>Windows设置>安全设置>本地策略>用户权限分配;
- 找到从远程系统强制关机,双击添加你用来访问远程的账户(本地管理员或域管理员);
- 确保目标机器的
Remote Registry服务处于开启状态(可以在服务管理器里搜索开启)。
第二步:修改代码,传递远程凭证
原来的代码缺少远程身份验证的步骤,我们需要先通过IPC$共享建立到远程机器的连接,再执行关机操作。修改后的代码如下:
import win32security import win32api import win32net import win32netcon import sys import time from ntsecuritycon import * import pywintypes def AdjustPrivilege(priv, enable=1): # 获取当前进程令牌并调整权限 flags = TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY htoken = win32security.OpenProcessToken(win32api.GetCurrentProcess(), flags) idd = win32security.LookupPrivilegeValue(None, priv) newPrivileges = [(idd, SE_PRIVILEGE_ENABLED)] if enable else [(idd, 0)] win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges) def ConnectToRemote(host, username, password): # 通过IPC$共享建立远程连接 try: win32net.NetUseAdd( host, win32netcon.RESOURCETYPE_DISK, f"\\\\{host}\\IPC$", None, username, password ) return True except Exception as e: print(f"远程连接失败: {str(e)}") return False def DisconnectFromRemote(host): # 断开远程IPC连接 try: win32net.NetUseDel(f"\\\\{host}\\IPC$", 0, 0) except Exception as e: print(f"断开连接失败: {str(e)}") def RebootServer(host, username, password, message='Rebooting', timeout=30, bForce=0, bReboot=1): if not ConnectToRemote(host, username, password): return AdjustPrivilege(SE_SHUTDOWN_NAME) try: win32api.InitiateSystemShutdown(host, message, timeout, bForce, bReboot) print(f"重启指令已发送至 {host}") except pywintypes.error as e: print(f"重启操作失败: {str(e)}") finally: AdjustPrivilege(SE_SHUTDOWN_NAME, 0) DisconnectFromRemote(host) def AbortReboot(host, username, password): if not ConnectToRemote(host, username, password): return AdjustPrivilege(SE_SHUTDOWN_NAME) try: win32api.AbortSystemShutdown(host) print(f"取消重启指令已发送至 {host}") except pywintypes.error as e: print(f"取消重启失败: {str(e)}") finally: AdjustPrivilege(SE_SHUTDOWN_NAME, 0) DisconnectFromRemote(host) # 测试配置:替换为你的远程机器信息 remote_host = "XXX.XXX.XX.XX" remote_admin_user = "Administrator" # 远程机器的管理员账户 remote_admin_pass = "你的管理员密码" RebootServer(remote_host, remote_admin_user, remote_admin_pass) time.sleep(10) print('Aborting shutdown') AbortReboot(remote_host, remote_admin_user, remote_admin_pass)
代码关键说明
ConnectToRemote函数通过Windows的IPC$共享(用于进程间通信的特殊共享)建立到远程机器的身份验证,这样后续的关机操作会使用你提供的管理员凭证;- 必须先完成远程连接,再调用
InitiateSystemShutdown,否则会默认使用本地用户身份,而本地用户在远程机器上没有权限; - 操作完成后记得断开远程连接,避免留下无效的连接会话。
额外注意事项
- 如果是域环境,用户名要写成
域名\用户名的格式,比如MYDOMAIN\AdminUser; - 远程机器的防火墙要允许远程管理相关的端口(比如135、445等),可以暂时关闭防火墙测试,确认没问题后再配置规则;
- 不要硬编码密码到代码里,建议用环境变量或者加密存储的方式读取密码,提升安全性。
内容的提问来源于stack exchange,提问作者DevLeb2022




