#===========autotelnet.sh============== #!/bin/bash
tmptty=`tty` #取得当前的tty值 tmptty=`basename $tmptty` #去掉tty的绝对路径 tmpname=`whoami` #取得当前执行程序的用户名 ip="10.22.33.44" #目标主机地址 inp1="ABC^M" #主机的用户名,注意^M必须在UNIX下重装用以下方法输入才能用!! #方法为按住ctrl键按v键,不放ctrl键,再按shift键和m键,完成后全部放开 inp2="ABC^M" #主机的密码,注意必须有^M inp3="ls^M" #其他进入后的命令,可无或用ls之类的命令代替,注意必须有^M inp4="pwd^M" #命令4,同上 #--------------------------
inputfile=in #导入文件管道用的,不要改,这个值没有任何关系 outputfile=out.log #最终导出的文件 rm -fr $inputfile rm -fr $outputfile mknod $inputfile p touch $outputfile
#file description 7 for out and 8 for in 使用7作为输入管道,8作为输入 exec 7<>$outputfile exec 8<>$inputfile
telnet $ip <&8 >&7 &
sleep 2; echo $inp1 >> $inputfile #看得懂吧 sleep 2; echo $inp2 >> $inputfile sleep 2; echo $inp3 >> $inputfile #如果没有其他命令,这行和下一行可以去掉 sleep 2; echo $inp4 >> $inputfile
tail -f $outputfile & 强制在屏幕上显示任何输入输出
while true #正常情况下已经进入目标主机了,可以输入任何命令,所有的一切输入输出都会被记录 do read str if [[ $str = "quit" || $str = "exit" ]] then echo $str >> $inputfile exit else echo $str >> $inputfile fi done
#退出时自动杀掉相关进程 ps -ef | grep telnet | grep -v grep | grep -v telnetd | grep $tmptty | grep $tmpname | awk '{print " kill -9", $2}' | sh ps -ef | grep tail | grep -v grep | grep -v telnetd | grep $tmptty | grep $tmpname | awk '{print " kill -9", $2}' | sh
|
这段代码实现的功能是在UNIX系统上执行这个脚本,自动登录到脚本中变量<ip>声明使用的主机上,并用脚本中<inp1>变量和<inp2>的值分别作为用户名和密码进行身份验证。然后,用户可以在控制台上输入任何命令,这些命令会被发送到远端主机执行。因此,我猜测这个脚本的作用和SecureCRT等TELNET工具提供的自动登录的功能是一样的。
这个例子的主要原理是这样的:用后台方式启动一个telnet进程。将这个进程的输入重定向到一个管道文件in,向这个管道文件追加要执行的指令,也就是将指令传送到telnet进程中执行;同时,将这个进程的输出重定向至一个日志文件out.log中,tail –f这个日志文件,就是实时刷新telnet的输出。
这里有几个细节问题需要说明一下:
1、 向管道文件写入要执行的命令时,必须有一个结束标志,告诉telnet启动的shell进程这是一个完整的命令,可以执行了。这个结束标志就是” ^M”。这个东西的输入还很复杂。按照作者的说明,要在UNIX系统上,按住Ctrl键后按v键,松开v键保持Ctrl键不放,然后按下Shift键后再按M键,然后同时放开Ctrl Shilf和M三个键。在实际使用中发现,不需要Shift键,放开v键之后直接按m键即可;
2、 在重定向telnet后台进程的输入时,因为要控制输入内容的速度(要等到出现login以后才能输入用户名),所以不能采用文件中直接保存用户名、密码及所有要执行指令的方式,要求in文件是空的。
3、 在重定向telnet后台进程的输入、输出时,必须使用文件描述符。具体原因还不清楚,但用文件名称进行重定向就不行;
4、 在用户输入quit或exit后,需要退出后台telnet进程以及tail –f进程。脚本中采用kill进程的方式实现这一目标。因为kiill进程时需要一些参数,因此在脚本的开始处记录了tty的类型等信息;
对于脚本中具体指令的解释,参加如下列表中的注释
#!/bin/bash
tmptty=`tty` #取得当前的tty值 tmptty=`basename $tmptty` #去掉tty的绝对路径 tmpname=`whoami` #取得当前执行程序的用户名 #以上信息在最后kill进程时作为筛选条件使用
ip="10.22.33.44" #目标主机地址 inp1="ABC^M" #主机的用户名。注意^M必须在UNIX下重装用以下方法输入才能用!! #方法为按住ctrl键按v键,不放ctrl键,再按shift键和m键,完成后全部放开 #经过实际使用,不比按shilf键也可以 inp2="ABC^M" #主机的密码,注意必须有^M inp3="ls^M" #其他进入后的命令,可无或用ls之类的命令代替,注意必须有^M inp4="pwd^M" #命令4,同上 #--------------------------
inputfile=in #将命令导入后台telnet进程用的管道文件名称 outputfile=out.log #包含telnet后台进程输入的文件名称 rm -fr $inputfile rm -fr $outputfile mknod $inputfile p #建立管道文件 touch $outputfile #建立输出文件
exec 7<>$outputfile #将文件描述符7分配给outputfile exec 8<>$inputfile #将文件描述符8分配给inputfile
telnet $ip <&8 >&7 & #后台运行telent,同时重定向输入、输出
sleep 2; echo $inp1 >> $inputfile #2秒后输入用户名 sleep 2; echo $inp2 >> $inputfile #2秒后输入密码 sleep 2; echo $inp3 >> $inputfile #2秒后输入命令inp3 sleep 2; echo $inp4 >> $inputfile #2秒后输入命令inp3 #这里面inp3和inp4只是一个说明,对自动登录实际上没有什么作用
tail -f $outputfile & #强制在屏幕上显示任何输入输出
while true #正常情况下已经进入目标主机了,可以输入任何命令,所有的一切输入输出都会被记录 do read str if [[ $str = "quit" || $str = "exit" ]] then echo $str >> $inputfile exit #这里的exit实际上是从循环中退出 else echo $str >> $inputfile fi done
#退出时自动杀掉相关进程 ps -ef | grep telnet | grep -v grep | grep -v telnetd | grep $tmptty | grep $tmpname | awk '{print " kill -9", $2}' | sh ps -ef | grep tail | grep -v grep | grep -v telnetd | grep $tmptty | grep $tmpname | awk '{print " kill -9", $2}' | sh
|
明白了这个脚本的原理,我们可以做一个试验,用另外一种更加明显的方式来做输入输出的重定向。
1、新建1个普通文件 touch out.log
2、新建一个管道文件: mknod commands p
3、为日志文件分配文件描述符7 : exec 7<>out.log
4、为管道文件分配文件描述符8: exec 8<>commands
5、后台方式启动telnet: telnet 136.30.75.102 <&8 >&7 &
6、实时刷新输出信息: tail –f out.log
7、另外启动一个终端,输入信息: echo “root^M” >>commands
echo “password^M”>>commands
echo “pwd^M” >> commands
….
8、观察tail –f的输出,可以看到输入命令的执行结果。
在这个脚本的基础上,可以实现自动telnet到一个主机上并执行一些列command的功能。脚本如下:
#!/bin/bash
tmptty=`tty`
tmptty=`basename $tmptty`
tmpname=`whoami`
ip="136.39.75.102"
inp1="root^M"
inp2="Easy2get^M"
#####################################################################
#you may add as many commands as you want
#NOTE:every command should end with ^M, which is input under UNIX
# 方法为按住ctrl键按v键,不放ctrl键,再按shift键和m键,完成后全部放开
#####################################################################
inp3="ls^M"
inp4="echo 'hello,TeMIP users' >> hello.txt^M"
#--------------------------
inputfile=in
outputfile=out.log
rm -fr $inputfile
rm -fr $outputfile
mknod $inputfile p
touch $outputfile
exec 7<>$outputfile
exec 8<>$inputfile
telnet $ip <&8 >&7 &
sleep 2; echo $inp1 >> $inputfile
sleep 2; echo $inp2 >> $inputfile
echo "executing command $inp3"
sleep 2; echo $inp3 >> $inputfile
echo "executing $inp4"
sleep 2; echo $inp4 >> $inputfile
echo "exiting"
sleep 2; echo "exit^M" >> $inputfile
rm $inputfile
rm $outputfile
|
附录:Shell中的输入/输出
(摘自HP-UX 参考手册 用户命令 ksh)
命令执行之前,其输入和输出可以使用专用表示法重定向由Shell 解释。下列内容可以出现在简单命令内的任何位置,或在命令之前之后,并且不传递给调用命令。命令和参数替换发生在使用word 或digit 之前,除非如下所示。文件名生成仅出现在模式匹配单一文件且不执行空白解释时。
l <word 使用文件word 作为标准输入(文件描述符0 )。
l >word 使用文件word 作为标准输出(文件描述符1 )。如果不存在该文件,将另行创建。如果文件存在,并且使用noclobber 选项,则发生错误;否则文件被截断为零长度。
l >|word 与> 相同,区别在于覆盖noclobber 选项。
l >>word 使用文件word 作为标准输出。如果文件存在,追加输出到其中(通过首先搜索文件末尾);否则,另行创建文件。
l <>word 打开文件word 作为标准输入以进行读取和写入。如果不存在该文件,将另行创建。
l <<[ - ]word 读取Shell 输入直至出现行与word 匹配,或者抵达文件末尾。在word 上没有执行参数替换、命令替换或文件名生成。得到的文档称为本文档,作为标准输入。如果引用word 的任意字符,不对文档字符进行解释。否则,发生参数和命令替换,忽略\newline,必须使用\ 引用字符\ 、$ 、‘ 和word 的第一个字符。如果- 追加到<< ,则从word 和文档去掉所有前导制表符。
l <&digit 从文件描述符复制标准输入digit (请参阅dup(2) )。
l >&digit 标准输出复制到文件描述符digit (请参阅dup(2) )。
l <&- 标准输入关闭。
l >&- 标准输出关闭。
l <&p 来自联合进程的输入移动到标准输入。
l >&p 到联合进程去的输出移动到标准输出。
如果上述一项有数字前导,文件描述符号引用由该数字指定(取代缺省的0 或1 )。例如:
... 2>&1
意味着文件描述符2 打开,作为文件描述符1 的副本用于写入。重定向顺序很重要,因为Shell 根据当前打开文件在计算时与指定文件描述符的关联计算重定向引用文件描述符。例如:
... 1>fname 2>&1
首先分配文件描述符1(标准输出)给文件fname ,然后分配文件描述符2(标准错误)给分配给文件描述符1的文件,也就是fname 。另一方面,如果重定向顺序反转如下:
... 2>&1 1>fname
文件描述符2 分配给当前标准输出,(用户终端,除非继承了不同的分配)。此时文件描述符1 重新分配给文件fname ,不更改文件描述符2 的分配。
co-process 的输入和输出可移动到多个文件描述符,允许其他命令使用上述重定向运算符向其中写入和读取。如果当前co-process 输入移动到多个文件描述符,另一个co-process 开始。
如果命令后跟随& 并且作业控制非活动状态,命令的缺省标准输入为空文件/dev/null 。否则,执行命令的环境包括调用Shell 的文件描述符,通过输入/输出规格修改
http://blog.csdn.net/muyuqing/archive/2007/04/27/1586824.aspx