close

上一篇談到如何在Windows系統上截取封包,接下來我們繼讀談如何在Linux系統上截取封包。

下列步驟適用Linux base系統:

後續的指令依據下圖的 IP來執行

  • 注意:因為在Linux上抓取封包是被列管的事項,所以你必須取得root或相等權限的帳號才能執行,下方範例是以root帳號執行。

 

  1. 開啟終端機程式

  2. 先輸入指令:ifconfig ,來確認我們要抓取的網卡為那一張

以這個例子來說,我們將要抓取eth1網卡的封包。

  1. 輸入指令

tcpdump -i eth1 -A -s 20000 host 192.168.21.35 and port 8080

指令說明:

tcpdump是截取封包指令,只有root層級的使用者才能使用。

-i  代表interface,要抓取那一張網卡的封包。

-A 將抓到的封包,用ASCII碼顯示出來。

-s 後接數字,代表截取每個封包內容的最大位元數,如果封包超出位元數,則只截取設定位元數的資料。

從host開始的後方藍色文字,代表顯示該封包的條件,有多個條件時需用and或or串接。

其他較常用的參數還有:

-w 將截取到的封包輸出成檔案,可以將檔案抓回,用Wireshake開啟。

-r 讀入封包的檔案。

下圖是執行中的狀況。

解析HTTP Request封包資料

抓到封包後,最常見的問題就是:不會解讀封包的資料。其實HTTP的封包格式非常簡單,我們來快速地說一下關於HTTP的封包格式吧。

 

HTTP封包範例1(由程式端送出的封包)

  1. POST /myParam/import HTTP/1.1

  2. Content-Length: 128

  3. Content-Type: application/x-www-form-urlencoded; charset=UTF-8

  4. Host: localhost:8080

  5. Connection: Keep-Alive

  6.  
  7. knowledgeXml=%3C%3F&gradeCode=&subjectCode=14&materialVersionId=VER504&type=1&appKey=xxxxxxxxxxxxxxxxxxxxx&sign=29DE9A5627838BDF

基本上要解讀HTTP的封包,要先將封包切成三個部份:

  1. 第一行的宣告。

  2. 中間的header,以本例來說就是2~5行。

  3. 最後一行的資料。

上方的例子是透過程式發送的封包,會比用瀏灠器送出的封包簡單得多,原因是:大部份的工程師不會浪費時間在沒有使用的參數上,所以只會使用最簡單的方式送出。所以閱讀起來會比較簡單。

首先是第1行,要切成三個區塊來閱讀,分別是POST、 /myParam/import、HTTP/1.1,它是意思是:這個封包是使用HTTP 1.1的協定,用POST方式傳遞到/myParam/import的這個網址。
 

從第2到第5行,則是HTTP的header。header的閱讀方式,要以中間的冒號(:)為區隔,左邊是header的key,右邊是對應的value。

所以第二行的「Content-Length: 128」意思是,宣告一個Content-Length的header,值為128。這個Content-Length的意思是,我這個封包所帶的資料長度是128位元;要注意的是,這裡所說的資料長度,是第7行的資料長度,跟1~6行沒有關係。

第三行的Content-Type,也是跟第7行的資料有關係;application/x-www-form-urlencoded代表資料經過urlencode,伺服器必須將其urldecode回來。

第四行「Host: localhost:8080」,表示我這個封包的目的,這裡要跟第一行組起來,即可以完成這個封包的目的地--http://localhost:8080/myParam/import。

第五行的Connection,代表這個連線完成後的處置,Keep-Alive表代持續連線,可降底建立連線的時間,加快下一道封包的連線速度。

第六行的空白,代表一個分隔,上方是HTTP的header,下方是POST所帶的資料。

第七行是POST帶的資料,要注意的是,這裡的資料長度要符合第二行的Content-Length所定義的長度,而資料的格式如下:

單一參數的情況

key=value  範列:a=b

多個參數的情況

key1=value1&key2=value2&key3=value3  範列:a=b&c=d&e=f

如此,伺服器即可以根據key,取得相對應的value。

 

HTTP封包範例2(由Chrome瀏灠器送出的封包)

  1. GET /xxxxx/logout/artEntrutsPcces HTTP/1.1

  2. Host: localhost:8080

  3. Connection: keep-alive

  4. Cache-Control: max-age=0

  5. Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

  6. Upgrade-Insecure-Requests: 1

  7. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36

  8. Referer: http://localhost:8080/

  9. Accept-Encoding: gzip, deflate, sdch

  10. Accept-Language: zh-TW,zh;q=0.8,en;q=0.6,en-US;q=0.4,zh-CN;q=0.2

  11. Cookie: username=user; JSESSIONID=F05805CA253769E22C6F6310E912F0EE; JSESSIONID=61C651207218679AA39C70EF2FA85BEC

這個封包是從瀏灠器送出的,看起來就比上一例麻煩很多,不過解讀的方式還是一樣,第一行的意思是:這個封包是使用HTTP 1.1的協定,用GET方式傳遞到/xxxxx/logout/artEntrutsPcces的這個網址;而從第2行~第11行,都是以header的方式解讀。

這個封包裡值得注意的部份有二個:

  1. 這個封包是以GET方式傳送的,所以資料不會帶在最下方,而是帶在網址中,所以如果有帶參數的話,第一行會變成像:

GET /xxxxx/logout/artEntrutsPcces?id=123&pass=abc HTTP/1.1

  1. 再來是第7行的User-Agent,伺服器是如何判斷來連線的是手機還是電腦?是IE還是Chrome呢?其實大部份都是根據這個header的值來判斷的。

至於其他的參數,大部份在Debug狀態下不會用到,所以在此就不詳細介紹了,有興趣的人可以自己上網查閱。

結論

封包擷取在責任的釐清是很有用的,因為可以很明確的指出,到底是誰的問題;除了責任釐清上,在Debug狀況中其實也很有用,以筆者曾經遇到的問題為例:

手機端的一支小程式,在下載的行為上,只能重頭下載到尾,不能支援續傳功能。

後來這個bug是透過抓取封包,發現手機端在封包的傳送上,有一個奇怪的現象:帶了二個相同的header(Content-Range)。之後利用一些小工具(下一節會談到),快速地重新送出修改後的封包,結果發現回傳的資料是正常的,這時就可以將責任歸屬到手機端,因為手機端送出了非預期的封包,造成伺服器的判斷有問題,最後再請手機端的開發人員去追查程式碼,才把這個問題解決。

不過這裡有一個要注意的點是,在HTTPS狀態下所擷取到的封包,是無法解讀的,因為被加密過了,下圖是擷取與https://www.google.com.tw連線的封包,可以看到跟我們在HTTP狀態下送的封包完全不同,根本無法解讀。


arrow
arrow

    JAVA Programmer 發表在 痞客邦 留言(0) 人氣()