Golang如何开发一个端口扫描工具 通过并发提高扫描效率

如何用go语言实现高效的端口扫描工具?1.使用go的net包中的dialtimeout函数实现基本端口扫描逻辑,尝试连接目标端口并根据响应判断开放状态;2.通过goroutine实现并发扫描,显著提高效率,并利用sync.waitgroup确保所有任务完成后再退出程序;3.加入错误处理机制,区分超时和其他网络错误,提升程序健壮性;4.使用带缓冲的channel作为信号量限制并发数量,防止资源耗尽;5.引入结果channel将扫描信息输出到文件或数据库,便于后续分析;6.通过命令行参数支持灵活的端口范围或列表输入方式,增强工具实用性。

Golang如何开发一个端口扫描工具 通过并发提高扫描效率

端口扫描,简单来说,就是探测目标主机开放了哪些端口,从而了解它运行着哪些服务。用go语言开发端口扫描工具,可以充分利用其并发特性,显著提高扫描效率。

Golang如何开发一个端口扫描工具 通过并发提高扫描效率

并发扫描,错误处理,结果展示。

Golang如何开发一个端口扫描工具 通过并发提高扫描效率

如何用Go语言实现基本的端口扫描?

最基本的端口扫描就是尝试连接目标主机的特定端口。如果连接成功,说明端口开放;连接失败,则端口关闭或被防火墙拦截。Go语言的net包提供了DialTimeout函数,可以方便地实现这个功能。

立即学习go语言免费学习笔记(深入)”;

package main  import (     "fmt"     "net"     "strconv"     "time" )  func scanPort(hostname string, port int) {     address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         // fmt.Printf("Port %d: Closedn", port) // 静默失败,只输出成功的         return     }     defer conn.Close()     fmt.Printf("Port %d: Openn", port) }  func main() {     hostname := "localhost" // 或者 "127.0.0.1"     for port := 1; port <= 100; port++ {         scanPort(hostname, port)     } }

这段代码会顺序扫描localhost的1到100端口。虽然简单,但效率很低。

Golang如何开发一个端口扫描工具 通过并发提高扫描效率

如何利用Go的并发特性提高扫描速度?

Go的goroutine和channel是实现并发的利器。我们可以为每个端口创建一个goroutine,并发地进行扫描。

package main  import (     "fmt"     "net"     "strconv"     "sync"     "time" )  func scanPort(hostname string, port int, wg *sync.WaitGroup) {     defer wg.Done()     address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         // fmt.Printf("Port %d: Closedn", port)         return     }     defer conn.Close()     fmt.Printf("Port %d: Openn", port) }  func main() {     hostname := "localhost"     var wg sync.WaitGroup      for port := 1; port <= 100; port++ {         wg.Add(1)         go scanPort(hostname, port, &wg)     }      wg.Wait() // 等待所有goroutine完成 }

现在,每个端口的扫描都在一个独立的goroutine中进行,大大提高了扫描速度。使用sync.WaitGroup来确保所有goroutine都完成后程序才退出。

如何处理扫描过程中的错误和超时?

在实际应用中,我们需要更完善的错误处理机制。例如,记录超时错误,或者区分不同的错误类型。

package main  import (     "fmt"     "net"     "strconv"     "sync"     "time" )  func scanPort(hostname string, port int, wg *sync.WaitGroup) {     defer wg.Done()     address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         // 区分超时和其他错误         if netErr, ok := err.(net.Error); ok && netErr.Timeout() {             // fmt.Printf("Port %d: Timeoutn", port)             return         }         // fmt.Printf("Port %d: Error - %sn", port, err)         return     }     defer conn.Close()     fmt.Printf("Port %d: Openn", port) }  func main() {     hostname := "scanme.nmap.org" // 使用nmap提供的测试主机     var wg sync.WaitGroup      for port := 1; port <= 100; port++ {         wg.Add(1)         go scanPort(hostname, port, &wg)     }      wg.Wait() }

这段代码加入了超时错误的判断,可以更准确地报告端口状态。

如何控制并发数量,避免资源耗尽?

虽然并发可以提高速度,但过多的goroutine可能会耗尽系统资源。我们需要限制并发数量。可以使用带缓冲的channel来实现:

package main  import (     "fmt"     "net"     "strconv"     "sync"     "time" )  func scanPort(hostname string, port int, wg *sync.WaitGroup, sem chan struct{}) {     defer wg.Done()     sem <- struct{}{} // 获取信号量     defer func() { <-sem }() // 释放信号量      address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         if netErr, ok := err.(net.Error); ok && netErr.Timeout() {             // fmt.Printf("Port %d: Timeoutn", port)             return         }         // fmt.Printf("Port %d: Error - %sn", port, err)         return     }     defer conn.Close()     fmt.Printf("Port %d: Openn", port) }  func main() {     hostname := "scanme.nmap.org"     var wg sync.WaitGroup     sem := make(chan struct{}, 20) // 限制并发数为20      for port := 1; port <= 100; port++ {         wg.Add(1)         go scanPort(hostname, port, &wg, sem)     }      wg.Wait() }

sem channel充当信号量,限制了同时运行的goroutine数量。

如何将扫描结果输出到文件或数据库?

可以将扫描结果写入文件或数据库,方便后续分析。以写入文件为例:

package main  import (     "fmt"     "net"     "os"     "strconv"     "sync"     "time" )  func scanPort(hostname string, port int, wg *sync.WaitGroup, sem chan struct{}, results chan string) {     defer wg.Done()     sem <- struct{}{}     defer func() { <-sem }()      address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         if netErr, ok := err.(net.Error); ok && netErr.Timeout() {             return         }         return     }     defer conn.Close()     results <- fmt.Sprintf("Port %d: Openn", port) }  func main() {     hostname := "scanme.nmap.org"     var wg sync.WaitGroup     sem := make(chan struct{}, 20)     results := make(chan string, 100) // 缓冲结果      for port := 1; port <= 100; port++ {         wg.Add(1)         go scanPort(hostname, port, &wg, sem, results)     }      go func() {         wg.Wait()         close(results) // 关闭channel,通知结果收集goroutine     }()      file, err := os.Create("scan_results.txt")     if err != nil {         fmt.Println("Error creating file:", err)         return     }     defer file.Close()      for result := range results {         _, err := file.WriteString(result)         if err != nil {             fmt.Println("Error writing to file:", err)             return         }     }      fmt.Println("Scan completed. Results saved to scan_results.txt") }

使用一个channel results 来传递扫描结果,另一个goroutine负责将结果写入文件。

如何支持扫描指定端口范围或列表?

可以修改main函数,使其接受命令行参数,指定要扫描的端口范围或列表。

package main  import (     "flag"     "fmt"     "net"     "os"     "strconv"     "strings"     "sync"     "time" )  func scanPort(hostname string, port int, wg *sync.WaitGroup, sem chan struct{}, results chan string) {     defer wg.Done()     sem <- struct{}{}     defer func() { <-sem }()      address := hostname + ":" + strconv.Itoa(port)     conn, err := net.DialTimeout("tcp", address, 3*time.Second)     if err != nil {         if netErr, ok := err.(net.Error); ok && netErr.Timeout() {             return         }         return     }     defer conn.Close()     results <- fmt.Sprintf("Port %d: Openn", port) }  func main() {     hostnamePtr := flag.String("host", "localhost", "Host to scan")     portsPtr := flag.String("ports", "1-100", "Ports to scan (e.g., 1-100, 80,443,8080)")     flag.Parse()      hostname := *hostnamePtr     ports := *portsPtr      var portList []int     if strings.Contains(ports, "-") {         parts := strings.Split(ports, "-")         start, err := strconv.Atoi(parts[0])         if err != nil {             fmt.Println("Invalid port range:", err)             return         }         end, err := strconv.Atoi(parts[1])         if err != nil {             fmt.Println("Invalid port range:", err)             return         }         for port := start; port <= end; port++ {             portList = append(portList, port)         }     } else {         portStrings := strings.Split(ports, ",")         for _, portStr := range portStrings {             port, err := strconv.Atoi(strings.TrimSpace(portStr))             if err != nil {                 fmt.Println("Invalid port:", err)                 return             }             portList = append(portList, port)         }     }      var wg sync.WaitGroup     sem := make(chan struct{}, 20)     results := make(chan string, len(portList))      for _, port := range portList {         wg.Add(1)         go scanPort(hostname, port, &wg, sem, results)     }      go func() {         wg.Wait()         close(results)     }()      file, err := os.Create("scan_results.txt")     if err != nil {         fmt.Println("Error creating file:", err)         return     }     defer file.Close()      for result := range results {         _, err := file.WriteString(result)         if err != nil {             fmt.Println("Error writing to file:", err)             return         }     }      fmt.Println("Scan completed. Results saved to scan_results.txt") }

现在可以通过命令行指定主机和端口范围,例如:go run main.go -host scanme.nmap.org -ports 1-100,443,8080

这些只是基本的实现。实际的端口扫描工具还需要考虑更多因素,例如SYN扫描、udp扫描、隐蔽扫描等。但这些例子可以帮助你理解如何利用Go语言的并发特性开发高效的端口扫描工具。

以上就是golang如何开发一个端口扫描

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享