一、Sniffer原理分析 在實現嗅探器之前,我們先需要掌握TCP/IP協議。TCP協議和IP協議指兩個用在Internet上的網絡協議(或數據傳輸的方法)。它們分別是傳輸控制協議和互連網協議。這兩個協議屬于眾多的TCP/IP 協議組中的一部分。 TCP/IP協議組中的協議保證Internet上數據的傳輸,提供了幾乎現在上網所用到的所有服務。這些服務包括:電子郵件的傳輸、文件傳輸、新聞組的發布和訪問萬維網。 TCP協議在IP協議之上。與IP協議提供不可靠傳輸服務不同的是,TCP協議為其上的應用層提供了一種可靠傳輸服務。這種服務的特點是:可靠、全雙工、流式和無結構傳輸。TCP傳輸原理: TCP協議使用了一個叫積極確認和重發送(positive acknowledgement with retransmission)的技術來實現可靠傳輸。接收者在收到發送者發送的數據后,必須發送一個相應的確認(ACK)消息,表示它已經收到了數據。發送者保存發送的數據的記錄,在發送下一個數據之前,等待這個數據的確認消息。在它發送這個數據的同時,還啟動了一個記時器。如果在一定時間之內,沒有接收到確認消息,就認為是這個數據在傳送時丟失了,接著,就會重新發送這個數據。 這種方法還產生了一個問題,就是包的重復。如果網絡傳輸速度比較低,等到等待時間結束后,確認消息才返回到發送者,那么,由于發送者采用的發送方法,就會出現重復的數據了。解決的一個辦法是給每個數據一個序列號,并需要發送者記住哪個序列號的數據已經確認了。為了防止由于延時或重復確認,規定確認消息里也要包含確認序列號。從而發送者就能知道哪個包已經確認了。TCP協議中還有一個重要的概念:滑動窗口。這一方法的使用,使得傳輸更加高效。 有前面的描述可見,發送者在發送完一個數據包之后,要等待確認。在它收到確認消息之前的這段時間是空閑的。如果網絡延時比較長,這個問題會相當明顯。滑動窗口方法是在它收到確認消息以前,發送多個數據包。可以想象成有一個窗口在一個序列上移動。如果一個包發送出去之后還沒有確認,叫做未確認包。通常未確認的包的個數就是窗口的大小。在接收端,也有一個滑動窗口接收和確認一個包。 使用TCP傳輸就是建立一個連接。在TCP傳輸中一個連接有兩個端點組成。其實,一個連接代表的是發送和接收兩端應用程序的之間的一個通信。可以把他們想象成建立了一個電路。通常一個連接用下面的公式表示:(host,port),host是主機,port是端口。TCP端口能被幾個應用程序共享。對于程序員來講,可以這樣理解:一個應用程序可以為不同的連接提供服務。TCP傳輸的單位是段,在建立連接,傳送數據,確認消息和告之窗口大小時均要進行段的交換。 TCP協議使用一個三次握手來建立一個TCP連接的。握手過程的第一個段的代碼位設置為SYN,序列號為x,表示開始一次握手。接收方收到這個段后,向發送者回發一個段。代碼位設置為SYN和ACK,序列號設置為y,確認序列號設置為x+1。發送者在受到這個段后,知道就可以進行TCP數據發送了,于是,它又向接收者發送一個ACK段,表示,雙方的連接已經建立。在完成握手之后,就開始正式的數據傳輸了。上面握手段中的序列號都是隨機產生的。 了解了TCP/IP協議后,還要掌握網絡編程。在LINUX網絡編程中,我們可以認為套接字是將Unix系統的文件操作推廣到提供點對點的通信。如果要操作文件,應用程序會根據應用程序的需要為之創建一個套接字。操作系統返回一個整數。應用程序通過引用這個正數來使用這個套接字。文件描述符和套接字描述符的不同點在于,在程序調用open()時,操作系統將一個文件描述符綁定到一個文件或設備,但在創建一個套接字時,可以不將它綁定到一個目標地址。程序可以在任何想要用這個套接字的時候指定目標地址。在點對點的通信程序中,我們將請求服務或數據的程序叫做客戶端程序,提供數據或服務的軟件叫做服務器程序。下面解釋一下一個基本的套接字系統調用函數,也是該嗅探器中要用到的函數: socket() #include < sys/types.h> #include < sys/socket.h> int socket(int family, int type, int protocol); int family參數指定所要使用的通信協議,取以下幾個值: AF_UNIX Unix內部協議 AF_INET Internet協議 AF_NS Xerox NS協議 AF_IMPLINK IMP 連接層 int type 指定套接字的類型,取以下幾個值 : SOCK_STREAM 流套接字 SOCK_DGRAM 數據報套接字 SOCK_RAW 未加工套接字 SOCK_SEQPACKET 順序包套接字 int protocol 參數通常設置為0。 socket()系統調用返回一個整數值,叫做套接字描述字sockfd,它的原理與文件描述符一樣。網絡I/O的第一步通常就是調用這個函數。 二、Sniffer具體實現 現在具體介紹一下該嗅探器的實現。該嗅探器是在Red Hat LINUX6.2版本中,用C語言編寫的,以調試并編譯通過。 Sniffer是一種常用的收集有用數據方法,這些數據可以是用戶的帳號和密碼,可以是一些商用機密數據等等。Sniffer是一種常用的收集有用數據的方法,這些數據可以是用戶的帳號和密碼,可以是一些商用機密數據等等。 以太網sniffing 是指對以太網設備上傳送的數據包進行偵聽,發現感興趣的包。如果發現符合條件的包,就把它存到一個log文件中去。通常設置的這些條件是包含字"username"或"password"的包。它的目的是將網絡層放到promiscuous模式,從而能干些事情。Promiscuous模式是指網絡上的所有設備都對總線上傳送的數據進行偵聽,并不僅僅是它們自己的數據。根據以太網的工作原理,可以知道:一個設備要向某一目標發送數據時,它是對以太網進行廣播的。一個連到以太網總線上的設備在任何時間里都在接受數據。不過只是將屬于自己的數據傳給該計算機上的應用程序。利用這一點,可以將一臺計算機的網絡連接設置為接受所有以太網總線上的數據,從而實現sniffer。 sniffer通常運行在路由器,或有路由器功能的主機上。這樣就能對大量的數據進行監控。sniffer屬第二層次的攻擊。通常是攻擊者已經進入了目標系統,然后使用sniffer這種攻擊手段,以便得到更多的信息。sniffer除了能得到口令或用戶名外,還能得到更多的其他信息,比如一個其他重要的信息,在網上傳送的金融信息等等。sniffer幾乎能得到任何以太網上的傳送的數據包。通常sniffer程序只看一個數據包的前200-300個字節的數據,就能發現想口令和用戶名這樣的信息。 下面對該程序的實現作一個介紹。結構etherpacket定義了一個數據包。其中的ethhdr,iphdr,和tcphdr分別是三個結構,用來定義以太網幀,IP數據包頭和TCP數據包頭的格式。 它們在頭文件中的定義如下: struct ethhdr { unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ unsigned char h_source[ETH_ALEN]; /* source ether addr */ unsigned short h_proto; /* packet type ID field */ }; struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t ihl:4; u_int8_t version:4; #elif __BYTE_ORDER == __BIG_ENDIAN u_int8_t version:4; u_int8_t ihl:4; #else #error "Please fix < bytesex.h>" #endif u_int8_t tos; u_int16_t tot_len; u_int16_t id; u_int16_t frag_off; u_int8_t ttl; u_int8_t protocol; u_int16_t check; u_int32_t saddr; u_int32_t daddr; /*The options start here. */ }; struct tcphdr { u_int16_t source; u_int16_t dest; u_int32_t seq; u_int32_t ack_seq; #if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; #elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; u_int16_t fin:1; #else #error "Adjust your < bits/endian.h > defines" #endif u_int16_t window; u_int16_t check; u_int16_t urg_ptr; }; 接下來,定義了一個結構變量victim。隨后,看一下函數int openintf(char *d),它的作用是打開一個網絡接口。在main中是將eth0作為參數來調用這個函數。在這個函數中,用到了下面的結構: struct ifreq { #define IFHWADDRLEN 6 #define IFNAMSIZ 16 union { char ifrn_name[IFNAMSIZ]; /* Interface name, e.g. "en0". */ } ifr_ifrn; union { struct sockaddr ifru_addr; struct sockaddr ifru_dstaddr; struct sockaddr ifru_broadaddr; struct sockaddr ifru_netmask; struct sockaddr ifru_hwaddr; short int ifru_flags; int ifru_ivalue; int ifru_mtu; struct ifmap ifru_map; char ifru_slave[IFNAMSIZ]; /* Just fits the size */ __caddr_t ifru_data; } ifr_ifru; }; 這個結構叫接口請求結構,用來調用在I/O輸入輸出時使用。所有的接口I/O輸出必須有一個參數,這個參數以ifr_name開頭,后面的參數根據使用不同的網絡接口而不同。 如果你要看看你的計算機有哪些網絡接口,使用命令ifconfig即可。一般會看到兩個接口lo0和eth0。在ifreq結構中的各個域的含義與ifconfig的輸出是一一對應的。在這里,程序將eth0作為ifr_name來使用的。接著,該函數將這個網絡接口設置成promiscuous模式。請記住,sniffer是工作在這種模式下的。 再看一下函數read_tcp,它的作用是讀取TCP數據包,傳給filter處理。Filter函數是對上述讀取的數據包進行處理。接下來的程序是將數據輸出到文件中去。函數clearup是在程序退出等事件時,在文件中作個記錄,并關閉文件。否則,你剛才做的記錄都沒了。 三、運行結果及結論 運行這個程序的結果,本人的計算機是處于一個由三十臺以上主機組成的局域網環境中,這個局域網中的所有主機通過代理網關可以上Internet。經測試,在晚上上網高峰期,可以在幾分鐘之內探測到5以上個不同IP地址的郵箱用戶名及密碼,當然,還可以探測到別的信息,如用戶用Web瀏覽器瀏覽的網頁內容、telnet登錄名及密碼等有用信息。預計如果該程序運行于網關,會截取更多的信息。 | |||||