主頁 > 後端開發 > 埠掃描器的幾種代碼實作方案

埠掃描器的幾種代碼實作方案

2020-09-19 10:42:05 後端開發

  搞安全的應該都知道埠掃描在滲透測驗、漏洞掃描程序中的重要性,其與URL爬蟲等技術構成了漏洞掃描的第一階段,即目標資訊收集,因此能否開發出一款高效穩定的埠掃描器,往往決定了漏洞掃描器的好壞,那么說到埠掃描器,我們往往會先想到nmap、masscan等神器,它們是這個領域的標桿,但本篇并不是為了介紹這幾款工具,而是談談如何自研一款高效穩定的埠掃描器,

  埠掃描器,顧名思義就是為了探測服務器上的某個埠是否開放,究其原理可以分為很多種探測方式,比如tcp三次握手掃描,syn掃描等等,本篇并不打算詳細介紹這些掃描方式的區別,有興趣的可以看下nmap的檔案,對這幾種掃描方式有詳細的介紹,

  那么說下本文重點,基于這幾天我研究并嘗試利用python、go開發tcp掃描器、tcp-syn掃描器,以及對比它們之間的速度性能、穩定性差異情況,將測驗結果在此做個記錄,并分享一下代碼以及方案,

  說明:文章結尾將給出本篇所使用代碼的Github地址,可供大家測驗,代碼測驗環境為centos7,

  scan for Python Socket

  Python的Socket模塊可以創建套接字,創建tcp三次握手連接,以此探測目標埠是否存活,本篇將使用socket模塊撰寫tcp掃描以及syn掃描,并對比兩者的差異,

  tcp scan

  快來看代碼:

 1 #! -*- coding:utf-8 -*-
 2 import time
 3 import socket
 4 socket_timeout = 0.1
 5 def tcp_scan(ip,port):
 6 try:
 7 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 s.settimeout(socket_timeout)
 9 c=s.connect_ex((ip,port))
10 if c==0:
11 print “%s:%s is open” % (ip,port)
12 else :
13 # print “%s:%s is not open” % (ip,port)
14 pass
15 except Exception,e:
16 print e
17 s.close()
18 if __name__== “__main__” :
19 s_time = time.time()
20 ip = “14.215.177.3821 for port in range(0,1024):
22 ” ‘ 此處可用協作 ‘ ”
23 tcp_scan(ip,port)
24 e_time = time.time()
25 print “scan time is “ ,e_time-s_time

  運行結果:

  說明一下:可以看到此代碼掃描1024個埠用了102s,當然代碼并沒有用多執行緒、協程等方式提高掃描效率(使用協程測驗過掃65535個埠用時400s左右),因為python在這方面的能力比較弱;由于掃描程序中會建立tcp三次握手,因此比較消耗資源,

  tcp syn scan

 

  相對tcp掃描,tcp syn掃描方式更為隱蔽,也更節省資源,那么如何利用socket模塊實作tcp syn掃描呢?這里需要用到SOCK_RAW,這個在socket編程中相對少用,資料也不多,

  1 # -*- coding: UTF-8 -*-
  2 import time
  3 import random
  4 import socket
  5 import sys
  6 from struct import *
  7 ” ‘
  8 Warning:must run it as root
  9 yum install python-devel libpcap-devel
 10 pip install pcap
 11 ‘ ”
 12 def checksum(msg):
 13 ” ‘ Check Summing ‘ ”
 14 s = 0
 15 for i in range(0,len(msg),2):
 16 w = (ord(msg[i]) << 8) + (ord(msg[i+1]))
 17 s = s+w
 18 s = (s>>16) + (s & 0xffff)
 19 s = ~s & 0xffff
 20 return s
 21 def CreateSocket(source_ip,dest_ip):
 22 ” ‘ create socket connection ‘ ”
 23 try:
 24 s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
 25 except socket.error, msg:
 26 print ‘Socket create error: ‘ ,str(msg[0]), ‘message: ‘ ,msg[1]
 27 sys.exit()
 28 ” ‘ Set the IP header manually ‘ ”
 29 s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
 30 return s
 31 def CreateIpHeader(source_ip, dest_ip):
 32 ” ‘ create ip header ‘ ”
 33 # packet = ”
 34 # ip header option
 35 headerlen = 5
 36 version = 4
 37 tos = 0
 38 tot_len = 20 + 20
 39 id = random.randrange(18000,65535,1)
 40 frag_off = 0
 41 ttl = 255
 42 protocol = socket.IPPROTO_TCP
 43 check = 10
 44 saddr = socket.inet_aton ( source_ip )
 45 daddr = socket.inet_aton ( dest_ip )
 46 hl_version = (version << 4) + headerlen
 47 ip_header = pack( ‘!BBHHHBBH4s4s’ , hl_version, tos, tot_len, id, frag_off, ttl, protocol, check, saddr, daddr)
 48 return ip_header
 49 def create_tcp_syn_header(source_ip, dest_ip, dest_port):
 50 ” ‘ create tcp syn header function ‘ ”
 51 source = random.randrange(32000,62000,1) # randon select one source_port
 52 seq = 0
 53 ack_seq = 0
 54 doff = 5
 55 ” ‘ tcp flags ‘ ”
 56 fin = 0
 57 syn = 1
 58 rst = 0
 59 psh = 0
 60 ack = 0
 61 urg = 0
 62 window = socket.htons (8192) # max windows size
 63 check = 0
 64 urg_ptr = 0
 65 offset_res = (doff << 4) + 0
 66 tcp_flags = fin + (syn<<1) + (rst<<2) + (psh<<3) + (ack<<4) + (urg<<5)
 67 tcp_header = pack( ‘!HHLLBBHHH’ , source , dest_port, seq, ack_seq, offset_res, tcp_flags, window, check, urg_ptr)
 68 ” ‘ headers option ‘ ”
 69 source_address = socket.inet_aton( source_ip )
 70 dest_address = socket.inet_aton( dest_ip )
 71 placeholder = 0
 72 protocol = socket.IPPROTO_TCP
 73 tcp_length = len(tcp_header)
 74 psh = pack( ‘!4s4sBBH’ , source_address, dest_address, placeholder, protocol, tcp_length);
 75 psh = psh + tcp_header;
 76 tcp_checksum = checksum(psh)
 77 ” ‘ Repack the TCP header and fill in the correct checksum ‘ ”
 78 tcp_header = pack( ‘!HHLLBBHHH’ , source , dest_port, seq, ack_seq, offset_res, tcp_flags, window, tcp_checksum, urg_ptr)
 79 return tcp_header
 80 def syn_scan(source_ip, dest_ip, des_port) :
 81 s = CreateSocket(source_ip, dest_ip)
 82 ip_header = CreateIpHeader(source_ip, dest_ip)
 83 tcp_header = create_tcp_syn_header(source_ip, dest_ip, des_port)
 84 packet = ip_header + tcp_header
 85 s.sendto(packet, (dest_ip, 0))
 86 data = https://www.cnblogs.com/darecy/p/s.recvfrom(1024) [0][0:]
 87 ip_header_len = (ord(data[0]) & 0x0f) * 4
 88 # ip_header_ret = data[0: ip_header_len – 1]
 89 tcp_header_len = (ord(data[32]) & 0xf0)>>2
 90 tcp_header_ret = data[ip_header_len:ip_header_len+tcp_header_len – 1]
 91 ” ‘ SYN/ACK flags ‘ ”
 92 if ord(tcp_header_ret[13]) == 0x12:
 93 print “%s:%s is open” % (dest_ip,des_port)
 94 else :
 95 print “%s:%s is not open” % (dest_ip,des_port)
 96 if __name__== “__main__” :
 97 t_s = time.time()
 98 source_ip = ” # 填寫本機ip
 99 dest_ip = ‘14.215.177.38100 for des_port in range(1024):
101 syn_scan(source_ip, dest_ip, des_port)
102 t_e = time.time()
103 print “time is “ ,(t_e-t_s)

  有一點需要注意的,運行這段代碼前,需要在系統上安裝依賴:

yum install python-devel libpcap-devel
pip install pcap

  

  運行結果:

  說明:從運行結果上來看,并沒有很準確,而且速度也不快,不清楚是不是代碼上有問題,

  scan for Python scapy

  除了socket模塊外,python還有一個scapy模塊,可以用來模擬發包,但只能在linux下使用,其他作業系統不建議使用此模塊,

  tcp syn csan

  代碼在這里:

 1 #! -*- coding:utf-8 -*-
 2 import time
 3 from scapy.all import *
 4 ip = “14.215.177.38 5 TIMEOUT = 0.5
 6 threads = 500
 7 port_range = 1024
 8 retry = 1
 9 def is_up(ip):
10 “” ” Tests if host is up “ “”
11 icmp = IP(dst=ip)/ICMP()
12 resp = sr1(icmp, timeout=TIMEOUT)
13 if resp == None:
14 return False
15 else :
16 return True
17 def reset_half_open(ip, ports):
18 # Reset the connection to stop half-open connections from pooling up
19 sr(IP(dst=ip)/TCP(dport=ports, flags= ‘AR’ ), timeout=TIMEOUT)
20 def is_open(ip, ports):
21 to_reset = []
22 results = []
23 p = IP(dst=ip)/TCP(dport=ports, flags= ‘S’ ) # Forging SYN packet
24 answers, un_answered = sr(p, verbose=False, retry=retry ,timeout=TIMEOUT) # Send the packets
25 for req, resp in answers:
26 if not resp.haslayer(TCP):
27 continue
28 tcp_layer = resp.getlayer(TCP)
29 if tcp_layer.flags == 0x12:
30 # port is open
31 to_reset.append(tcp_layer.sport)
32 results.append(tcp_layer.sport)
33 elif tcp_layer.flags == 0x14:
34 # port is open
35 pass
36 reset_half_open(ip, to_reset)
37 return results
38 def chunks(l, n):
39 “” “Yield successive n-sized chunks from l.” “”
40 for i in range(0, len(l), n):
41 yield l[i:i + n]
42 if __name__ == ‘__main__’ :
43 start_time = time.time()
44 open_port_list = []
45 for ports in chunks(list(range(port_range)), threads):
46 results = is_open(ip, ports)
47 if results:
48 open_port_list += results
49 end_time = time.time()
50 print “%s %s” % (ip,open_port_list)
51 print “%s Scan Completed in %fs” % (ip, end_time-start_time)

 

  運行結果:

  說明:由于scapy可以一次性發多個syn包,因此速度相對socket更快一些,但穩定性沒有很好,

  scan for python+nmap

  文章開頭提到了nmap,其實在python中也可以直接呼叫nmap,看代碼:

 1 #! -*- coding:utf-8 -*-
 2 ” ‘
 3 pip install python-nmap
 4 ‘ ”
 5 import nmap
 6 nm =nmap.PortScanner()
 7 def scan(ip,port,arg):
 8 try:
 9 nm.scan(ip, arguments=arg+str(port))
10 except nmap.nmap.PortScannerError:
11 print “Please run -O method for root privileges”
12 else :
13 for host in nm.all_hosts():
14 for proto in nm[host].all_protocols():
15 lport = nm[host][proto].keys()
16 lport.sort()
17 for port in lport:
18 print ( ‘port : %ststate : %s’ % (port, nm[host][proto][port][ ‘state’ ]))
19 if __name__== “__main__” :
20 port= “80,443,22,2121 scan(ip= “14.215.177.38” ,port=port,arg= “-sS -Pn -p” )
22 # tcp scan -sT
23 # tcp syn scan -sS

  運行結果:

  由于nmap掃描速度相對比較慢,因此這里只演示掃描4個埠,不做速度的對比,當然其穩定性還是可以的,

  scan for go

  前文一直在介紹使用python語言開發埠掃描器,然而由于python在多執行緒方面的弱勢,掃描器的性能可想而知,因此我又利用go語言的高并發性優勢,嘗試開發埠掃描器,(題外話:為此我花了半天時間看了下go語言的基礎,勉強看懂了掃描代碼,并做了一些修改)

  tcp scan

  直接看代碼吧:

 1 package main
 2 // port tcp scan
 3 import (
 4 “fmt”
 5 “net”
 6 “os”
 7 “runtime”
 8 “strconv”
 9 “sync”
10 “time”
11 )
12 func loop(inport chan int, startport, endport int) {
13 for i := startport; i <= endport; i++ {
14 inport <- i
15 }
16 close(inport)
17 }
18 type ScanSafeCount struct {
19 // 結構體
20 count int
21 mux sync.Mutex
22 }
23 var scanCount ScanSafeCount
24 func scanner(inport int, outport chan int, ip string, endport int) {
25 // 掃描函式
26 in := inport // 定義要掃描的埠號
27 // fmt.Printf( ” %d “ , in ) // 輸出掃描的埠
28 host := fmt.Sprintf( “%s:%d” , ip, in ) // 類似(ip,port)
29 tcpAddr, err := net.ResolveTCPAddr( “tcp4” , host) // 根據域名查找ip
30 if err != nil {
31 // 域名決議ip失敗
32 outport <- 0
33 } else {
34 conn, err := net.DialTimeout( “tcp” , tcpAddr.String(), 10*time.Second) //建立tcp連接
35 if err != nil {
36 // tcp連接失敗
37 outport <- 0
38 } else {
39 // tcp連接成功
40 outport <- in // 將埠寫入outport信號
41 fmt.Printf( “n *************( %d 可以 )*****************n” , in )
42 conn.Close()
43 }
44 }
45 // 執行緒鎖
46 scanCount.mux.Lock()
47 scanCount.count = scanCount.count – 1
48 if scanCount.count <= 0 {
49 close(outport)
50 }
51 scanCount.mux.Unlock()
52 }
53 func main () {
54 runtime.GOMAXPROCS(runtime.NumCPU()) // 設定最大可使用的cpu核數
55 // 定義變數
56 inport := make(chan int) // 信號變數,類似python中的queue
57 outport := make(chan int)
58 collect := []int{} // 定義一個切片變數,類似python中的list
59 // fmt.Println(os.Args, len(os.Args)) // 獲取命令列引數并輸出
60 if len(os.Args) != 4 {
61 // 命令列引數個數有誤
62 fmt.Println( “使用方式:port_scanner IP startport endport” )
63 os.Exit(0)
64 }
65 s_time := time.Now().Unix()
66 // fmt.Println( “掃描開始:” ) // 獲取當前時間
67 ip := string(os.Args[1]) // 獲取引數中的ip
68 startport, _ := strconv.Atoi(os.Args[2]) // 獲取引數中的啟始埠
69 endport, _ := strconv.Atoi(os.Args[3]) // 獲取引數中的結束埠
70 if startport > endport {
71 fmt.Println( “Usage: scanner IP startport endport” )
72 fmt.Println( “Endport must be larger than startport” )
73 os.Exit(0)
74 } else {
75 // 定義scanCount變數為ScanSafeCount結構體,即計算掃描的埠數量
76 scanCount = ScanSafeCount{count: (endport – startport + 1)}
77 }
78 fmt.Printf( “掃描 %s:%d———-%dn” , ip, startport, endport)
79 go loop(inport, startport, endport) // 執行loop函式將埠寫入input信號
80 for v := range inport {
81 // 開始回圈input
82 go scanner(v, outport, ip, endport)
83 }
84 // 輸出結果
85 for port := range outport {
86 if port != 0 {
87 collect = append(collect, port)
88 }
89 }
90 fmt.Println( “–“ )
91 fmt.Println(collect)
92 e_time := time.Now().Unix()
93 fmt.Println( “掃描時間:” , e_time-s_time)
94 }

  代碼我就不解釋了(我在代碼中加了些注釋,應該大致可以看懂),本文也不打算介紹go的用法,畢竟自己也是剛開始學習go,有興趣的可以看看go的檔案,然后再回過頭來看看這段代碼,

  代碼運行結果:

  說明:由于是tcp掃描,所以多少還是占資源的,而且測驗發現穩定性不是很好,

  tcp syn scan

  看代碼看代碼:

  1 package main
  2 // port tcp syn scan
  3 import (
  4 “bytes”
  5 “encoding/binary”
  6 “flag”
  7 “fmt”
  8 “log”
  9 “math/rand”
 10 “net”
 11 “os”
 12 “strconv”
 13 “strings”
 14 “time”
 15 “errors”
 16 )
 17 //TCPHeader test
 18 type TCPHeader struct {
 19 SrcPort uint16
 20 DstPort uint16
 21 SeqNum uint32
 22 AckNum uint32
 23 Flags uint16
 24 Window uint16
 25 ChkSum uint16
 26 UrgentPointer uint16
 27 }
 28 //TCPOption test
 29 type TCPOption struct {
 30 Kind uint8
 31 Length uint8
 32 Data []byte
 33 }
 34 type scanResult struct {
 35 Port uint16
 36 Opened bool
 37 }
 38 type scanJob struct {
 39 Laddr string
 40 Raddr string
 41 SPort uint16
 42 DPort uint16
 43 Stop uint8
 44 }
 45 var stopFlag = make(chan uint8, 1)
 46 func main () {
 47 rate := time.Second / 400
 48 throttle := time.Tick(rate)
 49 jobs := make(chan *scanJob, 65536)
 50 results := make(chan *scanResult, 1000)
 51 for w := 0; w < 10; w++ {
 52 go worker(w, jobs , throttle, results)
 53 }
 54 // 獲取命令列引數
 55 ifaceName := flag.String( “i” , “eth0” , “Specify network” )
 56 remote := flag.String( “r” , “” , “remote address” )
 57 portRange := flag.String( “p” , “1-1024” , “port range: -p 1-1024” )
 58 flag.Parse()
 59 // ifaceName := &interfaceName_
 60 // remote := &remote_
 61 // portRange := &portRange_
 62 s_time := time.Now().Unix()
 63 laddr := interfaceAddress(*ifaceName) //
 64 raddr := *remote
 65 minPort , maxPort := portSplit(portRange)
 66 // fmt.Println(laddr, raddr) // 輸出源ip地址,目標ip地址
 67 go func(num int){
 68 for i := 0; i < num; i++ {
 69 recvSynAck(laddr, raddr, results)
 70 }
 71 }(10)
 72 go func(jobLength int) {
 73 for j := minPort; j < maxPort + 1; j++ {
 74 s := scanJob{
 75 Laddr: laddr,
 76 Raddr: raddr,
 77 SPort: uint16(random(10000, 65535)),
 78 DPort: uint16(j + 1),
 79 }
 80 jobs <- &s
 81 }
 82 jobs <- &scanJob{Stop: 1}
 83 }(1024)
 84 for {
 85 select {
 86 case res := <-results:
 87 fmt.Println( “掃描到開放的埠:” ,res.Port)
 88 case <-stopFlag:
 89 e_time := time.Now().Unix()
 90 fmt.Println( “總共用了多少時間(s):” ,e_time-s_time)
 91 os.Exit(0)
 92 }
 93 }
 94 }
 95 func worker(id int, jobs <-chan *scanJob, th <-chan time.Time, results chan<- *scanResult) {
 96 for j := range jobs {
 97 if j.Stop != 1 {
 98 sendSyn(j.Laddr, j.Raddr, j.SPort, j.DPort)
 99 } else {
100 stopFlag <- j.Stop
101 }
102 <-th
103 }
104 }
105 func checkError(err error) {
106 // 錯誤check
107 if err != nil {
108 log.Println(err)
109 }
110 }
111 //CheckSum test
112 func CheckSum(data []byte, src, dst [4]byte) uint16 {
113 pseudoHeader := []byte{
114 src[0], src[1], src[2], src[3],
115 dst[0], dst[1], dst[2], dst[3],
116 0,
117 6,
118 0,
119 byte(len(data)),
120 }
121 totalLength := len(pseudoHeader) + len(data)
122 if totalLength%2 != 0 {
123 totalLength++
124 }
125 d := make([]byte, 0, totalLength)
126 d = append(d, pseudoHeader…)
127 d = append(d, data…)
128 return ^mySum(d)
129 }
130 func mySum(data []byte) uint16 {
131 var sum uint32
132 for i := 0; i < len(data)-1; i += 2 {
133 sum += uint32(uint16(data[i])<<8 | uint16(data[i+1]))
134 }
135 sum = (sum >> 16) + (sum & 0xffff)
136 sum = sum + (sum >> 16)
137 return uint16(sum)
138 }
139 func sendSyn(laddr, raddr string, sport, dport uint16) {
140 conn, err := net.Dial( “ip4:tcp” , raddr)
141 checkError(err)
142 defer conn.Close()
143 op := []TCPOption{
144 TCPOption{
145 Kind: 2,
146 Length: 4,
147 Data: []byte{0x05, 0xb4},
148 },
149 TCPOption{
150 Kind: 0,
151 },
152 }
153 tcpH := TCPHeader{
154 SrcPort: sport,
155 DstPort: dport,
156 SeqNum: rand.Uint32(),
157 AckNum: 0,
158 Flags: 0x8002,
159 Window: 8192,
160 ChkSum: 0,
161 UrgentPointer: 0,
162 }
163 buff := new(bytes.Buffer)
164 err = binary.Write(buff, binary.BigEndian, tcpH)
165 checkError(err)
166 for i := range op {
167 binary.Write(buff, binary.BigEndian, op[i].Kind)
168 binary.Write(buff, binary.BigEndian, op[i].Length)
169 binary.Write(buff, binary.BigEndian, op[i].Data)
170 }
171 binary.Write(buff, binary.BigEndian, [6]byte{})
172 data := buff.Bytes()
173 checkSum := CheckSum(data, ipstr2Bytes(laddr), ipstr2Bytes(raddr))
174 //fmt.Printf( “CheckSum 0x%Xn” , checkSum)
175 tcpH.ChkSum = checkSum
176 buff = new(bytes.Buffer)
177 binary.Write(buff, binary.BigEndian, tcpH)
178 for i := range op {
179 binary.Write(buff, binary.BigEndian, op[i].Kind)
180 binary.Write(buff, binary.BigEndian, op[i].Length)
181 binary.Write(buff, binary.BigEndian, op[i].Data)
182 }
183 binary.Write(buff, binary.BigEndian, [6]byte{})
184 data =https://www.cnblogs.com/darecy/p/ buff.Bytes()
185 //fmt.Printf( “% Xn” , data)
186 _, err = conn.Write(data)
187 checkError(err)
188 }
189 func recvSynAck(laddr, raddr string, res chan<- *scanResult) error {
190 listenAddr, err := net.ResolveIPAddr( “ip4” , laddr) // 決議域名為ip
191 checkError(err)
192 conn, err := net.ListenIP( “ip4:tcp” , listenAddr)
193 defer conn.Close()
194 checkError(err)
195 for {
196 buff := make([]byte, 1024)
197 _, addr, err := conn.ReadFrom(buff)
198 if err != nil {
199 continue
200 }
201 if addr.String() != raddr || buff[13] != 0x12 {
202 continue
203 }
204 var port uint16
205 binary.Read(bytes.NewReader(buff), binary.BigEndian, &port)
206 res <- &scanResult{
207 Port: port,
208 Opened: true ,
209 }
210 }
211 }
212 func ipstr2Bytes(addr string) [4]byte {
213 s := strings.Split(addr, “.” )
214 b0, _ := strconv.Atoi(s[0])
215 b1, _ := strconv.Atoi(s[1])
216 b2, _ := strconv.Atoi(s[2])
217 b3, _ := strconv.Atoi(s[3])
218 return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
219 }
220 func random(min, max int) int {
221 return rand.Intn(max-min) + min
222 }
223 func interfaceAddress(ifaceName string ) string {
224 iface, err:= net.InterfaceByName(ifaceName)
225 if err != nil {
226 panic(err)
227 }
228 addr, err := iface.Addrs()
229 if err != nil {
230 panic(err)
231 }
232 addrStr := strings.Split(addr[0].String(), “/” )[0]
233 return addrStr
234 }
235 func portSplit(portRange *string) (uint16, uint16) {
236 ports := strings.Split(*portRange, “-“ )
237 minPort, err := strconv.ParseUint(ports[0], 10, 16)
238 if err !=nil {
239 panic(err)
240 }
241 maxPort, err := strconv.ParseUint(ports[1], 10, 16)
242 if err != nil {
243 panic(err)
244 }
245 if minPort > maxPort {
246 panic(errors.New( “minPort must greater than maxPort” ))
247 }
248 return uint16(minPort), uint16(maxPort)
249 }

  代碼運行結果:

  沒錯,就是2s!我測驗了掃描全埠(0-65535),大概120s左右,而且穩定性不錯,

  scan for go+python

  經過前面的測驗我們不難發現,在并發的性能上,go完勝python,但go不適合做復雜的邏輯處理,以及web開發之類的,因此如何整合python跟go呢?這里我想了兩種方案,第一種將go語言打包成.so動態連接庫,利用python的ctypes模塊可以呼叫;第二種是go寫成介面,提供python呼叫,寫成介面的方式相對簡單一些,因此這里不介紹了,說說如何打包go,即如何利用python呼叫go的方法或者說函式,

  先看下修改過后的tcp_syn_scan.go代碼:

  1 package main
  2 // port tcp syn scan
  3 import (
  4 “C”
  5 “os”
  6 “bytes”
  7 “encoding/binary”
  8 “fmt”
  9 “log”
 10 “math/rand”
 11 “net”
 12 “strconv”
 13 “strings”
 14 “time”
 15 “errors”
 16 )
 17 //TCPHeader test
 18 type TCPHeader struct {
 19 SrcPort uint16
 20 DstPort uint16
 21 SeqNum uint32
 22 AckNum uint32
 23 Flags uint16
 24 Window uint16
 25 ChkSum uint16
 26 UrgentPointer uint16
 27 }
 28 //TCPOption test
 29 type TCPOption struct {
 30 Kind uint8
 31 Length uint8
 32 Data []byte
 33 }
 34 type scanResult struct {
 35 Port uint16
 36 Opened bool
 37 }
 38 type scanJob struct {
 39 Laddr string
 40 Raddr string
 41 SPort uint16
 42 DPort uint16
 43 Stop uint8
 44 }
 45 var stopFlag = make(chan uint8, 1)
 46 // export Scan
 47 func Scan(remote_ *C.char, portRange_ *C.char, interfaceName_ *C.char) {
 48 rate := time.Second / 400
 49 throttle := time.Tick(rate)
 50 jobs := make(chan *scanJob, 65536)
 51 results := make(chan *scanResult, 1000)
 52 for w := 0; w < 10; w++ {
 53 go worker(w, jobs , throttle, results)
 54 }
 55 // 獲取命令列引數
 56 // ifaceName := flag.String( “i” , “eth0” , “Specify network” )
 57 // remote := flag.String( “r” , “” , “remote address” )
 58 // portRange := flag.String( “p” , “1-1024” , “port range: -p 1-1024” )
 59 // flag.Parse()
 60 interfaceName_1 := C.GoString(interfaceName_)
 61 remote_1 := C.GoString(remote_)
 62 portRange_1 := C.GoString(portRange_)
 63 ifaceName := &interfaceName_1
 64 remote := &remote_1
 65 portRange := &portRange_1
 66 s_time := time.Now().Unix()
 67 laddr := interfaceAddress(*ifaceName) //
 68 raddr := *remote
 69 minPort , maxPort := portSplit(portRange)
 70 fmt.Println(laddr, raddr) // 輸出源ip地址,目標ip地址
 71 go func(num int){
 72 for i := 0; i < num; i++ {
 73 recvSynAck(laddr, raddr, results)
 74 }
 75 }(10)
 76 go func(jobLength int) {
 77 for j := minPort; j < maxPort + 1; j++ {
 78 s := scanJob{
 79 Laddr: laddr,
 80 Raddr: raddr,
 81 SPort: uint16(random(10000, 65535)),
 82 DPort: uint16(j + 1),
 83 }
 84 jobs <- &s
 85 }
 86 jobs <- &scanJob{Stop: 1}
 87 }(1024)
 88 for {
 89 select {
 90 case res := <-results:
 91 fmt.Println( “掃描到開放的埠:” ,res.Port) //輸出開放的埠號
 92 case <-stopFlag:
 93 e_time := time.Now().Unix()
 94 fmt.Println( “本次掃描總共耗時(s):” ,e_time-s_time)
 95 os.Exit(0)
 96 }
 97 }
 98 }
 99 func worker(id int, jobs <-chan *scanJob, th <-chan time.Time, results chan<- *scanResult) {
100 for j := range jobs {
101 if j.Stop != 1 {
102 sendSyn(j.Laddr, j.Raddr, j.SPort, j.DPort)
103 } else {
104 stopFlag <- j.Stop
105 }
106 <-th
107 }
108 }
109 func checkError(err error) {
110 // 錯誤check
111 if err != nil {
112 log.Println(err)
113 }
114 }
115 //CheckSum test
116 func CheckSum(data []byte, src, dst [4]byte) uint16 {
117 pseudoHeader := []byte{
118 src[0], src[1], src[2], src[3],
119 dst[0], dst[1], dst[2], dst[3],
120 0,
121 6,
122 0,
123 byte(len(data)),
124 }
125 totalLength := len(pseudoHeader) + len(data)
126 if totalLength%2 != 0 {
127 totalLength++
128 }
129 d := make([]byte, 0, totalLength)
130 d = append(d, pseudoHeader…)
131 d = append(d, data…)
132 return ^mySum(d)
133 }
134 func mySum(data []byte) uint16 {
135 var sum uint32
136 for i := 0; i < len(data)-1; i += 2 {
137 sum += uint32(uint16(data[i])<<8 | uint16(data[i+1]))
138 }
139 sum = (sum >> 16) + (sum & 0xffff)
140 sum = sum + (sum >> 16)
141 return uint16(sum)
142 }
143 func sendSyn(laddr, raddr string, sport, dport uint16) {
144 conn, err := net.Dial( “ip4:tcp” , raddr)
145 checkError(err)
146 defer conn.Close()
147 op := []TCPOption{
148 TCPOption{
149 Kind: 2,
150 Length: 4,
151 Data: []byte{0x05, 0xb4},
152 },
153 TCPOption{
154 Kind: 0,
155 },
156 }
157 tcpH := TCPHeader{
158 SrcPort: sport,
159 DstPort: dport,
160 SeqNum: rand.Uint32(),
161 AckNum: 0,
162 Flags: 0x8002,
163 Window: 8192,
164 ChkSum: 0,
165 UrgentPointer: 0,
166 }
167 buff := new(bytes.Buffer)
168 err = binary.Write(buff, binary.BigEndian, tcpH)
169 checkError(err)
170 for i := range op {
171 binary.Write(buff, binary.BigEndian, op[i].Kind)
172 binary.Write(buff, binary.BigEndian, op[i].Length)
173 binary.Write(buff, binary.BigEndian, op[i].Data)
174 }
175 binary.Write(buff, binary.BigEndian, [6]byte{})
176 data := buff.Bytes()
177 checkSum := CheckSum(data, ipstr2Bytes(laddr), ipstr2Bytes(raddr))
178 //fmt.Printf( “CheckSum 0x%Xn” , checkSum)
179 tcpH.ChkSum = checkSum
180 buff = new(bytes.Buffer)
181 binary.Write(buff, binary.BigEndian, tcpH)
182 for i := range op {
183 binary.Write(buff, binary.BigEndian, op[i].Kind)
184 binary.Write(buff, binary.BigEndian, op[i].Length)
185 binary.Write(buff, binary.BigEndian, op[i].Data)
186 }
187 binary.Write(buff, binary.BigEndian, [6]byte{})
188 data =https://www.cnblogs.com/darecy/p/ buff.Bytes()
189 //fmt.Printf( “% Xn” , data)
190 _, err = conn.Write(data)
191 checkError(err)
192 }
193 func recvSynAck(laddr, raddr string, res chan<- *scanResult) error {
194 listenAddr, err := net.ResolveIPAddr( “ip4” , laddr) // 決議域名為ip
195 checkError(err)
196 conn, err := net.ListenIP( “ip4:tcp” , listenAddr)
197 defer conn.Close()
198 checkError(err)
199 for {
200 buff := make([]byte, 1024)
201 _, addr, err := conn.ReadFrom(buff)
202 if err != nil {
203 continue
204 }
205 if addr.String() != raddr || buff[13] != 0x12 {
206 continue
207 }
208 var port uint16
209 binary.Read(bytes.NewReader(buff), binary.BigEndian, &port)
210 res <- &scanResult{
211 Port: port,
212 Opened: true ,
213 }
214 }
215 }
216 func ipstr2Bytes(addr string) [4]byte {
217 s := strings.Split(addr, “.” )
218 b0, _ := strconv.Atoi(s[0])
219 b1, _ := strconv.Atoi(s[1])
220 b2, _ := strconv.Atoi(s[2])
221 b3, _ := strconv.Atoi(s[3])
222 return [4]byte{byte(b0), byte(b1), byte(b2), byte(b3)}
223 }
224 func random(min, max int) int {
225 return rand.Intn(max-min) + min
226 }
227 func interfaceAddress(ifaceName string ) string {
228 iface, err:= net.InterfaceByName(ifaceName)
229 if err != nil {
230 panic(err)
231 }
232 addr, err := iface.Addrs()
233 if err != nil {
234 panic(err)
235 }
236 addrStr := strings.Split(addr[0].String(), “/” )[0]
237 return addrStr
238 }
239 func portSplit(portRange *string) (uint16, uint16) {
240 ports := strings.Split(*portRange, “-“ )
241 minPort, err := strconv.ParseUint(ports[0], 10, 16)
242 if err !=nil {
243 panic(err)
244 }
245 maxPort, err := strconv.ParseUint(ports[1], 10, 16)
246 if err != nil {
247 panic(err)
248 }
249 if minPort > maxPort {
250 panic(errors.New( “minPort must greater than maxPort” ))
251 }
252 return uint16(minPort), uint16(maxPort)
253 }
254 func main () { }

  然后利用go自身的build命令,將其打包成.so庫:

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

  打包后會得到一個tcp_syn_scan.so和一個tcp_syn_scan.h,然后利用下面的python代碼就可以呼叫Go代碼中的Scan()函式了,創建一個tcp_syn_scan.py檔案,

1 #! -*- coding:utf-8 -*-
2 from ctypes import *
3 lib = cdll.LoadLibrary(u ‘./scan.so’ )
4 lib.Scan( “14.215.177.38” , “1-1024” , “eth0” ) # ip,埠范圍,網卡

  代碼運行結果:

  說明:相當原生的go,利用python去呼叫go會損耗一些性能,但總體上還可以,

  后記

  本文結論就是可以利用go開發掃描模塊(性能更佳),并結合python呼叫,

  本文代碼專案地址:https://github.com/tengzhangchao/PortScan

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/80858.html

標籤:Python

上一篇:Maximum execution time of 30 seconds exceeded解決辦法【轉】

下一篇:selenium的彈框操作

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more