Several code implementations for port scanners

This rainy night is too long. Insomnia, Who is in my dreams? Singing

Security should know the importance of port scanning in the process of penetration testing and vulnerability scanning. Its technology with URL crawling constitutes the first stage of vulnerability scanning, namely the collection of target information. Therefore, the development of an efficient and stable port scanner often determines the quality of the vulnerability scanner. So when it comes to port scanners, we often think of artifacts such as nmap and masscan, which are the benchmarks in this field. But this article is not to introduce these tools, but to talk about how to develop an efficient and stable port scanner.

The port scanner, as its name suggests, is to detect whether a port on the server is open. The principle can be divided into many types of detection methods, such as tcp three-way handshake scanning, syn scanning, etc. This article does not intend to introduce these scanning methods in detail. The difference, interested can look at the nmap document, a detailed introduction to these types of scanning methods.
Then talk about the focus of this article, based on these days I researched and tried to use python, go to develop tcp scanner, tcp-syn scanner, and compare the speed performance and stability difference between them, the test results will be made here. Record and share the code and program.

Note: The Github address of the code used in this article will be given at the end of the article, which can be tested by everyone. The code test environment is centos7.

scan for Python Socket

Python’s Socket module can create sockets and create tcp three-way handshake connections to detect whether the target port is alive. This article will use the socket module to write tcp scans and syn scans, and compare the differences between the two.

tcp scan

Come and see the code:

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
#! -*- coding:utf-8 -*-
import time
import socket
socket_timeout = 0.1
def tcp_scan(ip,port):
try:
s = socket.socket (socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(socket_timeout)
c=s.connect_ex((ip,port))
if c==0:
print "%s:%s is open" % (ip,port)
else:
# print "%s:%s is not open" % (ip,port)
pass
except Exception,e:
print is
s.close()
if __name__=="__main__":
s_time = time.time()
ip = "14.215.177.38"
for port in range(0,1024):
''' Collaboration here '''
tcp_scan(ip,port)
e_time = time.time()
print "scan time is ",e_time-s_time

operation result:

Explain: You can see that this code scans 1024 ports for 102s. Of course, the code does not use multi-threading, coroutine, etc. to improve scanning efficiency (using the coroutine test to overscan 65,535 ports takes about 400s), because python is here. The ability of the aspect is relatively weak; since the tcp three-way handshake is established during the scanning process, resources are consumed.

tcp syn scan

Compared with tcp scanning, the tcp syn scanning method is more concealed and more resource-saving. How to use the socket module to implement tcp syn scanning? SOCK_RAW is needed here, which is relatively rare in socket programming and there is not much information.

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# -*- coding: UTF-8 -*-
import time
import random
import socket
import sys
from struct import *
'''
Warning:must run it as root
yum install python-devel libpcap-devel
pip install pcap
'''
def checksum(msg):
''' Check Summing '''
s = 0
for i in range(0,len(msg),2):
w = (ord(msg[i]) << 8) + (ord(msg[i+1]))
s = s+w
s = (s>>16) + (s & 0xffff)
s = ~s & 0xffff
return s
def CreateSocket(source_ip,dest_ip):
''' create socket connection '''
try:
s = socket.socket (socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
except socket.error, msg:
print 'Socket create error: ',str(msg[0]),'message: ',msg[1]
sys.exit()
''' Set the IP header manually '''
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
return s
def CreateIpHeader(source_ip, dest_ip):
''' create ip header '''
# packet = ''
# ip header option
headerlen = 5
version = 4
tos = 0
to_len = 20 + 20
id = random.randrange (18000,65535,1)
frag_off = 0
ttl = 255
protocol = socket.IPPROTO_TCP
check = 10
saddr = socket.inet_aton ( source_ip )
daddr = socket.inet_aton (dest_ip)
hl_version = (version << 4) + headerlen
ip_header = pack('!BBHHHBBH4s4s', hl_version, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr)
return ip_header
def create_tcp_syn_header(source_ip, dest_ip, dest_port):
''' create tcp syn header function '''
source = random.randrange (32000,62000,1) # randon select one source_port
seq = 0
ack_seq = 0
doff = 5
''' tcp flags '''
end = 0
syn = 1
rst = 0
psh = 0
ack = 0
urg = 0
window = socket.htons (8192) # max windows size
check = 0
urg_ptr = 0
offset_res = (doff << 4) + 0
tcp_flags = fin + (syn<<1) + (rst<<2) + (psh<<3) + (ack<<4) + (urg<<5)
tcp_header = pack('!HHLLBBHHH', source, dest_port, seq, ack_seq, offset_res, tcp_flags, window, check, urg_ptr)
''' headers option '''
source_address = socket.inet_aton( source_ip )
dest_address = socket.inet_aton (dest_ip)
placeholder = 0
protocol = socket.IPPROTO_TCP
tcp_length = len(tcp_header)
psh = pack('!4s4sBBH', source_address, dest_address, placeholder, protocol, tcp_length);
psh = psh + tcp_header;
tcp_checksum = checksum(psh)
''' Repack the TCP header and fill in the correct checksum '''
tcp_header = pack('!HHLLBBHHH', source, dest_port, seq, ack_seq, offset_res, tcp_flags, window, tcp_checksum, urg_ptr)
return tcp_header
def syn_scan(source_ip, dest_ip, des_port) :
s = CreateSocket(source_ip, dest_ip)
ip_header = CreateIpHeader(source_ip, dest_ip)
tcp_header = create_tcp_syn_header(source_ip, dest_ip, des_port)
packet = ip_header + tcp_header
s.sendto(packet, (dest_ip, 0))
data = s.recvfrom(1024) [0][0:]
ip_header_len = (ord(data[0]) & 0x0f) * 4
# ip_header_ret = data[0: ip_header_len - 1]
tcp_header_len = (ord(data[32]) & 0xf0)>>2
tcp_header_ret = data[ip_header_len:ip_header_len+tcp_header_len - 1]
''' SYN/ACK flags '''
if ord(tcp_header_ret[13]) == 0x12:
print "%s:%s is open" % (dest_ip,des_port)
else:
print "%s:%s is not open" % (dest_ip,des_port)
if __name__=="__main__":
t_s = time.time()
Source_ip = '' # Fill in the local ip
dest_ip = '14 .215.177.38 '
for des_port in range(1024):
syn_scan(source_ip, dest_ip, des_port)
t_e = time.time()
print "time is ",(t_e-t_s)

One thing to note is that you need to install dependencies on your system before running this code:

1
2
yum install python-devel libpcap-devel
pip install pcap

operation result:

Explanation: From the point of view of the running results, it is not very accurate, and the speed is not fast. It is not clear whether there is a problem in the code.

scan for Python scapy

In addition to the socket module, Python also has a scapy module, which can be used to simulate the delivery of packets, but can only be used under linux. This module is not recommended for other operating systems.

tcp is csan

The code is here:

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
#! -*- coding:utf-8 -*-
import time
from scapy.all import *
ip = "14.215.177.38"
TIMEOUT = 0.5
threads = 500
port_range = 1024
retry = 1
def is_up(ip):
""" Tests if host is up """
icmp = IP(dst=ip)/ICMP()
resp = sr1(icmp, timeout=TIMEOUT)
if resp == None:
return False
else:
return True
def reset_half_open(ip, ports):
# Reset the connection to stop half-open connections from pooling up
sr(IP(dst=ip)/TCP(dport=ports, flags='AR'), timeout=TIMEOUT)
def is_open(ip, ports):
to_reset = []
results = []
p = IP(dst=ip)/TCP(dport=ports, flags='S') # Forging SYN packet
answers, un_answered = sr(p, verbose=False, retry=retry ,timeout=TIMEOUT) # Send the packets
for req, resp in answers:
if not resp.haslayer(TCP):
continue
tcp_layer = resp.getlayer(TCP)
if tcp_layer.flags == 0x12:
# port is open
to_reset.append(tcp_layer.sport)
results.append(tcp_layer.sport)
elif tcp_layer.flags == 0x14:
# port is open
pass
reset_half_open(ip, to_reset)
return results
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in range(0, len(l), n):
yield l[i:i + n]
if __name__ == '__main__':
start_time = time.time()
open_port_list = []
for ports in chunks(list(range(port_range)), threads):
results = is_open(ip, ports)
if results:
open_port_list += results
end_time = time.time()
print "%s %s" % (ip,open_port_list)
print "%s Scan Completed in %fs" % (ip, end_time-start_time)

operation result:

Description: Since scapy can send multiple syn packages at once, the speed is faster than socket, but the stability is not very good.

scan for python+nmap

The article mentioned nmap at the beginning, in fact, you can also directly call nmap in python, see the code:

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
#! -*- coding:utf-8 -*-
'''
pip install python-nmap
'''
import nmap
nm =nmap.PortScanner()
def scan(ip,port,arg):
try:
nm.scan(ip, arguments=arg+str(port))
except nmap.nmap.PortScannerError:
print "Please run -O method for root privileges"
else:
for host in nm.all_hosts():
for proto in nm[host].all_protocols():
lport = nm[host][proto].keys()
lport.sort()
for port in lport:
print ('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
if __name__=="__main__":
port="80,443,22,21"
scan(ip="14.215.177.38",port=port,arg="-sS -Pn -p")
# tcp scan -sT
# tcp syn scan -sS

operation result:

Since the nmap scanning speed is relatively slow, only the 4 ports are scanned here, and the speed is not compared. Of course, the stability is still possible.

scan for go

I have been using the python language to develop a port scanner. However, due to the weakness of Python in multithreading, the performance of the scanner can be imagined. Therefore, I have tried to develop a port scanner by taking advantage of the high concurrency of the go language. (Off topic: For this reason, I spent half a day reading the basics of the go language, barely understood the scan code, and made some changes)

tcp scan

Look at the code directly:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package main
// port tcp scan
import (
"fmt"
"net"
"the"
"runtime"
"strconv"
"sync"
"time"
)
func loop(inport chan int, startport, endport int) {
for i := startport; i <= endport; i++ {
import <- i
}
close(import)
}
type ScanSafeCount struct {
// structure
count int
mux sync.Mutex
}
var scanCount ScanSafeCount
func scanner(inport int, outport chan int, ip string, endport int) {
// scan function
In := inport // Define the port number to scan
// fmt.Printf(" %d ", in) // Output the scanned port
Host := fmt.Sprintf("%s:%d", ip, in) // similar (ip,port)
tcpAddr, err := net.ResolveTCPAddr("tcp4", host) // Find ip based on domain name
if err != nil {
// Domain name resolution ip failed
outport <- 0
} else {
Conn, err := net.DialTimeout("tcp", tcpAddr.String(), 10*time.Second) //Create a tcp connection
if err != nil {
// tcp connection failed
outport <- 0
} else {
// tcp connection succeeded
Outport <- in // write the port to the outport signal
fmt.Printf("\n *************( %d can )*****************\n", in)
conn.Close()
}
}
// thread lock
scanCount.mux.Lock()
scanCount.count = scanCount.count - 1
if scanCount.count <= 0 {
close(outport)
}
scanCount.mux.Unlock()
}
func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) // Set the maximum number of cpu cores that can be used
/ / Define the variable
Inport := make(chan int) // signal variable, similar to queue in python
outport := make(chan int)
Collect := []int{} // Define a slice variable, similar to a list in python
// fmt.Println(os.Args, len(os.Args)) // Get command line arguments and output
if len(os.Args) != 4 {
// The number of command line arguments is incorrect.
fmt.Println("Usage: port_scanner IP startport endport")
os.Exit (0)
}
s_time := time.Now().Unix()
// fmt.Println("Scan Start:") // Get current time
Ip := string(os.Args[1]) // Get the ip in the parameter
Startport, _ := strconv.Atoi(os.Args[2]) // Get the starting port in the parameter
Endport, _ := strconv.Atoi(os.Args[3]) // Get the end port in the parameter
if start port > end port {
fmt.Println("Usage: scanner IP startport endport")
fmt.Println("Endport must be larger than startport")
os.Exit (0)
} else {
/ / Define the scanCount variable to the ScanSafeCount structure, that is, calculate the number of scanned ports
scanCount = ScanSafeCount{count: (endport - startport + 1)}
}
fmt.Printf("Scan %s:%d----------%d\n", ip, startport, endport)
Go loop(inport, startport, endport) // Execute the loop function to write the port to the input signal
for xrange import {
// start looping input
go scanner(v, outport, ip, endport)
}
// output result
for port := range outport {
if port != 0 {
collect = append(collect, port)
}
}
fmt.Println("--")
fmt.Println(collect)
e_time := time.Now().Unix()
fmt.Println("scan time:", e_time-s_time)
}

I will not explain the code (I added some comments in the code, it should be roughly understandable), this article does not intend to introduce the usage of go, after all, I am just beginning to learn go, interested can look at go documentation, Then look back at this code.

Code running results:

Note: Because it is a tcp scan, it still accounts for a lot of resources, and the test found that the stability is not very good.

tcp syn scan

Look at the code to see the code:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
package main
// port tcp syn scan
import (
"bytes"
"encoding/binary"
"flag"
"fmt"
"log"
"math/rand"
"net"
"the"
"strconv"
"strings"
"time"
"errors"
)
//TCPHeader test
type TCPHeader struct {
SrcPort uint16
DstPort uint16
SeqNum uint32
AckNum uint32
Flags uint16
Window uint16
Chicks Wynt16
UrgentPointer uint16
}
//TCPOption test
type TCPOption struct {
Kind uint8
Length uint8
Data []byte
}
type scanResult struct {
Port uint16
Opened book
}
type scanJob struct {
Ladder string
Raddr string
SPORT uint16
DPort uint16
Stop uint8
}
there stopFlag = make (chan uint8, 1)
func main() {
rate := time.Second / 400
throttle := time.Tick(rate)
jobs := make(chan *scanJob, 65536)
results := make(chan *scanResult, 1000)
for w := 0; w < 10; w++ {
go worker(w, jobs, throttle, results)
}
/ / Get the command line parameters
ifaceName := flag.String("i", "eth0", "Specify network")
remote := flag.String("r", "", "remote address")
portRange := flag.String("p", "1-1024", "port range: -p 1-1024")
flag.Parse()
// ifaceName := &interfaceName_
// remote := &remote_
// portRange: = & portRange_
s_time := time.Now().Unix()
laddr := interfaceAddress(*ifaceName) //
raddr := *remote
minPort, maxPort: = portSplit (portRange)
// fmt.Println(laddr, raddr) // output source ip address, destination ip address
Go func (int num) {
for i := 0; i < num; i++ {
recvSynAck(laddr, raddr, results)
}
}(10)
go func(jobLength int) {
for j := minPort; j < maxPort + 1; j++ {
s := scanJob{
Ladder: Ladder,
Raddr: raddr,
SPort: uint16(random(10000, 65535)),
DPort: uint16(j + 1),
}
jobs <- &s
}
jobs <- &scanJob{Stop: 1}
}(1024)
for {
select {
case res := <-results:
fmt.Println("Scan to open port:", res.Port)
case <-stopFlag:
e_time := time.Now().Unix()
fmt.Println("How much time (s) is spent:", e_time-s_time)
os.Exit (0)
}
}
}
func worker(id int, jobs <-chan *scanJob, th <-chan time.Time, results chan<- *scanResult) {
for j := range jobs {
if j.Stop != 1 {
sendSyn(j.Laddr, j.Raddr, j.SPort, j.DPort)
} else {
stopFlag <- j.Stop
}
<-th
}
}
func checkError(err error) {
// error check
if err != nil {
log.Println(err)
}
}
//CheckSum test
func CheckSum(data []byte, src, dst [4]byte) uint16 {
pseudoHeader := []byte{
src[0], src[1], src[2], src[3],
etc. [0], etc. [1], etc. [2], etc. [3],
0,
6,
0,
byte (len (data))
}
totalLength: = only (pseudoHeader) + len (data)
if totalLength%2 != 0 {
totalLength++
}
d := make([]byte, 0, totalLength)
d = append(d, pseudoHeader...)
d = append(d, data...)
return ^mySum(d)
}
func mySum(data []byte) uint16 {
was sum uint32
for i: = 0; i <len (data) -1; i + = 2 {
}
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
return uint16(sum)
}
func sendSyn(laddr, raddr string, sport, dport uint16) {
conn, err := net.Dial("ip4:tcp", raddr)
checkError(err)
defer conn.Close()
at: = [] TCPOption {
TCPOption{
Kind: 2,
Length: 4,
Data: []byte{0x05, 0xb4},
},
TCPOption{
Kind: 0,
},
}
tcpH := TCPHeader{
SrcPort: sports,
DstPort: sport,
SeqNum: rand.Uint32(),
AckNum: 0,
Flags: 0x8002,
Window: 8192,
As a section: 0,
UrgentPointer: 0,
}
buff := new(bytes.Buffer)
err = binary.Write(buff, binary.BigEndian, tcpH)
checkError(err)
for i: = range up {
binary.Write(buff, binary.BigEndian, op[i].Kind)
binary.Write(buff, binary.BigEndian, op[i].Length)
binary.Write(buff, binary.BigEndian, op[i].Data)
}
binary.Write(buff, binary.BigEndian, [6]byte{})
data := buff.Bytes()
checkSum := CheckSum(data, ipstr2Bytes(laddr), ipstr2Bytes(raddr))
//fmt.Printf("CheckSum 0x%X\n", checkSum)
tcpH.ChkSum = checkSum
buff = new(bytes.Buffer)
binary.Write(buff, binary.BigEndian, tcpH)
for i: = range up {
binary.Write(buff, binary.BigEndian, op[i].Kind)
binary.Write(buff, binary.BigEndian, op[i].Length)
binary.Write(buff, binary.BigEndian, op[i].Data)
}
binary.Write(buff, binary.BigEndian, [6]byte{})
data = buff.Bytes()
//fmt.Printf("% X\n", data)
_, err = conn.Write(data)
checkError(err)
}
func recvSynAck(laddr, raddr string, res chan<- *scanResult) error {
listenAddr, err := net.ResolveIPAddr("ip4", laddr) // resolve the domain name to ip
checkError(err)
conn, err := net.ListenIP("ip4:tcp", listenAddr)
defer conn.Close()
checkError(err)
for {
buff := make([]byte, 1024)
_, addr, err := conn.ReadFrom(buff)
if err != nil {
continue
}
if addr.String() != raddr || buff[13] != 0x12 {
continue
}
was port uint16
binary.Read(bytes.NewReader(buff), binary.BigEndian, &port)
res <- &scanResult{
Port: port,
Opened: true,
}
}
}
func ipstr2Bytes(addr string) [4]byte {
s := strings.Split(addr, ".")
b0, _ := strconv.Atoi(s[0])
b1, _ := strconv.Atoi(s[1])
b2, _ := strconv.Atoi(s[2])
b3, _ := strconv.Atoi(s[3])
return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
}
func random (min, max you) you {
return edge.Intn (max-min) + min
}
func interfaceAddress(ifaceName string ) string {
iface, err:= net.InterfaceByName(ifaceName)
if err != nil {
panic(err)
}
addr, err := iface.Addrs()
if err != nil {
panic(err)
}
addrStr := strings.Split(addr[0].String(), "/")[0]
return addrStr
}
func portSplit(portRange *string) (uint16, uint16) {
ports := strings.Split(*portRange, "-")
minPort, err := strconv.ParseUint(ports[0], 10, 16)
if err !=nil {
panic(err)
}
maxPort, err: = strconv.ParseUint (ports [1], 10, 16)
if err != nil {
panic(err)
}
if minPort > maxPort {
panic(errors.New("minPort must greater than maxPort"))
}
Let's run t16 (Minpo rt), Run t16 (Max po rt)
}

Code running results:

That’s right, it’s 2s! I tested the scan full port (0-65535), about 120s, and the stability is good.

scan for go+python

After the previous tests, we can easily find that in the performance of concurrent, go wins python, but go is not suitable for complex logic processing, and web development. So how to integrate python and go? Here I think of two options, the first one to package the go language into a .so dynamic link library, which can be called using the python ctypes module; the second is to write the interface into a python call. The way to write an interface is relatively simple, so I won’t introduce it here. Let’s talk about how to package go, that is, how to use python to call go’s method or function.

First look at the modified tcp_syn_scan.go code:

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
package main
// port tcp syn scan
import (
"C"
"the"
"bytes"
"encoding/binary"
"fmt"
"log"
"math/rand"
"net"
"strconv"
"strings"
"time"
"errors"
)
//TCPHeader test
type TCPHeader struct {
SrcPort uint16
DstPort uint16
SeqNum uint32
AckNum uint32
Flags uint16
Window uint16
Chicks Wynt16
UrgentPointer uint16
}
//TCPOption test
type TCPOption struct {
Kind uint8
Length uint8
Data []byte
}
type scanResult struct {
Port uint16
Opened book
}
type scanJob struct {
Ladder string
Raddr string
SPORT uint16
DPort uint16
Stop uint8
}
there stopFlag = make (chan uint8, 1)
//export Scan
func Scan(remote_ *C.char, portRange_ *C.char, interfaceName_ *C.char) {
rate := time.Second / 400
throttle := time.Tick(rate)
jobs := make(chan *scanJob, 65536)
results := make(chan *scanResult, 1000)
for w := 0; w < 10; w++ {
go worker(w, jobs, throttle, results)
}
/ / Get the command line parameters
// ifaceName := flag.String("i", "eth0", "Specify network")
// remote := flag.String("r", "", "remote address")
// portRange := flag.String("p", "1-1024", "port range: -p 1-1024")
// flag.Parse()
interfaceName_1 := C.GoString(interfaceName_)
remote_1 := C.GoString(remote_)
portRange_1: = C.GoString (portRange_)
ifaceName := &interfaceName_1
remote := &remote_1
portRange: = & portRange_1
s_time := time.Now().Unix()
laddr := interfaceAddress(*ifaceName) //
raddr := *remote
minPort, maxPort: = portSplit (portRange)
fmt.Println(laddr, raddr) // Output source ip address, destination ip address
Go func (int num) {
for i := 0; i < num; i++ {
recvSynAck(laddr, raddr, results)
}
}(10)
go func(jobLength int) {
for j := minPort; j < maxPort + 1; j++ {
s := scanJob{
Ladder: Ladder,
Raddr: raddr,
SPort: uint16(random(10000, 65535)),
DPort: uint16(j + 1),
}
jobs <- &s
}
jobs <- &scanJob{Stop: 1}
}(1024)
for {
select {
case res := <-results:
fmt.Println ("Scan to open port:", res.Port) / / output open port number
case <-stopFlag:
e_time := time.Now().Unix()
fmt.Println("This scan takes a total of (s):", e_time-s_time)
os.Exit (0)
}
}
}
func worker(id int, jobs <-chan *scanJob, th <-chan time.Time, results chan<- *scanResult) {
for j := range jobs {
if j.Stop != 1 {
sendSyn(j.Laddr, j.Raddr, j.SPort, j.DPort)
} else {
stopFlag <- j.Stop
}
<-th
}
}
func checkError(err error) {
// error check
if err != nil {
log.Println(err)
}
}
//CheckSum test
func CheckSum(data []byte, src, dst [4]byte) uint16 {
pseudoHeader := []byte{
src[0], src[1], src[2], src[3],
etc. [0], etc. [1], etc. [2], etc. [3],
0,
6,
0,
byte (len (data))
}
totalLength: = only (pseudoHeader) + len (data)
if totalLength%2 != 0 {
totalLength++
}
d := make([]byte, 0, totalLength)
d = append(d, pseudoHeader...)
d = append(d, data...)
return ^mySum(d)
}
func mySum(data []byte) uint16 {
was sum uint32
for i: = 0; i <len (data) -1; i + = 2 {
}
sum = (sum >> 16) + (sum & 0xffff)
sum = sum + (sum >> 16)
return uint16(sum)
}
func sendSyn(laddr, raddr string, sport, dport uint16) {
conn, err := net.Dial("ip4:tcp", raddr)
checkError(err)
defer conn.Close()
at: = [] TCPOption {
TCPOption{
Kind: 2,
Length: 4,
Data: []byte{0x05, 0xb4},
},
TCPOption{
Kind: 0,
},
}
tcpH := TCPHeader{
SrcPort: sports,
DstPort: sport,
SeqNum: rand.Uint32(),
AckNum: 0,
Flags: 0x8002,
Window: 8192,
As a section: 0,
UrgentPointer: 0,
}
buff := new(bytes.Buffer)
err = binary.Write(buff, binary.BigEndian, tcpH)
checkError(err)
for i: = range up {
binary.Write(buff, binary.BigEndian, op[i].Kind)
binary.Write(buff, binary.BigEndian, op[i].Length)
binary.Write(buff, binary.BigEndian, op[i].Data)
}
binary.Write(buff, binary.BigEndian, [6]byte{})
data := buff.Bytes()
checkSum := CheckSum(data, ipstr2Bytes(laddr), ipstr2Bytes(raddr))
//fmt.Printf("CheckSum 0x%X\n", checkSum)
tcpH.ChkSum = checkSum
buff = new(bytes.Buffer)
binary.Write(buff, binary.BigEndian, tcpH)
for i: = range up {
binary.Write(buff, binary.BigEndian, op[i].Kind)
binary.Write(buff, binary.BigEndian, op[i].Length)
binary.Write(buff, binary.BigEndian, op[i].Data)
}
binary.Write(buff, binary.BigEndian, [6]byte{})
data = buff.Bytes()
//fmt.Printf("% X\n", data)
_, err = conn.Write(data)
checkError(err)
}
func recvSynAck(laddr, raddr string, res chan<- *scanResult) error {
listenAddr, err := net.ResolveIPAddr("ip4", laddr) // resolve the domain name to ip
checkError(err)
conn, err := net.ListenIP("ip4:tcp", listenAddr)
defer conn.Close()
checkError(err)
for {
buff := make([]byte, 1024)
_, addr, err := conn.ReadFrom(buff)
if err != nil {
continue
}
if addr.String() != raddr || buff[13] != 0x12 {
continue
}
was port uint16
binary.Read(bytes.NewReader(buff), binary.BigEndian, &port)
res <- &scanResult{
Port: port,
Opened: true,
}
}
}
func ipstr2Bytes(addr string) [4]byte {
s := strings.Split(addr, ".")
b0, _ := strconv.Atoi(s[0])
b1, _ := strconv.Atoi(s[1])
b2, _ := strconv.Atoi(s[2])
b3, _ := strconv.Atoi(s[3])
return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
}
func random (min, max you) you {
return edge.Intn (max-min) + min
}
func interfaceAddress(ifaceName string ) string {
iface, err:= net.InterfaceByName(ifaceName)
if err != nil {
panic(err)
}
addr, err := iface.Addrs()
if err != nil {
panic(err)
}
addrStr := strings.Split(addr[0].String(), "/")[0]
return addrStr
}
func portSplit(portRange *string) (uint16, uint16) {
ports := strings.Split(*portRange, "-")
minPort, err := strconv.ParseUint(ports[0], 10, 16)
if err !=nil {
panic(err)
}
maxPort, err: = strconv.ParseUint (ports [1], 10, 16)
if err != nil {
panic(err)
}
if minPort > maxPort {
panic(errors.New("minPort must greater than maxPort"))
}
Let's run t16 (Minpo rt), Run t16 (Max po rt)
}
func main() { }

Then use go’s own build command to package it into a .so library:

1
go build -buildmode=c-shared -o tcp_syn_scan.so tcp_syn_scan.go

After packaging, you will get a tcp_syn_scan.so and a tcp_syn_scan.h. Then use the following python code to call the Scan() function in the Go code to create a tcp_syn_scan.py file.

1
2
3
4
5
#! -*- coding:utf-8 -*-
from ctypes import *
lib = cdll.LoadLibrary(u'./scan.so')
lib.Scan("14.215.177.38","1-1024","eth0") # ip, port range, network card

Code running results:

Description: Pretty native go, using python to call go will lose some performance, but overall it is OK.

Postscript

The conclusion of this article is that you can use go to develop a scanning module (better performance), combined with python calls.
This article code project address: https://github.com/tengzhangchao/PortScan

Reference article

https://blog.csdn.net/pwc1996/article/details/73469850

本文标题:Several code implementations for port scanners

文章作者:nmask

发布时间:2018年05月17日 - 18:05

最后更新:2019年07月11日 - 18:07

原始链接:https://thief.one/2018/05/17/1/en/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

nmask wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!

热门文章推荐: