使用 Powershell 在远程计算机上启动 GUI 程序

使用 Powershell 在远程计算机上启动 GUI 程序

同一网络上安装了两台 Windows 7 计算机。
我启用了所有必要的功能,以便它们可以进行通信winrm

当我运行以下命令时:

Invoke-Command -ComputerName REMOTE-PC -ScriptBlock { Start-Process calc.exe }

它运行正常,但远程机器上从未出现过该程序。据我所知,这是预期的行为。

我假设进程正确启动,然后在会话结束时立即关闭。如何运行该程序才能使其出现在主机上?

答案1

根据设计,您实际上不应该能够在其他人的会话中启动进程。

(需要澄清的是,即使您在计算机桌面上以交互方式登录,并且同时使用相同的凭据通过另一个单独的网络登录到同一台机器,这些仍然算作两个不同的登录会话。)

这完全违反了 Windows 本身的安全模型,试图破坏它的行为将遭到反对。因此,您不太可能找到一种简单、可支持的方式来执行此操作。这在技术上是可行的,但它涉及以本地系统身份运行、复制另一个登录用户的安全令牌,并使用该备用令牌启动进程。您需要 Windows API 来实现这一点,而这几乎是 Powershell 唯一不擅长的事情。有关详细信息,请参阅 Windows API WTSQueryUserTokenCreateProcessAsUser

还有一个想法,为了不在你的 Cheerios 里撒尿,你可以通过远程创建启动该过程的计划任务来实现这一点。参见https://devblogs.microsoft.com/scripting/how-can-i-remotely-start-an-interactive-process/了解更多信息。

编辑:哦,我忘了……很确定带-i参数的 PsExec 可以做到这一点。您必须提供登录会话 ID。并拥有执行此操作的权限。它很可能使用我提到的相同 Windows API,它利用了 PsExec 安装以本地系统运行的临时服务这一事实。

答案2

正如另一个答案中提到的,我能够使用 PsExec 来实现这个功能。

  1. 下载工具
  2. 解压至C:\Windows\PSTools
  3. 添加C:\Windows\PSTools到你的 PATH
  4. 获取 RDP 会话的进程 ID(tasklist可以工作,或者使用一行代码$session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1]:)
  5. 启动进程:(PsExec.exe -s -i 123 calc.exe“123”是 RDP 会话 ID)

这是我使用 Ansible 2.3.0 执行的操作:

- name: get RDP session number
  win_shell: "{{ item }}"
  with_items:
    - $session = tasklist /fo CSV | findstr RDP ; $session = $session.Split(",")[3] ; $session.Split('"')[1]
  register: rdp_session

- name: start calc.exe
  win_shell: PsExec.exe -s -i {{ rdp_session.results[0].stdout_lines[0] }} calc.exe

答案3

您可以在远程计算机上创建一个“可调用钩子”:这是一个计划任务,设置为“仅在用户登录时运行”,指定在执行时登录的用户帐户下运行。任务的操作是您要运行的可执行文件。

可以通过 powershell 或 schtasks 远程创建计划任务,然后使用 schtasks 或 powershell 的 Start-ScheduledTask 只需通过任务本身的“名称”即可调用。

  1. 在远程计算机上,创建一个由运行当前会话的用户运行的准系统计划任务。
  2. 将任务设置为“仅当用户登录时”运行
  3. 如果计划任务的“操作”选项卡中的 .exe 或项目在文件系统项上设置了“以管理员身份运行”(右键单击,属性,兼容性>以管理员身份运行),则计划任务也需要以提升的权限运行,否则它将失败或不会出现。
  4. 计划任务的作者应该具有管理员权限,以便在远程调用时,您可以使用此管理员帐户调用计划任务,然后在“仅在用户登录时运行”下指定的用户配置文件上执行该计划任务。
  5. 确保机器以该用户身份登录。
  6. 如果机器被锁定,此任务是否能正确执行可能取决于应用程序。根据我的经验,确实如此。
  7. 从这里,您可以使用 schtasks.exe 并调用计划任务的名称以及主机名,并传递用于编写计划任务的提升帐户的凭据(而不是将登录的用户)。
  8. 如果远程计算机正在运行支持 Start-ScheduledTask 的 powershell 版本,您也可以在 powershell 中调用它。

要调用该任务,您可以通过指定的名称引用该任务:

schtasks /run /TN "mytaskname" /s "host" /u "user" /p "password"

可以使用 powershell 中的 schtasks.exe 或 New-ScheduledTaskPrincipal 远程创建计划任务。如果任务中的可执行文件或“操作”项在文件系统上标记为“以管理员身份运行”,则该任务将需要在任务创建期间附加 New-ScheduledTaskPrincipal 凭据,以便正确设置该属性。

当前登录的用户可以是一个移动目标,尽管可以在计划任务创建之前通过 powershell 使用 Get-LoggedOnUser 进行查询。

此类详细代码将在接下来的 48 小时内发布,我希望让大家能够了解其基本结构。

答案4

如果您想避免使用 RDPing 获取 Victor 答案中的进程 ID,您可以通过query session在远程计算机上调用命令来实现。示例 Powershell 脚本

$user = "some_user"
$password = "password of the user"
$ComputerName = "name of the remote machine"

$processId = Invoke-Command $session -ScriptBlock  {
param($user)
    $sessions = query session $user;
    return $sessions[1].split(" ", [System.StringSplitOptions]::RemoveEmptyEntries)[2];

} -ArgumentList ($user)

PsExec \\$ComputerName -u $user -p $password -h -i $processId [some executable name]  [arguments to be passed if any]

相关内容