標籤:
現在的移動端應用幾乎都會通過網路請求來和伺服器互動,通過抓包來診斷和網路相關的bug是程式員的重要技能之一。抓包的手段有很多:針對http和https可以使用Charles設定代理來做,對於更廣泛的協議可以使用tcpdump或者wireshark。wireshark提供GUI,方便做深入全面的資料分析。tcpdump則輸出原始的包內容,好處是快速高效,之前寫過一篇簡單的紅包圖片的破解教程,就是用tcpdump來操作的。這篇文章主要介紹tcpdump的基本使用方法,閱讀目標是能基本掌握並運用tcpdump解決網路相關的問題。閱讀前提是對TCP/IP有初步的瞭解。
1.啟動tcpdump
1.1 iOS上啟動tcpdump
iOS裝置上啟動tcpdump比較方便。apple在mac上有個叫rvictrl的程式,可以通過iOS裝置的udid建立一個虛擬網卡,然後通過這個虛擬網卡監聽裝置上所有的網路流量。步驟如下:
擷取itunes擷取裝置udid
開啟終端(terminal),建立虛擬網卡
在終端輸入rvictl -s udid,建立虛擬網卡。
啟動tcpdump監控流量
在終端繼續輸入sudo tcpdump -i rvi0 -AAl,啟動tcpdump監控。
2.1 Android上啟動tcpdump
Android裝置沒辦法通過rvictl建立虛擬網卡,但是可以把tcpdump的可執行檔上傳到android裝置上,然後通過mac遠程登入android裝置運行tcpdump,前提是這台android裝置必須已經root過。步驟如下:
下載android版本的tcpdump
從這個連結可以下載到專門為android系統編譯的tcpdump版本。
通過adb將tcpdump上傳到android裝置
通過adb push將tcpdump檔案上傳到特定的目錄,這裡我們選擇/sdcard/data目錄。
在android裝置上運行tcpdump
通過adb shell登陸裝置,並執行tcpdump,最後一步執行./tcpdump即可。
2. 分析tcpdump輸出
經過上面的步驟成功運行tcpdump之後,接下來就可以分析輸出的網路包內容了,iOS裝置和Android裝置的輸出是一致的。我們先來解析下幾個基本的格式:
圖中紅色方框內的部分是一個ip包的詳細記錄,類似的紀錄還有好幾條。這裡我們著重分析第一條的各部分欄位含義。
14:37:41.615018 很簡單,是該包接收到的時間。
17.143.164.37.5223 是發送方的ip地址及連接埠號碼(5223是連接埠號碼)。
10.29.44.140.58036 是我iphone的ip地址及連接埠號碼。
Flags [P.] 是tcp包header部分的第14個位元組的P位。這個位元組所包含的幾個flag很重要,後面我會單獨詳細講解。這裡P位表示接受方需要馬上將包push到應用程式層。
seq 1:54 tcp包的seq號,1是起始值,54結束值。tcp之所以被認為是流,是因為tcp包所攜帶的每一個位元組都有標號(seq號)。1:54表明總共有54個位元組被接受,其中一個位元組是三向交握階段所使用,所以一共發送的長度是53位元組。
ack 101 tcp包的ack號,ack 101表明seq號為100的位元組已被確認收到,下一個期望接收的seq號從101開始。
win 255 win表示的是tcp包發送方,作為接受方還可以接受的位元組數。這裡win 255表明ip為17.143.164.37的主機還可以接受255個位元組。
options [nop,nop,…] options[…]表示的是該tcp包的options地區,nop是no opertion的縮寫,沒什麼實際用途,主要是用做padding,因為options地區按協議規定必須是4位元組的倍數。
options[… TS val 2381386761] ts val這個值是tcp包的時間戳記,不過這個時間戳記和裝置的系統時間沒啥關係,剛開始是隨機值,後面隨著系統時鐘自增長。這個時間戳記主要用處是seq序號越界從0重新開始後,可以確認包的順序。
options[… ecr 427050796] ts ecr這個值主要用來計算RTT。比如A發送一個tcp包給B,A會在包裡帶上TS val,B收到之後在ack包裡再把這個值原樣返回,A收到B的ack包之後再根據本地時鐘就可以計算出RTT了。這個值只在ack包裡有效,非ack包ecr的值就為0.
length 53 這個length是應用程式層傳過來的資料大小,不包括tcp的header。這個值和我們上面分析的seq 1:54是一致的。
以上就是一個基本的tcp包結構,大家可以按照上面的分析再把其他幾個包理解下。我們在做應用的時候面對的更多是http協議,但對一個http請求是怎麼通過tcp/ip分解成一個個的packet,然後怎麼在網路上穩定可靠的傳輸,要有個基本的印象。下面我們再看下tcpdump更多的功能,這些功能都是基於對tcp/ip協議的理解,遇到不理解的建議多google下相關的技術概念。
3. tcpdump知識拓展
再繼續深入tcpdump之前,先貼上一張tcp header格式圖,常看常新。
3.1 TCP Flags(tcp header第十四個位元組)
我們再仔細看下上面提到的flags概念,flags位於tcp header的第十四個位元組,包含8個位元位,也就是的CWR到FIN。這8個位元位都有特定的功能用途,分別是:CWR,ECE,URG,ACK,PSH,RST,SYN,FIN。
CWR ,ECE 兩個flag是用來配合做congestion control的,一般情況下和應用程式層關係不大。發送方的包ECE(ECN-Echo)為0的時候表示出現了congestion,接收方回的包裡CWR(Congestion Window Reduced)為1表明收到congestion資訊並做了處理。我們重點看其他六個flag。
URG URG代表Urgent,表明包的優先順序高,需要優先傳送對方並處理。像我們平時使用terminal的時候經常ctrl+c來結束某個任務,這種命令產生的網路資料包就需要urgent。
ACK 也就是我們所熟悉的ack包,用來告訴對方上一個資料包已經成功收到。不過一般不會為了ack單獨發送一個包,都是在下一個要發送的packet裡設定ack位,這屬於tcp的最佳化機制,參見delayed ack。
PSH Push我們上面解釋過,接收方接收到P位的flag包需要馬上將包交給應用程式層處理,一般我們在http request的最後一個包裡都能看到P位被設定。
RST Reset位,表明packet的發送方馬上就要斷開當前串連了。在http請求結束的時候一般可以看到一個資料包設定了RST位。
SYN SYN位在發送建立串連請求的時候會設定,我們所熟悉的tcp三向交握就是syn和ack位的配合:syn->syn+ack->ack。
FIN Finish位設定了就表示發送方沒有更多的資料要發送了,之後就要單向關閉串連了,接收方一般會回一個ack包。接收方再同理髮送一個FIN就可以雙向關閉串連了。
這8個flag首字母分別是:C E U A P R S F。初看難以記憶,我腦洞了下,把它們組合成 supr cafe,當然少了super少了個e,我可以將就下。我們在使用tcpdump的時候會經常看到這幾個flag,[S],[P],[R],[F],[.]。其他幾個都好理解,[.]特殊點,是個預留位置,沒有其他flag被設定的時候就顯示這個預留位置,一般表示ack。
3.2 tcpdump 更多使用參數
這部分我們來看下tcpdump常用的一些命令參數。文章最開始部分的tcpdump命令是這樣的:sudo tcpdump -i rvi0 -AAl。 -i rvi0 -AAl都是屬於參數部分。常見的有這些:
- -i, 要監聽的網卡名稱,-i rvi0監聽虛擬網卡。不設定的時候預設監聽所有網卡流量。
- -A, 用ASCII碼展示所截取的流量,一般用於網頁或者app裡http請求。-AA可以擷取更多的資訊。
- -X,用ASCII碼和hex來展示包的內容,和上面的-A比較像。-XX可以展示更多的資訊(比如link layer的header)。
- -n,不解析hostname,tcpdump會優先暫時主機的名字。-nn則不展示主機名稱和連接埠名(比如443連接埠會被展示成https)。
- -s,截取的包位元組長度,預設情況下tcpdump會展示96位元組的長度,要擷取完整的長度可以用-s0或者-s1600。
- -c,只截取指定數目的包,然後退出。
- -v,展示更多的有用資訊,還可以用-vv -vvv增加資訊的展示量。
- src,指明ip包的發送方地址。
- dst,指明ip包的接收方地址。
- port,指明tcp包發送方或者接收方的連接埠號碼。
- and,or,not,操作法,字面意思。
上面幾個是我個人比較常用的,更多的參數可以參考這個詳細文檔。有興趣的可以分析下面幾個例子練習下:
tcpdump ‘tcp[13] & 16!=0’
tcpdump src port 80 and tcp
tcpdump -vv src baidu and not dst port 23
tcpdump -nnvvS src 192.0.1.100 and dst port 443
4. 用tcpdump分析http完整請求
說了這麼多,我們再來實戰下,看一個完整的http請求流程。 下面裡的流量是我監聽的 知乎App點贊之後發送的一個https請求。我之前先分析過server的ip地址了,tcpdump命令是:
sudo tcpdump -i rvi0 -AAl src 60.28.215.123 or dst 60.28.215.123
圖中列出了6個前面的packet,10.29.44.240是我iphone的ip地址,60.28.215.123是知乎server的ip地址,紅色方框內是iphone發出的packet,白色方框內是server發出的packet。packet1是iphone三向交握的第一個syn包,packet2是server ack+syn的包,packet3是iphone ack的包。這3個packet之後tcp的三向交握就完成了。
packet4是iphone發出的http request。長度只有240個位元組,所以一個packet就發過去了,當然還設定了flags的P位,request需要馬上被應用程式層處理。包裡面出現了spdy,點贊。
packet5是server ack剛收到的包,長度位0,所以這僅僅是一個ack包。
packet6是server返回http的response了,1388個位元組。packet5和packet6都ack了seq為241的包,當然是為了增加ack的成功率。
中間還有好幾個packet就不仔細分析了,最後再看下請求完成的最後幾個包:
最後兩個packet比較簡單,iphone發送個FIN+ACK的包就中斷連線了,server直接發送了一個RST包後也中斷連線了。
這篇教程到這裡就結束了,建議大家自己多練習下,遇到不懂的參數或關鍵字多google。最好能系統的學習下tcp/ip協議??。
iOS,Android網路抓包教程之tcpdump