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 分享