Go实现端口扫描
形而上 Lv4

端口扫描基本原理

  1. 向目标主机的某个端口,发送建立链接的请求,如果对方开放了这个端口,就会响应;如果没有没开放,则不会响应。

  2. 根据这个原理,向一些常用的端口逐个建立链接,就能知道对方开放了哪些端口。

端口扫描方法

Telnet

Windows系统自带的 Telnet 命令,可以用来探测目标主机的端口是否开放。

格式:

1
telnet IP 端口

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。

1

半链接扫描

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

1

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
// 扫描440到450的端口打开情况
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

多线程版本

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()
//wg
var wg sync.WaitGroup
//ip
var ip = "127.0.0.1"
//var ip = "192.168.43.34"
//循环
for j := 21; j <= 1000; j++ {
//添加wg
wg.Add(1)
go func(i int) {
//释放wg
defer wg.Done()
var address = fmt.Sprintf("%s:%d", ip, i)
//conn, err := net.DialTimeout("tcp", address, time.Second*10)
conn, err := net.Dial("tcp", address)
if err != nil {
//fmt.Println(address, "是关闭的", err)
return
}
conn.Close()
fmt.Println(address, "打开")
}(j)
}
//等待wg
wg.Wait()
var elapseTime = time.Now().Sub(begin)
fmt.Println("耗时:", elapseTime)
}

1

线程池版本

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"
)

//wg
var wg sync.WaitGroup

//地址管道,100容量
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
}
//fmt.Println("address:", address)
conn, err := net.Dial("tcp", address)
//conn, err := net.DialTimeout("tcp", address, 10)
if err != nil {
//fmt.Println("close:", address, err)
continue
}
conn.Close()
fmt.Println("open:", address)
}
}
func main() {
var begin = time.Now()
//ip
var ip = "127.0.0.1"
//线程池大小
var pool_size = 10000
var pool = gohive.NewFixedPool(pool_size)

//拼接ip:端口
//启动一个线程,用于生成ip:port,并且存放到地址管道种
go func() {
for port := 1; port <= 60000; port++ {
var address = fmt.Sprintf("%s:%d", ip, port)
//将address添加到地址管道
//fmt.Println("<-:",address)
addressChan <- address
}
//发送完关闭 addressChan 管道
close(addressChan)
}()
//启动pool_size工人,处理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)
}

1

  • 本文标题:Go实现端口扫描
  • 本文作者:形而上
  • 创建时间:2023-12-27 08:11:00
  • 本文链接:https://deepter.gitee.io/2023_12_27_go_scan_port/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!