You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何用Python脚本重启远程桌面?权限拒绝问题排查

解决使用win32api重启远程机器时的"Access is denied"权限问题

看到你遇到了远程重启时的权限拒绝错误,这个问题其实很常见——本地进程的权限调整根本管不到远程机器,下面我给你拆解原因和解决办法:

为什么会出现"Access is denied"?

你代码里的AdjustPrivilege(SE_SHUTDOWN_NAME)只是给当前本地Python进程添加了本地关机权限,但重启远程机器本质是要在目标机器上执行关机操作,这个本地权限完全不适用。而且同一网络不代表自动拥有远程机器的权限,Windows远程关机需要满足几个核心条件:

  • 你用来访问远程机器的账户,必须是目标机器的本地管理员或域管理员
  • 目标机器的组策略必须允许该账户执行"从远程系统强制关机"操作;
  • 代码里没有传递远程机器的管理员凭证,默认用当前本地用户身份去访问,而这个用户在远程机器上没有权限。

具体解决方案

第一步:配置远程机器的权限

先在目标机器上做以下设置:

  1. 按下Win+R输入gpedit.msc打开组策略编辑器;
  2. 导航到计算机配置>Windows设置>安全设置>本地策略>用户权限分配
  3. 找到从远程系统强制关机,双击添加你用来访问远程的账户(本地管理员或域管理员);
  4. 确保目标机器的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

火山引擎 最新活动