端口扫描基本原理
向目标主机的某个端口,发送建立链接的请求,如果对方开放了这个端口,就会响应;如果没有没开放,则不会响应。
根据这个原理,向一些常用的端口逐个建立链接,就能知道对方开放了哪些端口。
端口扫描方法
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) }
|
