我突然有一个用 Bash 来编写 TCP 端口扫描器的想法。Bash 支持可读写的特殊文件 /dev/tcp/host/port ,往这个文件写内容可以让 bash 打开一个 TCP 连接到 host:port ,如果写文件成功则表示此端口是打开的,否则说明该端口没有打开。
因此我们先简单的写一个测试脚本:
1 | for port in {1..65535}; do |
2 | echo >/dev/tcp/google.com/$port && |
3 | echo "port $port is open" || |
4 | echo "port $port is closed" |
该脚本将扫描 google.com 服务器端口,从 1 到 65535。当然,如果端口没打开的话是没法工作的,bash 花了 2 分钟时间意识到这点。
为了解决这个问题我们需要一些类似 alarm(2) 的方法来中断 bash,而 bash 没有内置的 alarm 函数,因此我们用 Perl 语言写了一个:
04 | $SIG {ALRM} = sub { die }; |
这个 alarm 函数需要两个参数:alarm 调用的秒数和要执行的代码,如果执行的代码没有在指定的时间内执行完毕则该函数调用失败。
有了这个 alarm 函数,我们就可以修改上面的代码如下:
1 | for port in {1..65535}; do |
2 | alarm 1 " echo >/dev/tcp/google.com/$port && |
3 | echo \ "port $port is open\"" || |
4 | echo "port $port is closed" |
这个终于可以运行了,当扫描到某个端口是关闭的, bash 将在 1 秒后执行下一个端口的扫描。
然后我们将这些代码封装到一个 scan 函数中:
02 | if [[ -z $1 || -z $2 ]]; then |
03 | echo "Usage: $0 <host> <port, ports, or port-range>" |
11 | IFS=- read start end <<< "$2" |
12 | for ((port=start; port <= end; port++)); do |
17 | IFS=, read -ra ports <<< "$2" |
25 | for port in "${ports[@]}" ; do |
26 | alarm 1 " echo >/dev/tcp/$host/$port && |
27 | echo \ "port $port is open\"" || |
28 | echo "port $port is closed" |
这样就可以在 shell 中使用 scan 函数,需要的参数包括要扫描的主机地址、端口列表(可以时端口组合和端口范围,或者是某个特定端口)
下面是扫描 google.com 服务器的 78 - 82 端口:
如果你想测试 UDP 端口,只需要将前面提及的 /dev/tcp 改为 /dev/udp/
原文:http://www.oschina.net/question/12_67215