tcpdump/wireshark 抓包及分析

it運維翁 發佈 2024-03-29T11:14:40.006949+00:00

1 Pull Docker 鏡像。這裡可以看到:#4: client 向 server 發起 HTTP GET 請求,請求路徑為根路徑,這個 packet 長度為 74 字節。

1 基礎環境準備

為方便大家跟著上手練習,本文將搭建一個容器環境。

1.1 Pull Docker 鏡像

$ sudo docker pull alpine:3.8

1.2 運行容器

$ sudo docker run -d --name ctn-1 alpine:3.8 sleep 3600d
$ sudo docker ps
CONTAINER ID    IMAGE        COMMAND         CREATED        STATUS          PORTS  NAMES
233bc36bde4b    alpine:3.8   "sleep 3600d"   1 minutes ago  Up 14 minutes           ctn-1

進入容器:

$ sudo docker exec -it ctn-1 sh

查看容器網絡信息:

/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:09
          inet addr:172.17.0.9  Bcast:0.0.0.0  Mask:255.255.0.0

1.3 安裝 tcpdump

/ # apk update
/ # apk add TCPdump

2 HTTP/TCP 抓包

接下來我們用 wget 獲取一個網站的首頁文件(index.html),同時 tcpdump 抓包,對抓 到的網絡流量進行分析。

2.1 HTTP 請求:下載測試頁面

example.com 是一個測試網站,wget 是一個 linux 命令行工 具,可以下載網絡文件。

如下命令可以下載一個 example.com 網站的首頁文件 index.html:

/ # wget http://example.com
Connecting to example.com (93.184.216.34:80)
index.html           100% |*****************************|  1270   0:00:00 ETA

雖然這看起來極其簡單,但背後卻涵蓋了很多複雜的過程,例如:

  1. 域名查找:通過訪問 DNS 服務查找 example.com 伺服器對應的 IP 地址
  2. TCP 連接參數初始化:臨時埠、初始序列號的選擇等等
  3. 客戶端(容器)通過 TCP 三次握手協議和伺服器 IP 建立 TCP 連接
  4. 客戶端發起 HTTP GET 請求
  5. 伺服器返回 HTTP 響應,包含頁面數據傳輸
  6. 如果頁面超過一個 MTU,會分為多個 packet 進行傳輸(後面會看到,確實超過 MTU 了)
  7. TCP 斷開連接的四次揮手

2.2 抓包:打到標準輸出

用下面的 tcpdump 命令抓包,另一窗口執行 wget http://example.com,能看到如下類 似的輸出。為了方便後面的討論,這裡將一些欄位去掉了,並做了適當的對齊:

/ # tcpdump -n -S -i eth0 host example.com
1  02:52:44.513700 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [S] , seq 3310420140,                            length 0
2  02:52:44.692890 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [S.], seq 1353235534,            ack 3310420141, length 0
3  02:52:44.692953 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353235535, length 0
4  02:52:44.693009 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [P.], seq 3310420141:3310420215, ack 1353235535, length 74: HTTP: GET / HTTP/1.1
5  02:52:44.872266 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] ,                            ack 3310420215, length 0
6  02:52:44.873342 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , seq 1353235535:1353236983, ack 3310420215, length 1448: HTTP: HTTP/1.1 200 OK
7  02:52:44.873405 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353236983, length 0
8  02:52:44.874533 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [P.], seq 1353236983:1353237162, ack 3310420215, length 179: HTTP
9  02:52:44.874560 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353237162, length 0
10 02:52:44.874705 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [F.], seq 3310420215,            ack 1353237162, length 0
11 02:52:45.053732 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] ,                            ack 3310420216, length 0
12 02:52:45.607825 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [F.], seq 1353237162,            ack 3310420216, length 0
13 02:52:45.607869 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353237163, length 0

參數說明:

  • -n:列印 IP 而不是 hostname,列印埠號而不是協議(例如列印 80 而不是 http)
  • -S:列印絕對時間戳
  • -i eth0:指定從 eth0 網卡抓包
  • host example.com:抓和 example.com 通信的包(雙向)

更多 tcpdump 的常用命令,可以參考tcpdump: An Incomplete Guide。

2.3 抓包:存文件

-w 命令可以將抓到的包寫到文件,注意這和用重定向方式將輸出寫到文件是不同的。 後者寫的只是標準輸出列印的 LOG,而 -w 寫的是原始包。

/ # tcpdump -i eth0 host example.com -w example.pcap
^C
13 packets captured
13 packets received by filter
0 packets dropped by kernel

生成的 pcap 文件可以用 tcpdump 或者 wireshark 之類的網絡流量分析工具打開。

3 流量分析: tcpdump

如果不指定輸出的話,tcpdump 會直接將信息打到標準輸出,就是我們上面看到的那樣。從 這些輸出里,我們看到很多信息。

3.1 每列說明

第 1 列是為了討論方便而加的行號,實際的 tcpdump 輸出並沒有這一列。接下來將用 # 號加數字表示第幾個包,例如 #3 表示第 3 個包。

接下來依次為:

  • packet 時間戳,例如 02:52:44.513700 表示抓到這個包的時間是** 02 時 52 分 44 秒 513 毫秒**
  • packet 類型,這裡是 IP 包
  • 源 (SRC) IP 和埠,目的 (DST) IP 和埠
  • packet TCP flags,其中S 表示 syn 包. 表示 ack 包F 表示 fin 包P 表示 push 包(發送正常數據)
  • 序列號(seq)
  • 應答號(ack)
  • 包的 payload 長度
  • 包的部分內容(ASCII)

3.2 三次握手(1~3)

wget 是基於 HTTP 協議,因此它在下載文件之前,必定要和服務端建立一個連接。

而 TCP 建立連接的過程就是著名的三次握手 [4]:

  1. client -> server: SYN
  2. server -> client: SYN+ACK
  3. client -> server: ACK

我們可以看到,這剛好對應於前三個包:

1  02:52:44.513700 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [S] , seq 3310420140,                 length 0
2  02:52:44.692890 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [S.], seq 1353235534, ack 3310420141, length 0
3  02:52:44.692953 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                 ack 1353235535, length 0

第一次握手: SYN

#1 包含以下信息:

  1. 02:52:44.513700 時刻,客戶端主動向 server(93.184.216.34)發起一個 SYN 請求,請求建立連接
  2. 客戶端請求的服務端埠是 80(HTTP 服務默認 80 埠),客戶端使用的是臨時埠(大於 1024)41038
  3. #1 序列號是 3310420140,這是客戶端的初始序列號(客戶端和服務端分別維護自己的序列號,兩者沒有關係;另外,初始序列號是系統選擇的,一般不是 0)
  4. #1 length 為 0,因為 SYN 包不帶 TCP payload,所有信息都在 TCP header

第二次握手: SYN+ACK

#2 的 ack 是 3310420140,等於 #1 的 seq 加 1,這就說明,#2 是 #1 的應 答包。

這個應答包的特點:

  1. TCP flags 為 S.,即 SYN+ACK
  2. length 也是 0,說明沒有 payload
  3. seq 為 1353235534,這是服務端的初始序列號
  4. 到達 eth0 的時間為 02:52:44.692890,說明時間過了 18ms

第三次握手: ACK

同理,#3 的 ack 等於 #2 的 seq 加 1,說明 #3 是 #2 的應答包。

這個包的特點:

  1. TCP flags 為 .,即 ACK
  2. 長度為 0,說明沒有 TCP payload

至此,三次握手完成。

3.3 正常數據傳輸

三次握手完成後,client 和 server 開始 HTTP 通信,客戶端通過 HTTP GET 方法下載 index.html。

4  02:52:44.693009 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [P.], seq 3310420141:3310420215, ack 1353235535, length 74: HTTP: GET / HTTP/1.1
5  02:52:44.872266 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] ,                            ack 3310420215, length 0
6  02:52:44.873342 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] , seq 1353235535:1353236983, ack 3310420215, length 1448: HTTP: HTTP/1.1 200 OK
7  02:52:44.873405 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353236983, length 0
8  02:52:44.874533 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [P.], seq 1353236983:1353237162, ack 3310420215, length 179: HTTP
9  02:52:44.874560 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                            ack 1353237162, length 0

這裡可以看到:

  1. #4: client 向 server 發起 HTTP GET 請求,請求路徑為根路徑(/),這個 packet 長度為 74 字節
  2. #5: 發送了 ACK 包,對 #4 進行確認
  3. #6: 發送了 1448 字節的數據給 client
  4. #7: client 對 server 的 #6 進行應答
  5. #8: server 向 client 端繼續發送 179 字節數據
  6. #9: client 對 server 的 #8 進行應答

3.4 四次揮手

最後是四次揮手 [5]:

  1. client -> server: FIN (我們看到的是 FIN+ACK,這是因為這個 FIN 包除了正常的關閉連接功能之外,還被用於應答 client 發過來的前一個包)
  2. server -> client: ACK
  3. server -> client: FIN+ACK
  4. client -> server: ACK
10 02:52:44.874705 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [F.], seq 3310420215, ack 1353237162, length 0
11 02:52:45.053732 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [.] ,                 ack 3310420216, length 0
12 02:52:45.607825 IP 93.184.216.34.80 > 172.17.0.9.41038: Flags [F.], seq 1353237162, ack 3310420216, length 0
13 02:52:45.607869 IP 172.17.0.9.41038 > 93.184.216.34.80: Flags [.] ,                 ack 1353237163, length 0
關鍵字: