tcp端口扫描器
Created At :
Views 👀 :
并发tcp全端口扫描
Go语言是原生支持并发的语言,它的并发是通过协程实现的。这里介绍了两个版本的支持并发的TCP全连接端口扫描器
1 生成扫描任务列表:首先解析出需要扫描的IP与端口的切片,然后将需要扫描的IP与端口列表放入一个[]map[string]int中,map的key为IP地址,value为端口,
[]map[string]int表示所有需要扫描的IP与端口对的切片。
1 2 3 4 5 6 7 8 9 10 11 12
| 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) }
|
2.分割扫描任务:根据并发数将需要扫描的[]map[string]int切片分割成组,以便按组进行并发扫描。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| func AssigningTasks(tasks []map[string]int) { scanBatch := len(tasks) / vars.ThreadNum
for i := 0; i < scanBatch; i++ { curTask := tasks[vars.ThreadNum*i : vars.ThreadNum*(i+1)] RunTask(curTask) }
if len(tasks)%vars.ThreadNum > 0 { lastTasks := tasks[vars.ThreadNum*scanBatch:] RunTask(lastTasks) } }
|
3 按组执行扫描任务:分别将每组扫描任务传入具体的扫描任务中,扫描任务函数利用sync.WaitGroup实现并发扫描,在扫描的过程中将结果保存到一个并发安全的map中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| func RunTask(tasks []map[string]int) { var wg sync.WaitGroup wg.Add(len(tasks)) for _, task := range tasks { for ip, port := range task { go func(ip string, port int) { err := SaveResult(Connect(ip, port)) _ = err wg.Done() }(ip, port) } } wg.Wait() }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func SaveResult(ip string, port int, err error) error { if err != nil { return err }
v, ok := vars.Result.Load(ip) if ok { ports, ok1 := v.([]int) if ok1 { ports = append(ports, port) vars.Result.Store(ip, ports) } } else { ports := make([]int, 0) ports = append(ports, port) vars.Result.Store(ip, ports) } return err }
|
4 展示扫描结果:所有扫描任务完成后,输出保存在并发安全map中的扫描结果。
1 2 3 4 5 6 7 8 9 10
| 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 }) }
|
以上4步全部完成后,在main函数中分别调用任务生成、任务分配与结果展示的函数即可,代码片断如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| func main() { if len(os.Args) == 3 { ipList := os.Args[1] portList := os.Args[2] ips, err := util.GetIpList(ipList) ports, err := util.GetPorts(portList) _ = err
task, _ := scanner.GenerateTask(ips, ports) scanner.AssigningTasks(task) scanner.PrintResult()
} else { fmt.Printf("%v iplist port\n", os.Args[0]) } }
func init() { runtime.GOMAXPROCS(runtime.NumCPU()) }
|
接下来用新实现的并发端口扫描器tcp-connect-scanner1与Nmap分别执行一遍刚才的任务
这个扫描器虽然已经实现了并发扫描,但对协程的控制不够精细,每组扫描任务都会瞬间启动大量的协程,然后逐渐关闭,而不是一个平滑的过程。这种方法可能会瞬间将服务器的CPU占满,为了解决此问题,在tcp-connect-scanner2中使用sync.WaitGroup与channel配合实现了新的并发方式,代码片断如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| func RunTask(tasks []map[string]int) { wg := &sync.WaitGroup{}
taskChan := make(chan map[string]int, vars.ThreadNum*2)
for i := 0; i < vars.ThreadNum; i++ { go Scan(taskChan, wg) }
for _, task := range tasks { wg.Add(1) taskChan <- task }
close(taskChan) wg.Wait() }
func Scan(taskChan chan map[string]int, wg *sync.WaitGroup) { for task := range taskChan { for ip, port := range task { err := SaveResult(Connect(ip, port)) _ = err wg.Done() } } }
|
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com