支持并发的TCP全连接端口扫描器
0x00 扫描器并发的实现
使用sync.WaitGroup与channel配 合实现的并发方式,代码片断如下所示
func RunTask(tasks []map[string]int) {
wg := &sync.WaitGroup{}
// 创建一个buffer为vars.threadNum * 2的channel
taskChan := make(chan map[string]int, vars.ThreadNum*2)
// 创建vars.ThreadNum个协程
for i := 0; i < vars.ThreadNum; i++ {
go Scan(taskChan, wg)
}
// 生产者,不断地往taskChan channel发送数据,直接channel阻塞
for _, task := range tasks {
wg.Add(1)
taskChan <- task
}
close(taskChan)
wg.Wait()
}
func Scan(taskChan chan map[string]int, wg *sync.WaitGroup) {
// 每个协程都从channel中读取数据后开始扫描并入库
for task := range taskChan {
for ip, port := range task {
err := SaveResult(Connect(ip, port))
_ = err
wg.Done()
}
}
}
RunTask函数不断地将扫描任务发送到taskChan中,Scan会不断 地消费taskChan中的数据。
0x01生成扫描任务列表
生成扫描任务列表:
首先解析出需要扫描的IP与端口的切片, 然后将需要扫描的IP与端口列表放入一个[]map[string]int中, map的key为IP地址,value为端口,[]map[string]int表示所有需 要扫描的IP与端口对的切片。
具体代码实现:
func GenerateTask(ipList []net.IP, ports []int) ([]map[string]int, int) {
tasks := make([]map[string]int, 0)
for _, ip := range ipList {
for _, port := range ports {
ipPort := map[string]int{ip.String(): port}
tasks = append(tasks, ipPort)
}
}
return tasks, len(tasks)
}
0x02 输出结果
展示扫描结果,直接通过sync.map的Range方法枚举出所有结 果并展示出来,代码如下所示:
func PrintResult() {
vars.Result.Range(func(key, value interface{}) bool {
fmt.Printf("ip:%v\n", key)
fmt.Printf("ports: %v\n", value)
fmt.Println(strings.Repeat("-", 100))
return true
})
}
运行结果:
go run main.go 45.33.32.156,114.114.114.114 22,23,53,80-100
ip:114.114.114.114
ports: [53]
----------------------------------------------------------------------------------------------------
ip:45.33.32.156
ports: [22 80]
----------------------------------------------------------------------------------------------------
也是比全连接加上了并发