什么WinRM
WinRM 是微软的远程管理协议,广泛用于执行远程命令和管理远程系统,类似于linux中的ssh。
如何开启WinRM
在远程机器上进行以下设置
1.开启windows remote management服务并设置为自动启动,执行以下powershell(管理员运行)命令:
Start-Service winrm
Set-Service winrm -StartupType Automatic
2.开启powershell远程控制权限,执行以下powershell(管理员运行)命令:
Enable-PSRemoting -Force
备注:该命令会自动创建相应的防火墙规则开放相应的端口
3.允许基本身份认证和非加密连接,执行以下cmd命令(也是管理员运行,非powershell):
winrm set winrm/config/service/auth @{Basic="true"}
winrm set winrm/config/service @{AllowUnencrypted="true"}
备注:因为我的场景对安全性无任何要求,所以无需https加密连接。
测试WinRM连接
要测试WinRM是否成功配置,最简单的方法就是使用powershell对远程机器发出命令。
假如远程机器的ip为192.168.1.112,想要列出其桌面下的所有文件。可在本地机器上打开powershell,执行以下命令:
Invoke-Command -ComputerName 192.168.1.112 -ScriptBlock { Get-ChildItem -Path "C:\Users\ken\Desktop" } -Credential (Get-Credential)
如下图所示:
备注:在这一步中,极有可能会出错,具体解决过程可参考后面内容。
如果成功执行,即会返回远程主机桌面上相应的文件列表,如下图所示:
备注:上述命令中的IP地址和桌面路径(主要是Users\后面的用户名不同)因人而异,请作相应的修改。
代码调用
C#中调用
这里我使用的是.net 8.0,这里要安装两个Nuget包,分别是:
Microsoft.Management.Infrastructure {3.0.0}
System.Management.Automation {7.3.7}
如下图所示:
具体代码如下:
using System.Collections.ObjectModel;
using System.Management.Automation.Runspaces;
using System.Management.Automation;
using System.Security;
namespace DemoWinRM
{
internal class Program
{
static void Main(string[] args)
{
Demo2();
}
static void Demo2()
{
// 请修改为正确的连接信息
var remoteComputer = "192.168.1.112";
var userName = "ken";
var password = "1234566666";
var command = "Get-ChildItem -Path \"C:\\Users\\ken\\Desktop\"";
WSManConnectionInfo connectionInfo = new WSManConnectionInfo();
connectionInfo.ComputerName = remoteComputer;
connectionInfo.Credential = new PSCredential(userName, ConvertToSecureString(password));
using (Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo))
{
runspace.Open();
using (PowerShell ps = PowerShell.Create())
{
ps.Runspace = runspace;
ps.AddScript(command);
Collection<PSObject> results = ps.Invoke();
foreach (var result in results)
{
Console.WriteLine(result);
}
}
}
}
static SecureString ConvertToSecureString(string password)
{
SecureString secureString = new SecureString();
foreach (char c in password)
{
secureString.AppendChar(c);
}
secureString.MakeReadOnly();
return secureString;
}
}
}
运行结果如下:
python中调用
这里我使用是的python3.11,要先安装winrm模块:
pip install pywinrm
具体代码如下:
import winrm
def run():
hostname = '192.168.1.112'
username = 'ken'
password = '1234566666'
command = 'powershell -Command "Get-ChildItem -Path \"C:\\Users\\ken\\Desktop\""'
# 输出结果中有文时,编码无论怎么改都好像有问题(感觉是这个库的一个BUG)
session = winrm.Session(hostname, auth=(username, password))
response = session.run_ps(command)
print(response.std_out.decode('gbk')) # 无论是cp1252, gbk, utf-8等都有问题
if __name__ == '__main__':
run()
运行结果如下(这里的乱码问题请暂时忽略):
问题排查与解决
(1) [192.168.1.112] 连接到远程服务器 192.168.1.112 失败,并显示以下错误消息: WinRM 客户端无法处理该请求。如果身份验证方案与
Kerberos 不同,或者客户端计算机未加入到域中, 则必须使用 HTTPS 传输或者必须将目标计算机添加到 TrustedHosts 配置设置。
如下图所示:
解决办法,在客户端(本地)计算机中,将ip 192.168.1.112加入到可信任主机配置即可,在powershell下(管理员)执行以下命令:
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.112" -Force
(2) 在上述命令将ip加入到可信任主机配置时,可能也会报错,即:Set-Item : 客户端无法连接到请求中指定的目标。 请验证该目标上的服务是否正在运行以及是否正在接受请求。 有关目标(通常是 II
S 或 WinRM)上运行的 WS 管理服务,请查阅日志和文档。 如果目标是 WinRM 服务,则在目标上运行以下命令来分析和配置 WinRM 服
务: "winrm quickconfig"。
如下图所示:
这个问题也好解决,在客户端上重启一下winrm服务,然后重新执行上述命令(管理员执行)即可:
Restart-Service winrm
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "192.168.1.112" -Force
如下图所示:
参考
Windows 远程管理 - Win32 apps | Microsoft Learn
使用 WinRM 进行 PowerShell 远程处理的安全注意事项 - PowerShell | Microsoft Learn