用Wireshark抓包來揭開ftp client GG和ftp server MM的勾搭內容並用C代碼來簡要類比實現Windows內建的ftp client

來源:互聯網
上載者:User

標籤:

      前面, 我們玩過http, 頗有點意思, 在本文中, 我們繼續來玩ftp(file transfer protocol).   http和ftp都是建立在tcp之上的應用程式層協議, 無論他們怎麼封裝, 怎麼裝bigger, 最終還是基於tcp端到端傳輸的。本文主要分為兩個部分: 一. 用Wireshark抓包來揭開ftp client GG和ftp server MM的勾搭內容。二.用C代碼來簡要類比實現Windows內建的ftp client. 

 

      說明, 本文中的實驗, 我用了兩台電腦, 分別是pc1(192.168.1.100)和pc2(192.168.1.102). 其中, pc1做用戶端(ftp client GG), pc2做服務端(ftp server MM)。  好吧, 開啟兩台電腦的電源開關吧,  來玩ftp。


       一. 用Wireshark抓包來揭開ftp client GG和ftp server MM的勾搭內容

       1. 在pc2上開啟ftp server MM, 設定使用者名稱為1, 密碼為1. 我們在cmd中執行netstat -nao | findstr 21 可以看到, pc開啟了21連接埠的監聽。 在ftp server MM對應的目錄下建立a.txt和b.txt兩個檔案。(ftp server MM的搭建方法請參考我之前的博文:http://blog.csdn.net/stpeace/article/details/38026285)

       2. 在pc1上進行wireshark抓包, 並開啟cmd, 依次執行如下命令, 得到結果為:

C:\Documents and Settings\Administrator>ftp 192.168.1.102
Connected to 192.168.1.102.
220 歡迎訪問 Slyar FTPserver!
User (192.168.1.102:(none)): 1
331 Please specify the password.
Password:
230 Login successful.
ftp>
ftp>
ftp> dir
200 Port command successful.
150 Opening ASCII mode data connection for directory list.
-rwx------ 1 user group              0 Apr 16 23:04 a.txt
-rwx------ 1 user group              0 Apr 16 23:04 b.txt
226 Transfer complete
ftp: 收到 118 位元組,用時 0.01Seconds 7.87Kbytes/sec.
ftp>


    我們首先執行ftp 192.168.1.102, 此時ftp client GG與ftp server MM建立tcp串連, 然後輸入使用者名稱和密碼進行認證。 認證通過後, 最後用dir命令來查詢ftp server MM中有什麼東東, 結果看到了a.txt和b.txt檔案

     3. 我們將抓包檔案儲存下來(port.cap), 進行分析, 如下:


     4.  第1.2個包很簡單, 住pc1發的arp廣播, 主要是去找192.168.1.102這台電腦(pc2)的mac, 第3個包就表明成功擷取了pc2的mac地址, 有了mac才能通訊啊。

     5.  實際上, 上述的arp操作是由ftp 192.168.1.102觸發的, 其實ftp 192.168.1.102的更重要用途是與ftp server MM建立tcp串連通道。第4, 5, 6個包就是傳說中的三向交握協議, 太重要太基礎, 故不多說。

     6. 第7-14個包主要使用者ftp client GG與ftp server MM之間的使用者名稱和密碼認證, 約妹子, 要獲得妹子的驗證和任何, 也是理所當然的了。

     7. 當我們執行dir命令時,會觸發後續所有的包。 第15個包是ftp client GG向ftp server MM發送PORT 192,168,1,100,7,220命令, 並對ftp server MM說: 你待會兒要主動跟我串連我, 建立資料轉送的tcp通道(第二個tcp通道), 我監聽地址是192.168.1.100上的 (7*256+220) 連接埠, 並意猶未盡地暗示: 你這個ftp server MM要主動一點來聯絡我, 我就在剛才的那個ip和port處等你, 不見不散。 此時ftp server GG端開始監聽(7*256+220)這個連接埠, 等待ftp server MM主動上鉤。 第16個包是ftp server MM的回應, 意思是在說: 好的, 我一定會去那個地點那個連接埠找你。

    8.  第17個包是ftp client GG發送的dir請求, 意思是詢問ftp server MM: 請問你家產有多少?

    9.  第18個包是ftp server MM的回應, 彷彿在說: 我已經知道你在問什麼問題了, 我待會兒會在我們之前約定的地點告訴你(第二個臨時的tcp通道)。

    10.  好,到此為止, ftp client GG和ftp server MM的第一輪勾搭暫時告一段落。注意, 是暫時告一段落, 不是終止, 你看看, 根本沒有揮手byebye的收據包啊。

    11.  好, ftp client GG和ftp server MM的第二輪勾搭正式開始, 只不過, 這次, ftp server MM開始騷動了, 主動到照預約的地點去上鉤, 請看第19-21個包, 這就是第二輪勾搭的三向交握協議。

    12.  我們來一起僅僅地盯住第22個包, 這次, ftp server MM要在剛剛建立的tcp通道(第二個臨時的tcp通道)上來傳輸資料了, 別忘了, ftp client GG在第一輪勾搭中問過ftp server MM家有多少財產, 這次, ftp server MM該說實話了, 財產肯定會涉及到隱私啊, 所以用新的tcp通道來回饋。 展開第22個包, 我們看到ftp server MM終於不再矜持了, 把自己的家產和盤托出, 通訊內容為:

-rwx------ 1 user group              0 Apr 16 23:04 a.txt
-rwx------ 1 user group              0 Apr 16 23:04 b.txt

     13. 我們看到, 其實, ftp serve MM家裡也沒有多少財產, 只有可憐巴巴的a.txt和b.txt兩個空空的檔案。

     14. 後續的23-29個包無非就是一些掛電話的過程(關閉第二個臨時的tcp通道), 說byebye了, 可以清晰看到有斷開socket的過程。 值得注意的是第28個包, ftp server MM非常貼心地說: 我已經說說完了, 財產就這麼多, 接下來你開著辦吧。

     15. 請注意, 到此為止, ftp client GG和ftp server MM的在第二個tcp通道上已經斷開了, 但是在第一個tcp通道上仍然保持著聯絡。 後續如果ftp client繼續執行其他請求命令, 比如探探三圍啊, 他自身又會重新開啟新的隨機連接埠進行監聽, 並把這個隨機連接埠又一次告訴ftp server MM, 讓ftp server MM來發起第三次臨時tcp串連。 同理, ftp server MM會在第三個tcp通道上吧三圍資料告訴給ftp client GG, 隨後第三個臨時的tcp通道也會被拆除。 不變的是, 第一個tcp通道依然在那裡緊緊相連。

     總結: 以上就是ftp的主動模式。 第一個“永久”tcp通道主要用來傳遞請求命令, 是ftp client GG主動去勾引ftp server MM, 後續的第二/三/四...個臨時tcp通道主要用來傳資料, 而且是ftp server MM主動上鉤。所謂的ftp主動模式, 是指ftp server MM主動。


      二.用C代碼來簡要類比實現Windows內建的ftp client.

      到此為止, 對tcp主動模式應該有了比較通透的理解了, 現在我們嘗試來用C代碼簡要類比一下上述過程, 類比ftp client GG的代碼如下:

// 我花了較長時間調試, 如果要轉載, 請註明本部落格地址, 尊重著作權// 部落格地址:http://blog.csdn.net/stpeace/article/details/45100687#include <stdio.h>#include <string.h>#include <winsock2.h>  // winsock介面#pragma comment(lib, "ws2_32.lib") // winsock實現庫// 緩衝區長度#define LEN (1024 + 1)  SOCKET g_ctrlSocket = 0;  // ftp client端負責在"命令控制tcp通道"上通訊的socketSOCKET g_listenSocket = 0;  // ftp client端負責監聽的socketSOCKET g_dataSocket = 0;  // ftp client端負責在"資料轉送tcp通道"上通訊的socket// 建立"命令控制tcp通道"的通訊socketint createCtrlSocket(){g_ctrlSocket = socket(AF_INET, SOCK_STREAM, 0);return 0;}// 擷取ftp server 在"命令控制tcp通道"上返回的資訊int getCmdResFromFtpServer(){char szRecvBuf[LEN] = {0};int nRet = recv(g_ctrlSocket, szRecvBuf, sizeof(szRecvBuf) - 1, 0);if(nRet < 0){printf("recv error\n");return -1;}if(0 == nRet){printf("connection has been closed by ftp server");return -1;}printf("%s", szRecvBuf);return 0;}// 建立"命令控制tcp通道"int connectFtpServer(const char *pIP,  unsigned short port){struct sockaddr_in ftpServerAddr;ftpServerAddr.sin_family = AF_INET;ftpServerAddr.sin_addr.S_un.S_addr = inet_addr(pIP);ftpServerAddr.sin_port = htons(port);int nRet = connect(g_ctrlSocket,  (struct sockaddr *)&ftpServerAddr,  sizeof(ftpServerAddr));if(nRet < 0){printf("connect error\n");return -1;}getCmdResFromFtpServer();return 0;}// 從"命令控制tcp通道"上向ftp server 發起相關命令請求int requestFtpServer(const char *pPassWord){char szSendBuf[LEN] = {0};sprintf(szSendBuf, "%s\r\n", pPassWord);send(g_ctrlSocket, szSendBuf, strlen(szSendBuf) + 1, 0);getCmdResFromFtpServer();return 0;}// 開啟ftp client監聽線程,準備接受ftp server請求建立"資料轉送tcp通道"DWORD WINAPI createDataSocketThread(LPVOID p){unsigned int a = 0;unsigned int b = 0;sscanf((const char *)p, "%d:%d", &a, &b);SOCKET g_listenSocket = socket(AF_INET, SOCK_STREAM, 0);      SOCKADDR_IN addrSrv;      addrSrv.sin_family = AF_INET;    addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.100");     addrSrv.sin_port = htons(a * 256 + b);    bind(g_listenSocket,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));      listen(g_listenSocket, 5);       SOCKADDR_IN addrClient;      int len = sizeof(SOCKADDR);      // 等待ftp server主動請求建立"資料轉送tcp通道"    g_dataSocket = accept(g_listenSocket, (SOCKADDR*)&addrClient, &len);return 0;}// 從"資料轉送tcp通道" 上擷取ftp server的資料資訊int getDateFromFtpServer(){char szRecvBuf[1000] = {0};  int nRet = recv(g_dataSocket, szRecvBuf, 1000 - 1, 0); if(nRet < 0){printf("recv error\n");return -1;}if(0 == nRet){printf("closed by ftp server\n");return -1;}printf("%s", szRecvBuf);return 0;}int main(){// 網路初始化WSADATA wsaData;WSAStartup(MAKEWORD(1,1), &wsaData);// 建立"命令控制tcp通道"上通訊的socketcreateCtrlSocket();// 建立"命令控制tcp通道"connectFtpServer("192.168.1.102", 21);// 從"命令控制tcp通道"發送使用者名稱和密碼給ftp server進行認證requestFtpServer("user 1");requestFtpServer("pass 1");// 建立"資料轉送tcp通道"HANDLE handle = CreateThread(NULL, 0, createDataSocketThread, "12:34", 0, NULL); // 主線程阻塞1s, 確保createDataSocketThread線程拉起監聽Sleep(1000);// 從"命令控制tcp通道" 把ftp client即將監聽的ip, port傳給ftp serverrequestFtpServer("PORT 192,168,1,100,12,34");// 從"命令控制tcp通道" 上把LIST訊息請求傳遞過去requestFtpServer("LIST");// 等1s(相當有必要), 等待ftp server的串連, 否則, 如果"資料轉送tcp通道"沒有建立好, 那還談什麼溝通呢?Sleep(1000);// 從"資料轉送tcp通道"上接收資料getDateFromFtpServer();// 關閉臨時的"資料轉送tcp通道". 注意: "命令控制tcp通道"不能關閉closesocket(g_dataSocket);closesocket(g_listenSocket);// 重複上述請求handle = CreateThread(NULL, 0, createDataSocketThread, "12:40", 0, NULL); Sleep(1000);requestFtpServer("PORT 192,168,1,100,12,40");requestFtpServer("LIST");Sleep(1000);getDateFromFtpServer();closesocket(g_dataSocket);closesocket(g_listenSocket);while(1); // 阻塞CloseHandle(handle); closesocket(g_ctrlSocket);WSACleanup();return 0;}

      ftp client端的結果為:

220 歡迎訪問 Slyar FTPserver!
331 Please specify the password.
230 Login successful.
200 Port command successful.
150 Opening ASCII mode data connection for directory list.
-rwx------ 1 user group              0 Apr 16 23:04 a.txt
-rwx------ 1 user group              0 Apr 16 23:04 b.txt
226 Transfer complete
200 Port command successful.
-rwx------ 1 user group              0 Apr 16 23:04 a.txt
-rwx------ 1 user group              0 Apr 16 23:04 b.txt

     

     我在調試上述程式的時候吃了一些苦頭, 有一下幾點值得注意:

     1. 確保ftp server MM先開啟, 然後再運行我上面的程式。

     2. 確保ftp server MM的21連接埠沒有被防火牆堵住, 在pc1上用telnet 192.168.1.102 21就可以測試。

     3. 確保ftp client GG的隨機連接埠沒有被防火牆堵住, 我在調試的時候, 就栽倒在此處, 結果ftp server MM總是不能與ftp client GG建立第二個tcp通道。 典型癥狀是, 在pc2上, 執行telnet 192.168.1.100 xxx 無法成功, 其中xxx是ftp client GG監聽的連接埠。 後來, 我把pc1上的防火牆放開, 就可以了。

     4. 程式中採用線程建立socket是很好的方式, 確保ftp client GG先啟動accept.

     5. 程式中的兩個Sleep比較關鍵, 在程式中已經有注釋, 所以我就不再贅述了。

     6. 很多資料說, 在後續的"資料轉送tcp通道"中, ftp server MM端的連接埠是20, 也就是說, 去connect的時候, 她自身socket綁定到了20連接埠, 剛好, 我們之前在博文中講過bind的這種應用。 但是, 在實際抓包中我發現,ftp server MM並沒有死死守在20連接埠, 當然, 這並不為錯, 主要是具體實現的差異所致。 而且, 我覺得不綁定20號連接埠更好。


     

      OK . 到此為止, 我們算是對ftp的主動模式有了比較深入的瞭解了。 那什麼是ftp的被動模式呢? 很簡單: 第一次tcp通道由ftp client GG請求建立, 後續的所有臨時tcp通道也是由ftp client GG請求建立, 也就是說, ftp server MM很被動, 很被動。 

     我也親自驗證過, Windows上內建的ftp client其實並不支援所謂的被動模式。 網上所說的quote pasv或literal pasv其實並沒有pasv請求的功能。 當然, 在這個問題上, 更詳細的闡述是:http://blogs.isaserver.org/pouseele/2006/11/09/about-the-microsoft-command-line-ftp-client/ , 有興趣的可以去瞄瞄。

      鑒於ftp被動模式和上面介紹的主動模式大同小異, 我就不再贅述ftp被動模式了, 有興趣的童鞋可以自己深入學習一下, 找個支援被動模式的ftp client,  抓抓包看看。



      好了, 關於ftp的介紹到此為止。





    

用Wireshark抓包來揭開ftp client GG和ftp server MM的勾搭內容並用C代碼來簡要類比實現Windows內建的ftp client

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.