使用PowerShell自动化Telnet

9

我该如何编写PowerShell脚本来自动执行以下命令?

  • telnet到一台机器,
  • 执行几个命令,
  • 分析telnet窗口中的输出结果,
  • 根据输出结果发送更多的命令。

服务器端是什么?Unix?OSX?Windows+Telnetd?PowerShell v1或v2?您能在远程端安装东西吗?客户端呢? - x0n
PowerShell v2. 我正在一台Windows机器上编写PowerShell脚本。远程端可以是Linux或Windows。我无法在远程站点安装新的东西。 - Guru Je
7个回答

5

好的,这不是最优雅的解决方案,但它确实依赖于强烈的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;

那应该能做到你所要求的...

注意:这不是我的解决方案,而是从这篇博客文章中找到的,我已经使用过一两次。


3

不要尝试自动化一个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 <ip> portnum // 等待屏幕显示BIOS START <发送以下序列> "esc crtl [" 如果我能做到以上操作,剩下的脚本就变得很容易了。再次感谢您的帮助,我会根据上面的示例继续尝试。 - Guru Je
等等,你正在尝试telnet到远程机器来捕获启动序列?告诉我我是错的... - x0n
不,Telnet通过远程机器上的串口捕获开发板的输出。我在远程机器上有另一个Telnet会话,用于向开发板发送命令。因此,这个想法是从第一个Telnet会话中捕获输出,找出开发板的状态,通过第二个Telnet窗口发送更多命令给开发板。 - Guru Je

2

在这里,我不会使用套接字,因为你需要实现至少部分telnet规范。如果我没记错的话,该规范有点有趣。但是这里列出了一些.NET telnet实现:C# Telnet Library,你可以适应或直接从powershell中使用,就像Goyuix在他的答案中使用套接字代码一样。


1
我建议您使用这个免费软件TeraTerm。您可以通过telnet连接到您的机器,然后运行一个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

1
这是一个基本的PowerShell Telnet客户端。它实际上只是 .net Framework 的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

0
我已经创建了一个PowerShell脚本,可以从单个主机telnet多个商店,并具有捕获或不捕获tracert和ping命令的选项。
命令用于telnet多个主机并捕获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 off Powershell.exe -executionpolicy remotesigned -File .\TelnetMultipleHost.ps1 pause

0

我使用以下脚本来telnet到多个IP地址:

$server_list = @('1.1.1.1:443', '10.100.8.22:3389', '10.100.8.21:22')
Foreach ($t in $server_list)
{
  $source = $t.Split(':')[0]
  $port = $t.Split(':')[1]
  Write-Host "Connecting to $source on port $port" | Out-File 'output.txt' -Append
  try
  {
    $socket = New-Object System.Net.Sockets.TcpClient($source, $port)
  }
  catch [Exception]
  {
    Write-Host $_.Exception.GetType().FullName | Out-File 'output.txt' -Append
    Write-Host $_.Exception.Message | Out-File 'output.txt' -Append
  }
  Write-Host "Connected`n" | Out-File 'output.txt' -Append
}


当你连接到IP的脚本时,它会显示你已连接。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接