端口扫描基本原理
向目标主机的某个端口,发送建立链接的请求,如果对方开放了这个端口,就会响应;如果没有没开放,则不会响应。
 
根据这个原理,向一些常用的端口逐个建立链接,就能知道对方开放了哪些端口。
 
端口扫描方法
Telnet
Windows系统自带的 Telnet 命令,可以用来探测目标主机的端口是否开放。
格式:
Nmap
1
   | nmap -sV -p 1-65535 206.119.105.9
   | 
 
Masscan
1
   | masscan -p 0-65535 206.119.105.9
   | 
 
几种扫描工具的原理和区别
- Telnet 使用完整的三次握手建立链接,常用于单个端口的测试。
 
- Masscan 只发送SYN包,如果对方返回 ACK+SYN 就说明端口开放。
 
- Nmap 默认使用SYN扫描,可以通过修改参数来修改扫描的方式。
 
端口扫描分类
完全链接扫描
使用TCP三次握手建立一次完整的链接,从系统调用 connect()开始,端口开放则建立链接,端口不开放则返回-1。

半链接扫描
就是我们常说的SYN扫描,只建立TCP的前两次链接,发送一个SYN后,就停止建立链接,等待对方的响应。
如果返回一个ACK,就说明端口开放;如果返回一个RESET,就说明端口没开放。

go实现端口扫描
在Go中,我们通常使用net.Dial进行TCP连接。它就两种情况成功:返回conn,失败:err != nil。
单线程版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   |  package main
  import ( 	"fmt" 	"net" )
  func main() { 	var ip = "127.0.0.1" 	for i := 440; i <= 450; i++ { 		var address = fmt.Sprintf("%s:%d", ip, i) 		conn, err := net.Dial("tcp", address) 		if err != nil { 			fmt.Println(address, "是关闭的") 			continue 		} 		conn.Close() 		fmt.Println(address, "打开") 	} }
 
  | 
 

多线程版本
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 32 33 34 35 36 37 38 39 40
   | package main
  import ( 	"fmt" 	"net" 	"sync" 	"time" )
  func main() {
  	var begin = time.Now() 	 	var wg sync.WaitGroup 	 	var ip = "127.0.0.1" 	 	 	for j := 21; j <= 1000; j++ { 		 		wg.Add(1) 		go func(i int) { 			 			defer wg.Done() 			var address = fmt.Sprintf("%s:%d", ip, i) 			 			conn, err := net.Dial("tcp", address) 			if err != nil { 				 				return 			} 			conn.Close() 			fmt.Println(address, "打开") 		}(j) 	} 	 	wg.Wait() 	var elapseTime = time.Now().Sub(begin) 	fmt.Println("耗时:", elapseTime) }
   | 
 

线程池版本
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
   | package main
 
  import ( 	"fmt" 	"net" 	"sync" 	"time"
  	"github.com/loveleshsharma/gohive" )
 
  var wg sync.WaitGroup
 
  var addressChan = make(chan string, 100)
  type Worker struct { }
  func (s Worker) Run() { 	worker() }
 
  func worker() { 	 	defer wg.Done() 	for { 		address, ok := <-addressChan 		if !ok { 			break 		} 		 		conn, err := net.Dial("tcp", address) 		 		if err != nil { 			 			continue 		} 		conn.Close() 		fmt.Println("open:", address) 	} } func main() { 	var begin = time.Now() 	 	var ip = "127.0.0.1" 	 	var pool_size = 10000 	var pool = gohive.NewFixedPool(pool_size)
  	 	 	go func() { 		for port := 1; port <= 60000; port++ { 			var address = fmt.Sprintf("%s:%d", ip, port) 			 			 			addressChan <- address 		} 		 		close(addressChan) 	}() 	 	for work := 0; work < pool_size; work++ { 		wg.Add(1) 		pool.Submit(Worker{}) 	} 	 	wg.Wait() 	 	var elapseTime = time.Now().Sub(begin) 	fmt.Println("耗时:", elapseTime) }
 
   | 
 
