• 正文
    • 一、題目總結
    • 二、TCP基礎知識點
    • 三、 抓包舉例
    • 四、 socket
    • 五、libpcap
    • 六、設計方案
  • 相關推薦
申請入駐 產業(yè)圖譜

粉絲提問:設計和實現(xiàn)一個TCP協(xié)議半連接的端口掃描程序

02/06 15:30
272
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

某學生粉絲發(fā)來問題:

這個題目一看就知道這位同學是網(wǎng)絡安全相關專業(yè)。

很多粉絲以為彭老師知識搞驅動的,但是其實作為一個擁有多篇網(wǎng)絡協(xié)議專利的老鳥,網(wǎng)絡知識還是比較擅長的!

應用層套接字、組網(wǎng)、網(wǎng)卡驅動都有所涉獵,目前還缺Linux內核協(xié)議棧這塊沒深入研究,后期會補上。

一、題目總結

題目要求是掃描所有TCP半連接的端口,需要實現(xiàn)的功能如下:

      1. 攻擊方啟動任務1,循環(huán)向指定

    服務器端+端口

      1. 發(fā)送SYN數(shù)據(jù)包,(端口從0開始遞增)如果該服務器上有服務打開了這個端口,就會回復

    SYN+ACK

      1. ,此時服務端進入SYN_RCVD狀態(tài),攻擊方啟動任務2,掃描收到的所有

    SYN+ACK

    數(shù)據(jù)包,如果客戶端收到SYN+ACK,那么說明服務器改端口打開,任務2就可以將所有打開的端口信息打印出來
    任務1使用socket API任務2使用pcap庫

二、TCP基礎知識點

解決這個問題必須掌握以下幾個知識點:

    什么是TCPTCP3次握手什么是半連接TCP、IP協(xié)議頭如何使用Libpcap庫線程、進程

整體來說對網(wǎng)絡知識的基本功要求還是很高的。關于TCP/IP協(xié)議棧這些基礎知識點的本文就不列舉了。

下面主要強化下這個題目涉及的TCP的知識點。

1.TCP

首先就是我們必須了解TCP協(xié)議頭:

序列號:在建立連接時由計算機生成的隨機數(shù)作為其初始值,通過 SYN 包傳給接收端主機,每發(fā)送一次數(shù)據(jù),就「累加」一次該「數(shù)據(jù)字節(jié)數(shù)」的大小。用來解決網(wǎng)絡包亂序問題

確認應答號:指下一次「期望」收到的數(shù)據(jù)的序列號,發(fā)送端收到這個確認應答以后可以認為在這個序號以前的數(shù)據(jù)都已經(jīng)被正常接收。用來解決不丟包的問題

控制位:ACK:該位為 1 時,「確認應答」的字段變?yōu)橛行?,TCP 規(guī)定除了最初建立連接時的 SYN 包之外該位必須設置為 1
RST:該位為 1 時,表示 TCP 連接中出現(xiàn)異常必須強制斷開連接
SYN:該位為 1 時,表示希望建立連接,并在其「序列號」的字段進行序列號初始值的設定
FIN:該位為 1 時,表示今后不會再有數(shù)據(jù)發(fā)送,希望斷開連接。當通信結束希望斷開連接時,通信雙方的主機之間就可以相互交換 FIN 位置為 1 的 TCP 段

與本題目相關的是最主要字段是控制位,控制位的操作最主要體現(xiàn)在3次握手和4次握手。

2. tcp三次握手

開始客戶端和服務器都處于CLOSED狀態(tài),然后服務端開始監(jiān)聽某個端口,進入LISTEN狀態(tài):

    第一次握手(SYN=1, seq=x),發(fā)送完畢后,客戶端進入 SYN_SENT 狀態(tài)第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1), 發(fā)送完畢后,服務器端進入 SYN_RCVD 狀態(tài)第三次握手(ACK=1,ACKnum=y+1),發(fā)送完畢后,客戶端進入 ESTABLISHED 狀態(tài),當服務器端接收到這個包時,也進入 ESTABLISHED 狀態(tài),TCP 握手,即可以開始數(shù)據(jù)傳輸

3. tcp四次揮手

四次揮手過程:

    客戶端打算關閉連接,此時會發(fā)送一個 TCP 首部 FIN 標志位被置為 1 的報文,也即 FIN 報文,之后客戶端進入 FIN_WAIT_1 狀態(tài)服務端收到該報文后,就向客戶端發(fā)送 ACK 應答報文,接著服務端進入 CLOSED_WAIT 狀態(tài)客戶端收到服務端的 ACK 應答報文后,之后進入 FIN_WAIT_2 狀態(tài)等待服務端處理完數(shù)據(jù)后,也向客戶端發(fā)送 FIN 報文,之后服務端進入 LAST_ACK 狀態(tài)客戶端收到服務端的 FIN 報文后,回一個 ACK 應答報文,之后進入 TIME_WAIT 狀態(tài)服務器收到了 ACK 應答報文后,就進入了 CLOSE 狀態(tài),至此服務端已經(jīng)完成連接的關閉客戶端在經(jīng)過 2MSL 一段時間后,自動進入 CLOSE 狀態(tài),至此客戶端也完成連接的關閉

更詳細 tcp知識點可以參考下面文章:《28 張圖,一次性說清楚 TCP》

4.TCP狀態(tài)

TCP協(xié)議狀態(tài)遷移圖如下:

    CLOSED:表示初始狀態(tài)LISTEN:表示服務器端的某個SOCKET處于監(jiān)聽狀態(tài),可以接受連接了SYN_RCVD:表示接收到了SYN報文SYN_SENT:表示客戶端已發(fā)送SYN報文ESTABLISHED:表示連接已經(jīng)建立了TIME_WAIT:表示收到了對方的FIN報文,并發(fā)送出了ACK報文,就等2MSL后即可回到CLOSED可用狀態(tài)了CLOSING:表示你發(fā)送FIN報文后,并沒有收到對方的ACK報文,反而卻也收到了對方的FIN報文。如果雙方幾乎在同時* close一個SOCKET的話,那么就出現(xiàn)了雙方同時發(fā)送FIN報 文的情況,也即會出現(xiàn)CLOSING狀態(tài),表示雙方都正在關閉SOCKET連接CLOSE_WAIT:表示在等待關閉

5. 半連接/全連接

TCP半連接及全連接狀態(tài),在服務器的性能分析中,起著重要的作用,它通常是反應服務端的處理能力

1)半連接隊列(syn queue)

客戶端發(fā)送SYN包,服務端收到后回復SYN+ACK后,服務端進入SYN_RCVD狀態(tài),這個時候的socket會放到半連接隊列。

2)全連接隊列(accept queue)

當服務端收到客戶端的ACK后,socket會從半連接隊列移出到全連接隊列。當調用accpet函數(shù)的時候,會從全連接隊列的頭部返回可用socket給用戶進程。

全連接隊列中存放的是已完成TCP三次握手的過程,等待被處理的連接,在客戶端及服務端的狀態(tài)均為 ESTABLISHED

三、 抓包舉例

要想學好網(wǎng)絡,抓包工具是必須掌握的。

下圖是一口君通過抓包工具抓取的一個完整的 tcp 3次握手 + HTTP GET請求 + 4次握手 的完整通信數(shù)據(jù)包。

如何抓包,可以參考下面文章:

《一文包你學會網(wǎng)絡數(shù)據(jù)抓包》

B站也有詳細的教學視頻:

《教你如何抓取網(wǎng)絡中的數(shù)據(jù)包!黑客必備技能》

https://www.bilibili.com/video/BV1xr4y1T7cT/?vd_source=07570058a62e0e8a6cf489efac35cfec

四、 socket

關于socket API內容,大家可以的參考下面這篇文章《socket到底是什么?》

五、libpcap

libpcap是一個網(wǎng)絡數(shù)據(jù)包捕獲函數(shù)庫,功能非常強大,Linux下著名的tcpdump就是以它為基礎的。

libpcap主要由兩部分組成:網(wǎng)絡分接頭(network tap)和數(shù)據(jù)過濾器(packet filter)。

網(wǎng)絡分接頭從網(wǎng)絡設備驅動程序中收集數(shù)據(jù)進行拷貝,過濾器決定是否接收該數(shù)據(jù)包。

libpcap利用BSD packet filter(BPF)算法對網(wǎng)卡接收到的鏈路層數(shù)據(jù)包進行過濾。

libpcap的包捕獲機制就是在數(shù)據(jù)鏈路層加一個旁路處理。當一個數(shù)據(jù)包到達網(wǎng)絡接口時,libpcap首先利用已經(jīng)創(chuàng)建的套接字從鏈路層驅動程序中獲得該數(shù)據(jù)包的拷貝,再通過Tap函數(shù)將數(shù)據(jù)包發(fā)給BPF過濾器。

BPF過濾器根據(jù)用戶已經(jīng)定義好的過濾規(guī)則對數(shù)據(jù)包進行逐一匹配,匹配成功則放入內核緩沖區(qū),并傳遞給用戶緩沖區(qū),匹配失敗則直接丟棄。

如果沒有設置過濾規(guī)則,所有數(shù)據(jù)包都將放入內核緩沖區(qū),并傳遞給用戶層緩沖區(qū)。

1. libpcap安裝

    在線安裝
sudo?apt-get??install??libpcap-dev

這種適合有網(wǎng)絡的朋友

如何無法安裝嘗試更新下源:

sudo?apt-get?update
    離線編譯安裝
http://www.tcpdump.org/#latest-release

然后解壓
tar?zxvf?libpcap-1.10.3.tar.gz??
cd?libpcap-1.10.3
./configure
sudo?make
sudo?make?install

2. Libpcap的抓包流程:

    查找網(wǎng)絡設備:目的是發(fā)現(xiàn)可用的網(wǎng)卡,實現(xiàn)的函數(shù)為pcap_lookupdev(),如果當前有多個網(wǎng)卡,函數(shù)就會返回一個網(wǎng)絡設備名的指針列表。打開網(wǎng)絡設備:利用上一步中的返回值,可以決定使用哪個網(wǎng)卡,通過函數(shù)pcap_open_live()打開網(wǎng)卡,返回用于捕捉網(wǎng)絡數(shù)據(jù)包的秒數(shù)字。獲得網(wǎng)絡參數(shù):這里是利用函數(shù)pcap_lookupnet(),可以獲得指定網(wǎng)絡設備的IP地址和子網(wǎng)掩碼。編譯過濾策略:Lipcap的主要功能就是提供數(shù)據(jù)包的過濾,函數(shù)pcap_compile()來實現(xiàn)。設置過濾器:在上一步的基礎上利用pcap_setfilter()函數(shù)來設置。利用回調函數(shù),捕獲數(shù)據(jù)包:函數(shù)pcap_loop()和pcap_dispatch()來抓去數(shù)據(jù)包,也可以利用函數(shù)pcap_next()和pcap_next_ex()來完成同樣的工作。關閉網(wǎng)絡設備:pcap_close()函數(shù)關系設備,釋放資源。

3. 數(shù)據(jù)結構說明:

struct?pcap_pkthdr?{
????struct?timeval?ts;??/*?time?stamp?*/
????bpf_u_int32?caplen;?/*?抓到的數(shù)據(jù)包實際長度?*/
????bpf_u_int32?len;????/*數(shù)據(jù)包的長度?*/
};

4. libcap庫函數(shù)

關于libcap的詳細講解,后續(xù)會出文章,

本文只講幾個重要的函數(shù)。

    打開網(wǎng)絡接口
//這個函數(shù)會返回指定接口的pcap_t類型指針,后面的所有操作都要使用這個指針。
pcap_t?*?pcap_open_live(const?char?*?device,?int?snaplen,?int?promisc,?int?to_ms,?char?*?errbuf)
device:網(wǎng)絡接口字符串,可以直接使用硬編碼,比如eth0。
snaplen:對于每個數(shù)據(jù)包,從開頭要抓多少個字節(jié),我們可以設置這個值來只抓每個數(shù)據(jù)包的頭部,而不關心具體的內容。典型的以太網(wǎng)幀長度是1518字節(jié),但其他的某些協(xié)議的數(shù)據(jù)包會更長一點,但任何一個協(xié)議的一個數(shù)據(jù)包長度都必然小于65535個字節(jié)。
promisc:指定是否打開混雜模式(Promiscuous?Mode),0表示非混雜模式,任何其他值表示混合模式。如果要打開混雜模式,那么網(wǎng)卡必須也要打開混雜模式,可以使用如下的命令打開eth0混雜模式:ifconfig eth0?
to_ms:抓包時長單位為毫秒,0標示一直等待。
errbuf:?輸出參數(shù),打開網(wǎng)絡接口失敗原因。
    打開離線的pcap文件
pcap_t?*?pcap_open_offline?(const?char?*fname,?char?*errbuf)
fname :文件名稱。
errbuf :打開失敗的錯誤信息。
    抓包函數(shù)
int?pcap_loop(pcap_t?*?p,?int?cnt,?pcap_handler?callback,?u_char?*?user)
p:?打開的pcap_t類型指針。
cnt:一共抓多少個包,如果為負數(shù)就一直循環(huán)。
callback:回調函數(shù)指針
user:傳遞給回調函數(shù)的參數(shù)。
void?callback(u_char?*?userarg,?const?struct?pcap_pkthdr?*?pkthdr,?const?u_char?*?packet)
userarg:是pcap_loop的最后一個參數(shù),當收到足夠數(shù)量的包后pcap_loop會調用callback回調函數(shù),同時將pcap_loop()的user參數(shù)傳遞給它
pkthdr:?抓到的報文頭信息。
packet:收到的包的數(shù)據(jù)。
    過濾函數(shù)編譯
int?pcap_compile(pcap_t?*?p,?struct?bpf_program?*?fp,?char?*?str,?int?optimize,?bpf_u_int32?netmask)
//fp:這是一個傳出參數(shù),存放編譯后的bpf
//str:過濾表達式
//optimize:是否需要優(yōu)化過濾表達式
//metmask:簡單設置為0即可

    設置過濾函數(shù)
int?pcap_setfilter(pcap_t?*?p,??struct?bpf_program?*?fp)
//參數(shù)fp就是pcap_compile()的第二個參數(shù),存放編譯后的bpf
    釋放網(wǎng)絡接口
void?pcap_close(pcap_t?*?p)
//該函數(shù)用于關閉pcap_open_live()獲取的pcap_t的網(wǎng)絡接口對象并釋放相關資源。
    打開網(wǎng)絡包保存文件
pcap_dumper_t?*?pcap_dump_open?(pcap_t?*p,?const?char?*fname)
?//p:是我們已經(jīng)打開的網(wǎng)絡設備,從這個設備接收數(shù)據(jù)包。
??// fname:是我們要寫入的文件名,隨便起。
??//return:?如果出錯,會返回NULL??梢越璐藱z查這個文件有沒有打開。
    將網(wǎng)絡包寫入文件
void?pcap_dump?(u_char?*user,?const?struct?pcap_pkthdr?*h,?const?u_char?*sp)
user:就是文件描述符dumpfp,只不過要做一下類型轉換。
由于這個函數(shù)一般在pcap_loop()的函數(shù)指針所指向的packet_handler中使用,所以packet_handler中的user就是這里的user。
?h:就是pkt_header
    網(wǎng)絡包文件關閉
pcap_dump_close(pcap_dumper_t?*?t);

5. libcap過濾規(guī)則

一些過濾表達式的例子如下:

    只接收源ip地址是192.168.1.177的數(shù)據(jù)包
src?host?192.168.1.177
    只接收tcp/udp的目的端口是80的數(shù)據(jù)包
dst?port?80
    只接收不使用tcp協(xié)議的數(shù)據(jù)包
not?tcp
    只接收SYN標志位置位且目標端口是22或23的數(shù)據(jù)包(tcp首部開始的第13個字節(jié))
tcp[13]?==?0x02?and?(dst?port?22?or?dst?port?23)
    只接收icmp的ping請求和ping響應的數(shù)據(jù)包
icmp[icmptype]?==?icmp-echoreply?or?icmp[icmptype]?==?icmp-echo
    只接收以太網(wǎng)mac地址是00:e0:09:c1:0e:82的數(shù)據(jù)包
ether?dst?00:e0:09:c1:0e:82

只接收ip的ttl=5的數(shù)據(jù)包(ip首部開始的第8個字節(jié))

ip[8]?==?5

本例只抓取ip地址為本地IP的數(shù)據(jù)包,然后程序再對數(shù)據(jù)包協(xié)議頭進行解析:

host?192.168.0.113

六、設計方案

實現(xiàn)原理:

atach、cap進程運行在ubuntu中,要攻擊的目的終端可以使網(wǎng)絡中任意設備,只需要能ping通即可。本例在windows上測試,采用橋接模式將ubuntu的網(wǎng)口和windows的網(wǎng)口橋接起來。

atach進程主要功能:

    創(chuàng)建tcp套接字設置需要攻擊的終端的ip+port,然后執(zhí)行connect函數(shù)connect成功,說明對方該端口可以使用修改port值,重復前面3個步驟

cap進程主要功能:

    1. 通過eth0,抓取指定規(guī)則:

host 192.168.0.116

    數(shù)據(jù)包解析出以太頭、tcp頭,ip頭、tcp頭,判斷tcp頭中sync+ack位為1的所有數(shù)據(jù)包打印出步驟2過濾出來的數(shù)據(jù)包

代碼流程:

七、測試

1. 環(huán)境:

windows ip:192.168.0.116
ubuntu ip:192.168.0.113

2. 文件:

peng@ubuntu:~/work/test/pcap$?ls
atach??header.c????????libpcap-1.10.3.tar.gz??cap.c
cap????libpcap-1.10.3??atach.c?????????????protocol.h

其中atach是上攻擊方,用于向指定ip發(fā)送sync包
cap 用于檢測所有網(wǎng)卡收到的sync+ack數(shù)據(jù)包
程序運行在ubuntu中。

3. 啟動網(wǎng)絡調試助手

在windows上啟動網(wǎng)絡調試助手,

建立幾個Tcp Server,端口號分別為55、56、57

在這里插入圖片描述

4. 啟動程序

1)首先啟動cap
peng@ubuntu:~/work/test/pcap$?sudo?./cap?192.168.0.116
found?device:?eth0
netaddr:0000a8c0
try?to?open?device?eth0
filter:host?192.168.0.116
2)啟動攻擊程序atach

需要新開啟一個終端。

peng@ubuntu:~/work/test/pcap$?./atach?192.168.0.116

5. 運行截圖如下:

右邊log可見,列舉出了所有可以訪問的端口,包括55、56、57。

注意:那個單詞atach故意少了一個t,否則編譯不過去:大家可以試試你們的編譯器,刑不刑!

八、代碼

代碼已經(jīng)同步到gitee,地址如下:

https://gitee.com/yikoulinux/pcap.git

更多嵌入式、Linux、網(wǎng)絡知識,后臺留言加一口君好友!

相關推薦

登錄即可解鎖
  • 海量技術文章
  • 設計資源下載
  • 產業(yè)鏈客戶資源
  • 寫文章/發(fā)需求
立即登錄

公眾號『一口Linux』號主彭老師,擁有15年嵌入式開發(fā)經(jīng)驗和培訓經(jīng)驗。曾任職ZTE,某研究所,華清遠見教學總監(jiān)。擁有多篇網(wǎng)絡協(xié)議相關專利和軟件著作。精通計算機網(wǎng)絡、Linux系統(tǒng)編程、ARM、Linux驅動、龍芯、物聯(lián)網(wǎng)。原創(chuàng)內容基本從實際項目出發(fā),保持原理+實踐風格,適合Linux驅動新手入門和技術進階。