TCP-Scanner
连接网络设备时,一般都会在网络设备端选取 0-65535 之间的一个端口进行连接,端口扫描是指:检查网络设备上 0-65535 号端口哪些端口是开启状态。
如果黑客扫描到某网络设备的 80 端口是开启状态,那么很有可能是 web 服务,此时黑客会使用一些手段攻击 web 服务器,这样一来,端口扫描会增加服务器的安全负担,因此一般网络都会阻止端口扫描。
当然端口扫描对于防御性渗透测试也非常有用。我们可以扫描自己的系统来确定哪些服务暴露给了网络,这样我们就能有针对性的对系统的配置进行检查。
实现原理
该项目中,我们通过 Go 去实现一个 TCP 端口的扫描。
怎么进行扫描呢?思路就是根据目标地址对每个端口进行 TCP 连接,如果连接成功的话,就说明这个端口是开启的。
func Dial(network, address string)(Conn, error)
我们主要通过 net.Dial
方法来去建立 TCP 连接。
并发实现
显然,如果直接对所有端口循环地进行连接建立,这样的处理是非常慢的,所以比较好的实现是我们对每个端口都开启协程去进行实现。
如果我们对每个端口都进行并发请求,可能使服务资源耗尽,而且大量的并发输出可能导致结果混乱。
因此,我们可以通过并发池来去完成并发的操作,分配额定的 worker
去执行任务,这样能够避免同时的大量并发操作。
思路是这样的:
- 1、定义
woker
函数,分别用两个channel
表示要处理的任务和结果。 - 2、在
main
中,对要处理的任务进行遍历,然后通过worker
函数去进行处理。 - 3、最后通过
close
关闭channel
,然后等待worker
函数执行完毕。
完整代码
package main
import (
"fmt"
"net"
"sort"
"time"
)
func worker(ports chan int, res chan int) {
for p := range ports {
address := fmt.Sprintf("scanme.nmap.org:%d", p)
conn, err := net.Dial("tcp", address)
if err != nil {
res <- 0
continue
}
conn.Close()
res <- p
}
}
var openports []int
func main() {
start := time.Now()
ports := make(chan int, 100)
results := make(chan int)
for i := 0; i < cap(ports); i++ {
go worker(ports, results)
}
go func() {
for i := 1; i < 1024; i++ {
ports <- i
}
}()
for i := 1; i < 1024; i++ {
port := <-results
if port != 0 {
openports = append(openports, port)
}
}
close(ports)
close(results)
sort.Ints(openports)
for _, port := range openports {
fmt.Printf("%d open\n", port)
}
elapsed := time.Since(start)
fmt.Printf("Took %s", elapsed)
}