TCP協定端口掃描
要使用TCP協定去完成端口掃描,肯定是需要了解TCP協定通信過程和原理才能完成的
TCP協定的特點
1
面向連接配接的:使用TCP協定通信的雙方必須先建立連接配接,然後才能開始資料的讀寫,TCP連接配接是
全雙工的,即雙方的資料讀寫可以通過一個連接配接進行。完成資料交換之後,通信雙方都必須斷開
連接配接以釋放資源。TCP協定的這種連接配接是一對一的,是以基于廣播和多點傳播(目标是多個主機位址)
的應用程式不能使用TCP服。而無連接配接協定UDP則非常适合于廣播和多點傳播。
流式服務:TCP的位元組流服務的表現形式就展現在,發送端執行的寫操作數和接收端執行的讀操作
次數之間沒有任何數量關系,當發送端應用程式連續執行多次寫操作的時,TCP子產品先将這些資料
放入TCP發送緩沖區中。當TCP子產品真正開始發送資料的時候,發送緩沖區中這些等待發送的資料
可能被封裝成一個或多個TCP封包段發出。
TCP通過檢驗和,序列号,确認應答,重發控制,連接配接管理以及視窗控制等機制實作可靠性傳輸。
1
TCP三向交握介紹
所謂三次握手(Three-way Handshake),是指建立一個 TCP 連接配接時,需要用戶端和伺服器總共發
送3個包。三次握手的目的是連接配接伺服器指定端口,建立 TCP 連接配接,并同步連接配接雙方的序列号和确認号,
交換 TCP 視窗大小資訊。在 socket 程式設計中,用戶端執行 connect() 時。将觸發三次握手。
第一次握手(SYN=1, seq=x):用戶端發送一個 TCP 的 SYN 标志位置1的包,指明用戶端打算連接配接的伺服器
的端口,以及初始序号 X,儲存在標頭的序列号(Sequence Number)字段裡。
發送完畢後,用戶端進入 SYN_SEND 狀态。
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):伺服器發回确認包(ACK)應答。即 SYN 标志位和
ACK 标志位均為1。伺服器端選擇自己 ISN 序列号,放到 Seq 域裡,同時将确認序号(Acknowledgement Number)
設定為客戶的 ISN 加1,即X+1。 發送完畢後,伺服器端進入 SYN_RCVD 狀态。
第三次握手(ACK=1,ACKnum=y+1)用戶端再次發送确認包(ACK),SYN 标志位為0,ACK 标志位為1,
并且把伺服器發來ACK 的序号字段+1,放在确定字段中發送給對方,并且在資料段放寫ISN的+1發送完
畢後,用戶端進入 ESTABLISHED 狀态,當伺服器端接收到這個包時,也進入 ESTABLISHED 狀态,TCP 握手結束。
TCP協定常見标志位
FIN:斷開連接配接,對應值1
1
SYN:同步信号,用于連接配接,對應值2
1
RST:重置連接配接,對應值4
1
ACK:确認資訊,對應值位16
1
掃描原理
通過構造TCP 标志位(flags)為SYN的資料包,向目标主機端口請求連接配接
1
目标主機端口如果開放的話,收到SYN資料包請求建立三次握手,就會回複SYN+ACK同意建立連接配接。
1
判斷目标主機響應資料包中是否存在SYN+ACK标志位存在
1
代碼部分
掃描函數
構造TCP标志位為SYN的資料包,向目标主機端口發送
1
判斷響應包鐘是否存在SYN+ACK,存在即端口開放
1
def scan(ip,port):
try:
packet = IP(dst=ip)/TCP(dport=port, flags="S")# 構造标志位為syn的資料包
result = sr1(packet,timeout=0.5, verbose=0)
if int(result[TCP].flags) == 18:
# 通過判斷響應的資料包中,是否存在第二次握手Ack+syn标志位,存在即端口開放
time.sleep(0.1)
print(ip, "TCP" , port, "open")
# 注意這裡如果使用+号進行字元串拼接的話會導緻報錯,使用逗号即可拼接
return
except:
pass
參數擷取
擷取使用者輸入的參數,并執行個體化
1
判斷使用者是掃描i單個ip位址還是網段亦是讀取ip位址檔案
1
調用多線程去執行掃描函數
def main():
# 如果沒有輸出參數就會輸出幫助資訊
parser = OptionParser("Usage program -i -n -p ")
parser.add_option("-i", '--host', type="string",dest="tgtIP",help="specify target host or website")
parser.add_option("-n","--network", type="string",dest="tgtNetwork",help="specify target Network")
parser.add_option("-f", "--addressfile", type="string", dest="tgtFile", help="specify target addressfile")
parser.add_option("-p","--port", type="string",dest="tgtPorts",help="specify target port separated by comma")
options,args = parser.parse_args()# 執行個體化使用者輸入的參數
tgtIP = options.tgtIP
tgtNetwork = options.tgtNetwork # 網段
tgtFile = options.tgtFile
tgtPorts = options.tgtPorts
tgtPorts = tgtPorts.split(",") # 将使用者輸入的多個端口以逗号分割生成清單
if tgtPorts is None or tgtNetwork is None and tgtIP is None and tgtFile is None :# 判斷使用者是否輸入參數
print(parser.usage)# 如果沒有輸入參數則輸出幫助資訊,然後退出程式
exit(0)
if tgtIP:# 輸入單個ip位址時的操作
for p in tgtPorts:
port = int(p)
t = Thread(target=scan,args=(tgtIP,port))
t.start()
if tgtNetwork:# 輸入整個網段時的操作
prefix = tgtNetwork.split(".")[0] + "." + tgtNetwork.split(".")[1] + "." + tgtNetwork.split(".")[2] + "."# 将使用者輸入的網段提取提取前三位當作字首
for i in range(1,255):
ip = prefix + str(i)# 和字首結合形成網段内所有的位址
for p in tgtPorts:
port = int(p)
t = Thread(target=scan, args=(ip,port))
t.start()
if tgtFile:# 如果時位址檔案則進行的操作
if not os.path.exists(tgtFile):# 判斷檔案是否存在
print("File not founFd")
sys.exit()
with open(tgtFile,"r") as f:# 讀取位址檔案
for i in f.readlines():
ip = i.strip()# 讀取使用者位址檔案的位址,并去點換行空格
for p in tgtPorts:
port = p.strip()
port = int(port)
t = Thread(target=scan,args=(ip,port))
t.start()# 多線程掃描
整體代碼
import os
import time
from scapy.all import *
from optparse import OptionParser
from threading import Thread
def scan(ip,port):
try:
packet = IP(dst=ip)/TCP(dport=port, flags="S")# 構造标志位為syn的資料包
result = sr1(packet,timeout=0.5, verbose=0)
if int(result[TCP].flags) == 18:
# 通過判斷響應的資料包中,是否存在第二次握手Ack+syn标志位,存在即端口開放
time.sleep(0.1)
print(ip, "TCP" , port, "open")
# 注意這裡如果使用+号進行字元串拼接的話會導緻報錯,使用逗号即可拼接
return
except:
pass
def main():
# 如果沒有輸出參數就會輸出幫助資訊
parser = OptionParser("Usage program -i -n -p ")
parser.add_option("-i", '--host', type="string",dest="tgtIP",help="specify target host or website")
parser.add_option("-n","--network", type="string",dest="tgtNetwork",help="specify target Network")
parser.add_option("-f", "--addressfile", type="string", dest="tgtFile", help="specify target addressfile")
parser.add_option("-p","--port", type="string",dest="tgtPorts",help="specify target port separated by comma")
options,args = parser.parse_args()# 執行個體化使用者輸入的參數
tgtIP = options.tgtIP
tgtNetwork = options.tgtNetwork # 網段
tgtFile = options.tgtFile
tgtPorts = options.tgtPorts
tgtPorts = tgtPorts.split(",") # 将使用者輸入的多個端口以逗号分割生成清單
if tgtPorts is None or tgtNetwork is None and tgtIP is None and tgtFile is None :# 判斷使用者是否輸入參數
print(parser.usage)# 如果沒有輸入參數則輸出幫助資訊,然後退出程式
exit(0)
if tgtIP:# 輸入單個ip位址時的操作
for p in tgtPorts:
port = int(p)
t = Thread(target=scan,args=(tgtIP,port))
t.start()
if tgtNetwork:# 輸入整個網段時的操作
prefix = tgtNetwork.split(".")[0] + "." + tgtNetwork.split(".")[1] + "." + tgtNetwork.split(".")[2] + "."# 将使用者輸入的網段提取提取前三位當作字首
for i in range(1,255):
ip = prefix + str(i)# 和字首結合形成網段内所有的位址
for p in tgtPorts:
port = int(p)
t = Thread(target=scan, args=(ip,port))
t.start()
if tgtFile:# 如果時位址檔案則進行的操作
if not os.path.exists(tgtFile):# 判斷檔案是否存在
print("File not found")
sys.exit()
with open(tgtFile,"r") as f:# 讀取位址檔案
for i in f.readlines():
ip = i.strip()# 讀取使用者位址檔案的位址,并去點換行空格
for p in tgtPorts:
port = p.strip()
port = int(port)
t = Thread(target=scan,args=(ip,port))
t.start()# 多線程掃描
if __name__ == '__main__':
main()
運作效果