【问题标题】:Automating Telnet with PowerShell使用 PowerShell 自动化 Telnet
【发布时间】:2011-02-07 16:58:15
【问题描述】:

如何编写 PowerShell 脚本来自动执行这组命令?

  • Telnet 到一台机器,
  • 执行几个命令,
  • 在 telnet 窗口中分析输出,
  • 根据该输出,再发送一些命令

【问题讨论】:

  • 什么是服务器端? Unix? osx?窗户+远程登录? powershell v1 还是 v2?你能在远程端安装东西吗?客户端?
  • powershell v2.我正在 Windows 机器上编写 powershell 脚本。远程端可以,linux,windows。我无法在远程站点上安装新东西

标签: powershell automation


【解决方案1】:

好吧,这不是最优雅的解决方案,它确实依赖于 shudder VBscript,但它就在这里......

创建一个 VBScript 以实际加速 telnet 会话,这是一个示例

set oShell = CreateObject("WScript.Shell")
oShell.run("Telnet")
WScript.Sleep 1000
oShell.SendKeys("Open 127.0.0.1 23")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("n")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys"MyName"
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("MyPassword")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000
oShell.SendKeys("MyCommand")
WScript.Sleep 1000
oShell.SendKeys("{Enter}")
WScript.Sleep 1000

然后使用 Powershell 调用该脚本并将您要执行的命令传递给它,在下面的示例中,这些命令存储在一个名为 CommandList.txt 的文件中

function Connect-MyTelnet{
Param(
 [string] $IPAddress,
 [string] $Port,
 [string] $UserName,
 [string] $Password,
 [string] $cmdlistPath
)
    ## - Setting default values:
    if($port -eq $null){ $Port = "23"; };
    if($cmdlistPath -eq $null) { $CmdlistPath = 'c:\temp\cmdlist.txt'; };

    ## create vbscript file: MyTelnetSession.vbs
    ## - For Microsoft Telnet:
    $MyVBScript = @"
                   set oShell = CreateObject("WScript.Shell")`r`n
                   oShell.run("Telnet")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("Open $IPAddress $Port")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("{Enter}")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("n")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("{Enter}")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("$UserName")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("{Enter}")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("$Password")`r`n
                   WScript.Sleep 1000`r`n
                   oShell.SendKeys("{Enter}")`r`n
                   WScript.Sleep 1000`r`n
                 "@;

    ## - Get file with telnet commands:
    [array] $Cmdlist = Get-Content $cmdlistPath;

    ## loop through and build each telnet command line:
    foreach($cmd in $cmdlist)
    {
        ## - Build VBscript lines:
        $MyVBScript += 'oShell.SendKeys("'+$cmd+'")'+"`r`n";
        $MyVBScript += "WScript.Sleep 1000`r`n";
        $MyVBScript += 'oShell.SendKeys("{Enter}")'+"`r`n";
        $MyVBScript += 'WScript.Sleep 1000'+"`r`n";
    }

    ## - Close Telnet Session:
        $MyVBScript += 'oShell.SendKeys("  QUIT")'+"`r`n";
        $MyVBScript += "WScript.Sleep 1000`r`n";
        $MyVBScript += 'oShell.SendKeys("{Enter}")'+"`r`n";
        $MyVBScript += 'WScript.Sleep 1000'+"`r`n";

    ## - Save and execute generated VBscript:
    $MYVBScript | Out-File -FilePath c:\temp\MyTelnet.vbs -Encoding ASCII;
    & c:\temp\MyTelnet.vbs
}; Set-Alias ct Connect-MyTelnet;

这应该可以满足您的要求...

注意:不是我的解决方案,从 this blog post 找到的,我已经使用过一两次了。

【讨论】:

    【解决方案2】:

    与其尝试自动执行 telnet 可执行文件,不如创建套接字并发出命令,将它们读回,并据此做出决定。这是一个连接到我的本地 Web 服务器的过于简单的示例:

    function test() {
      $msg = [System.Text.Encoding]::ASCII.GetBytes("GET / HTTP/1.0`r`nHost: localhost`r`n`r`n")
      $c = New-Object System.Net.Sockets.TcpClient("localhost", 80)
      $str = $c.GetStream()
      $str.Write($msg, 0, $msg.Length)
      $buf = New-Object System.Byte[] 4096
      $count = $str.Read($buf, 0, 4096)
      [System.Text.Encoding]::ASCII.GetString($buf, 0, $count)
      $str.Close()
      $c.Close()
    }
    

    显然您需要从端口 80 更改它,并传递用户名/密码而不是 Web 请求标头...但这应该足以让您开始。

    【讨论】:

    • 感谢您的样品。我对powershell很陌生。我根据上面的代码尝试了几件事,但没有奏效,但我一定遗漏了一些东西。这就是我想做的事情。 telnet portnum // 等到屏幕显示 BIOS START "esc crtl [" 如果我​​能做到以上,脚本的其余部分就变得相当容易了。再次感谢您的意见,我会根据上面的例子继续尝试。
    • 等等,您正试图通过 telnet 登录到远程计算机以捕获启动序列?!告诉我我错了……
    • 不,telnet 通过远程机器上的串行端口捕获开发板的输出。我在远程机器上有一个 telnet 会话以向开发板发送命令。所以想法是捕获第一个 telnet 会话的输出,弄清楚开发板的状态,通过第二个 telnet 窗口向开发板发送更多命令。
    【解决方案3】:

    我不会在这里对套接字做任何事情,因为您至少需要实现部分 telnet 规范。如果我记得,那个规格有点有趣。但是这里列出了一些 .NET telnet 实现:C# Telnet Library 您可能可以直接从 powershell 调整或使用,就像 Goyuix 在他的答案中使用套接字代码一样。

    【讨论】:

      【解决方案4】:

      我建议您使用 TeraTerm 这个免费软件。 您可以远程登录到您的机器,然后运行 ​​TTL 脚本。 它非常强大和可靠。我每天都在使用它来工作。 如果您有兴趣,可以进行更多搜索。 TTL脚本示例:

      i = 100
      do while i>0
          sendln 'un 1357'
          wait '>'
          sendln '.w 4 42800024 0000000a'
          wait '>'
          sendln '.w 4 42800014 00000004'
          wait 'in service'
          sendln 'info'
          wait'>'
          sendln 'start'
          wait '20'#13#10'>' '0'#13#10'>'
          if result!=2 then 
              break
          endif
          i = i - 1
      loop
      

      【讨论】:

        【解决方案5】:

        这是 PowerShell 中一个非常基本的 Telnet 客户端。它本质上只是 .net 框架的 TcpClient,带有一些额外的代码来拒绝任何 IAC 命令(即,在与服务器协商其能力时,它只是对所有请求说“我不会/不会这样做”,确保最可以使用基本的 NVT 实现)。

        此处维护的代码:https://gist.github.com/JohnLBevan/e28fbb6c0dfdd45a21e03c104999c212

        Function New-TelnetClient {
            [CmdletBinding()]
            Param (
                [Parameter()]
                [string]$ComputerName = '127.0.0.1'
                ,
                [Parameter()]
                [int]$PortNo = 23
                ,
                [Parameter()]
                [System.Text.Encoding]$Encoding = [System.Text.Encoding]::ASCII
                ,
                [Parameter()]
                [int]$BufferSize = 1024
            )
            [System.Net.Sockets.TcpClient]$telnet = New-Object 'System.Net.Sockets.TcpClient'
            try {
                $telnet.PSTypeNames.Add('ClearChannel.Net.Sockets.TelnetClient')
                $telnet | Add-Member -MemberType 'NoteProperty' -Name 'Encoding' -Value ($Encoding)
                $telnet | Add-Member -MemberType 'NoteProperty' -Name 'EndOfCommand' -Value ([System.Environment]::NewLine)
                $telnet | Add-Member -MemberType 'NoteProperty' -Name 'BufferSize' -Value ($BufferSize)
                $telnet.Connect($ComputerName, $PortNo)
                $telnet | Add-Member -MemberType 'NoteProperty' -Name 'Writer'      -Value (New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList ($telnet.GetStream()))
                $telnet.Writer.AutoFlush = $true
                $telnet | Add-Member -MemberType 'ScriptMethod' -Name 'SendCommand' -Value ({
                    Param([string]$CommandText)
                    #$this.Writer.WriteLine($CommandText + $this.EndOfCommand) #writeline should stick the line endings in place anyway, but just to be sure, added this
                    $this.Writer.WriteLine($CommandText)
                    (New-Object -TypeName 'PSObject' -Property @{Direction='Input'; Value=$CommandText; When=((Get-Date).ToUniversalTime())})
                })
                $telnet | Add-Member -MemberType 'ScriptMethod' -Name 'HandleIac' -Value ({
                    if ($this.Available) {
                        [int]$byte = $this.GetStream().ReadByte()
                        [byte]$defaultResponse = 254 # for most IAC requests, we'll respond with don't
                        switch ($byte) {
                            -1 { # end of stream (shouldn't happen, but handled in case)
                                Write-Warning 'Unexpected end of stream whilst processing IAC'
                                return
                            }
                            255 { # Escaped IAC character
                                Write-Debug 'IAC Escaped'
                                return $byte
                            }
                            253 { #if we get a DO, change default response to WON'T instead of DON'T
                                $defaultResponse = 252
                                # do not break; continue to next case statement
                            }
                            {(251, 252, 253, 254) -contains $_} { # Will, Won't, Do, Don't
                                $byte = $this.GetStream().ReadByte() # this is the option we need to respond to; currently we just deny all options to get a raw NVT
                                switch ($byte) {
                                    -1 {
                                        Write-Warning 'Unexpected end of stream whilst processing IAC'
                                    }
                                    # if we want to handle specific IAC codes we can add support here
                                    default {
                                        $this.GetStream().WriteByte(255)              # IAC
                                        $this.GetStream().WriteByte($defaultResponse) # Don't/Won't
                                        $this.GetStream().WriteByte($byte)            # whatever you told me
                                    }
                                }
                                return
                            }
                            default {
                                Write-Warning "$byte is not a control character, but was received after an IAC character"
                            }
        
                        }
                    }
                })
                $telnet | Add-Member -MemberType 'ScriptMethod' -Name 'GetBytes'   -Value ({
                    Start-Sleep -Milliseconds 500 #added to get correct output; otherwise we seem to fly past the handshake :/
                    while ($this.Available -gt 0) {
                        [int]$byte = $this.GetStream().ReadByte() #held as int to allow -1 status code for end of stream
                        switch ($byte) {
                            -1 { # end of stream
                                return
                            }
                            255 { #IAC control character received
                                Write-Verbose 'IAC Command Received'
                                $this.HandleIac()
                                break
                            }
                            {($_ -ge 0) -and ($_ -lt 255)} { # normal data (not sure if it's worth returning the 0s... haven't seen anything to suggest that they're special though, as -1 is the eof.
                                [byte]$byte
                                Write-Debug "found $byte"
                                break
                            }
                            default {
                                throw "Received value $_ when expecting a byte (0-255)"
                            }
        
                        }
                    }
                })
                $telnet | Add-Member -MemberType 'ScriptMethod' -Name 'GetOutput'   -Value ({
                    [byte[]]$bytes = $this.GetBytes()
                    if (($null -ne $bytes) -and ($bytes.Length -gt 0)) {
                        Write-Verbose "raw output is $(($bytes | %{"$_"}) -join ', ')"
                        $this.Encoding.GetString($bytes)
                    } else {
                        write-verbose 'no output this time'
                    }
                })
                $telnet | Add-Member -MemberType 'ScriptMethod' -Name 'ReceiveThenSendCommands' -Value ({
                    Param([string[]]$Commands)
                    foreach ($commandText in $commands) {
                        $this.GetOutput()
                        $this.SendCommand($commandText)
                    }
                    $this.GetOutput()
                })
                if ($telnet.Connected) {
                    $telnet
                } else {
                    throw 'Failed to connect'
                }
            } catch {
                Remove-TelnetClient -TelnetClient $telnet
            }
        }
        
        Function Remove-TelnetClient {
            [CmdletBinding()]
            Param (
                [Parameter(Mandatory = $true)]
                [AllowNull()]
                [PSObject]$TelnetClient
            )
            if ($null -ne $TelnetClient) {
                if ($TelnetClient.Connected) {
                    $TelnetClient.GetStream().Close()
                    $TelnetClient.Close()
                }
                if($TelnetClient.Dispose) {
                    $TelnetClient.Dispose()
                }
            }
        }
        

        这是一个如何在普通脚本会话中使用它的示例:

        # Example Usage
        
        $telnet = New-TelnetClient -ComputerName 'TelnetServerNameFqdnOrIp'
        try {
            $telnet.ReceiveThenSendCommands(@(
                'myTelnetUsername'
                'myPlaintextTelnetPassword'
                'DIR' #or whatever command I want to run
            )) | Format-List # show the output in a readable format, including when it contains new line characters
        } finally {
            Remove-TelnetClient $telnet
        }
        

        但是如果你想在交互模式下运行它,只需在你想向服务器推送命令时调用 SendCommand,然后调用 GetOutput 来查看结果;例如您可以一次运行低于一行的每一行。

        $telnet = New-TelnetClient -ComputerName 'TelnetServerNameFqdnOrIp'
        $telnet.GetOutput() # will probably display a welcome message & logon prompt
        $telnet.SendCommand('myUsername') # send your username
        $telnet.GetOutput() # will probably echo back your username then prompt for a password
        $telnet.SendCommand('myPassword') # send your password
        $telnet.GetOutput() # unlikely to output anything for a valid password; will give an error for an invalid one
        $telnet.SendCommand('DIR') # send whatever commands you want to run
        $telnet.GetOutput() # get the output of those commands
        Remove-TelnetClient $telnet # once you're done, cleanly closes down the client
        

        【讨论】:

          【解决方案6】:

          我创建了一个 powershell 脚本,用于从单个主机远程登录多个商店,并且可以选择捕获或不捕获 tracert 和 ping 命令

          远程登录多个主机并捕获tracert和ping命令的命令

              #Mutlple Telneting guide
          
              #Created by : Mohit
          
              #How to use ? 
              #Step 1 : Add mutiple IPs in DestinationIP.csv 
              #Step 2 : Run Batch file TelnetMultipleHost.bat
          
              ####################################################################################################################
              $DestinationIP= Get-Content .\DestinationIP.csv
              $ipV4 = (Test-Connection -ComputerName (hostname) -Count 1).IPV4Address.IPAddressToString
              ####################################################################################################################
          
              write-host "-------------------Welcome to Multiple Telnet Host Panel-------------------------"
              write-host ""
              write-host ""
              write-host "IMPORTANT: Make sure you are running this tool from source IP which in this case is " $ipV4
              write-host ""
              $Ports = Read-Host -Prompt "Enter Destination Port No.(# for multple ports just seperate ports with ,)"
              write-host ""
              write-host "Port No. you entered:" $Ports
              write-host ""
              write-host "Select Option"
              write-host ""
              write-host "Type 1 for telnet Host WITH trace logs"
              write-host "Type 2 for telnet Host WITHOUT trace logs"
              write-host ""
              $option =Read-Host -Prompt "Type here"
              write-host ""
              Start-Transcript -Path .\TraceLogs_$ipV4.txt
          
          
              switch($option)
              {
              #Type 1 for telnet Host WITH trace logs
              1{
                foreach ($Destination in $DestinationIP) 
                {
                      foreach ($Port in $Ports) {
          
                              # Create a Net.Sockets.TcpClient object to use for      # checking for open TCP ports.
                              $Socket = New-Object Net.Sockets.TcpClient
                              # Suppress error messages
                              $ErrorActionPreference = 'SilentlyContinue'
                              # Try to connect
                              $Socket.Connect($Destination, $Port)
                              # Make error messages visible again
                              $ErrorActionPreference = 'Continue'
                              # Determine if we are connected.
                              if ($Socket.Connected) {
                                  "${Destination}: Port $Port is open"
                                  $Socket.Close()
                              }
                              else {
                                  "${Destination}: Port $Port is closed or filtered"
          
                                  if (test-connection $Destination -count 1 -quiet) {
                                               write-host $Destination "Ping succeeded." -foreground green
          
                                      } else {
                                               write-host $Destination "Ping failed." -foreground red
                                          }
          
                                  Test-NetConnection $Destination -TraceRoute
          
                              }
                              # Apparently resetting the variable between iterations is necessary.
                              $Socket = $null
                     }
                  }
              }
              # Type 2 for telnet Host WITHOUT trace logs
              2{
              foreach ($Destination in $DestinationIP) {
                  foreach ($Port in $Ports) {
          
                      # Create a Net.Sockets.TcpClient object to use for
                      # checking for open TCP ports.
                      $Socket = New-Object Net.Sockets.TcpClient
          
                      # Suppress error messages
                      $ErrorActionPreference = 'SilentlyContinue'
          
                      # Try to connect
                      $Socket.Connect($Destination, $Port)
          
                      # Make error messages visible again
                      $ErrorActionPreference = 'Continue'
          
                      # Determine if we are connected.
                      if ($Socket.Connected) {
                          "${Destination}: Port $Port is open"
                          $Socket.Close()
                      }
                      else {
                          "${Destination}: Port $Port is closed or filtered"
          
                           }
                      # Apparently resetting the variable between iterations is necessary.
                      $Socket = $null
                  }
               } 
              }
              }
              Stop-Transcript
          

          请注意:TelnetMultipleHost.bat 这个批处理是用来运行powershell命令的

          确保我们在同一目录中有bat、ps1文件

          批处理文件代码:

          @echo 关闭 Powershell.exe -executionpolicy remotesigned -File .\TelnetMultipleHost.ps1 暂停

          【讨论】:

            猜你喜欢
            • 2021-08-20
            • 2014-08-13
            • 2023-04-01
            • 2017-12-10
            • 2012-05-05
            • 2016-08-01
            • 2010-12-02
            • 1970-01-01
            • 2010-11-01
            相关资源
            最近更新 更多