bbs > 嗅探
目錄
No. 1
  嗅探 sniff。嗅探器可以竊聽網絡上流經的數據包。
  用集綫器hub組建的網絡是基於共享的原理的,
  局域網內所有的計算機都接收相同的數據包,
  而網卡構造了硬件的“過濾器“
  通過識別mac地址過濾掉和自己無關的信息,
  嗅探程序衹需關閉這個過濾器,
  將網卡設置為“混雜模式“就可以進行嗅探
  用交換機switch組建的網絡是基於“交換“原理的
  ,交換機不是把數據包發到所有的端口上,
  而是發到目的網卡所在的端口。
  這樣嗅探起來會麻煩一些,嗅探程序一般利用“arp欺騙“的方法
  ,通過改變mac地址等手段,欺騙交換機將數據包發給自己,
  嗅探分析完畢再轉發出去
  一 前言
  sniff真是一個古老的話題,關於在網絡上采用sniff來獲取敏感信息已經不是什麽新鮮事,也不乏很多成功的案例,那麽,sniff究竟是什麽呢?sniff就是嗅探器,就是竊聽器,sniff靜悄悄的工作在網絡的底層,把你的秘密全部記錄下來。看過威爾史密斯演的《全民公敵》嗎?sniff就象裏面精巧的竊聽器一樣,讓你防不勝防。
  sniff可以是軟件,也可以是硬件,既然是軟件那就要分平臺,有windows下的、unxi下的等,硬件的sniff稱為網絡分析儀,反正不管硬件軟件,目標衹有一個,就是獲取在網絡上傳輸的各種信息。本文僅僅介紹軟件的sniff。
  當你舒適的坐在傢裏,愜意的享受網絡給你帶來的便利,收取你的email,購買你喜歡的物品的時候,你是否會想到你的朋友給你的信件,你的信用卡帳號變成了一個又一個的信息包在網絡上不停的傳送着,你是否曾經這些信息包會通過網絡流入別人的機器呢?你的擔憂不是沒有道理的,因為sniff可以讓你的擔憂變成實實在在的危險。就好像一個人躲在你身後偷看一樣。。。。。。
  二 網絡基礎知識
  “網絡基礎知識”,是不是聽起來有點跑題了?雖然聽起來這和我們要談的sniff沒什麽關係,可是還是要說一說的,萬丈高樓平地起,如果連地基都沒打好,怎麽蓋樓?!如果你對網絡還不是十分清楚的話,最好能靜下心來好好看看,要知道,這是基礎的基礎,在這裏我衹是簡單的說一下,免得到時候有人迷糊,詳細的最好能夠自己去找書看看。
  (1)tcp/ip體係結構
  開放係統互連(osi)模型將網絡劃分為七層模型,分別用以在各層上實現不同的功能,這七層分別為:應用層、表示層、會話層、傳輸層、網絡層、數據鏈路層及物理層。而tcp/ip體係也同樣遵循這七層標準,衹不過在某些osi功能上進行了壓縮,將表示層及會話層合併入應用層中,所以實際上我們打交道的tcp/ip僅僅有5層而已,網絡上的分層結構决定了在各層上的協議分佈及功能實現,從而决定了各層上網絡設備的使用。實際上很多成功的係統都是基於osi模型的,如:如幀中繼、atm、isdn等。
  tcp/ip的網絡體係結構(部分)
  從上面的圖中我們可以看出,第一層物理層和第二層數據鏈路層是tcp/ip的基礎,而tcp/ip本身並不十分關心低層,因為處在數據鏈路層的網絡設備驅動程序將上層的協議和實際的物理接口隔離開來。網絡設備驅動程序位於介質訪問子層(mac) (2)網絡上的設備  中繼器:中繼器的主要功能是終結一個網段的信號並在另一個網段再生該信號,一句話,就是簡單的放大而已,工作在物理層上。
  網 橋:網橋使用mac物理地址實現中繼功能,可以用來分隔網段或連接部分異種網絡,工作在數據鏈路層。
  路由器:路由器使用網絡層地址(ip,x.121,e.164等),主要負責數據包的路由尋徑,也能處理物理層和數據鏈路層上的工作。
  網 關:主要工作在網絡第四層以上,主要實現收斂功能及協議轉換,不過很多時候網關都被用來描述任何網絡互連設備。
  (3)tcp/ip與以太網
  以太網和tcp/ip可以說是相互相成的,可以說兩者的關係幾乎是密不可分,以太網在一二層提供物理上的連綫,而tcp/ip工作在上層,使用32位的ip地址,以太網則使用48位的mac地址,兩者間使用arp和rarp協議進行相互轉換。從我們上面tcp/ip的模型圖中可以清楚的看到兩者的關係。
  載波監聽/衝突檢測(csma/cd)技術被普遍的使用在以太網中,所謂載波監聽是指在以太網中的每個站點都具有同等的權利,在傳輸自己的數據時,首先監聽信道是否空閑,如果空閑,就傳輸自己的數據,如果信道被占用,就等待信道空閑。而衝突檢測則是為了防止發生兩個站點同時監測到網絡沒有被使用時而産生衝突。以太網采用廣播機製,所有與網絡連接的工作站都可以看到網絡上傳遞的數據。
  為了加深你的理解,我們來看看下面的圖,一個典型的在以太網中客戶與服務器使用tcp/ip協議的通信
  以太網
  唆唆了這麽多,有人煩了吧?相信我,這是基礎的基礎,可以說是說得是很簡單拉,如果需要,拿出個幾十萬字來說上面的內容,我想也不嫌多,好了,讓我們進入下一節,sniff的原理。
  三 sniff的原理
  要知道在以太網中,所有的通訊都是廣播的,也就是說通常在同一個網段的所有網絡接口都可以訪問在物理媒體上傳輸的所有數據,而每一個網絡接口都有一個唯一的硬件地址,這個硬件地址也就是網卡的mac地址,大多數係統使用48比特的地址,這個地址用來表示網絡中的每一個設備,一般來說每一塊網卡上的mfc地址都是不同的,每個網卡廠傢得到一段地址,然後用這段地址分配給其生産的每個網卡一個地址。在硬件地址和ip地址間使用arp和rarp協議進行相互轉換。
  在正常的情況下,一個網絡接口應該衹響應這樣的兩種數據幀:
   1.與自己硬件地址相匹配的數據幀。 2.發嚮所有機器的廣播數據幀。
  在一個實際的係統中,數據的收發是由網卡來完成的,網卡接收到傳輸來的數據,網卡內的單片程序接收數據幀的目的mac地址,根據計算機上的網卡驅動程序設置的接收模式判斷該不該接收,認為該接收就接收後産生中斷信號通知cpu,認為不該接收就丟掉不管,所以不該接收的數據網卡就截斷了,計算機根本就不知道。cpu得到中斷信號産生中斷,操作係統就根據網卡的驅動程序設置的網卡中斷程序地址調用驅動程序接收數據,驅動程序接收數據後放入信號堆棧讓操作係統處理。而對於網卡來說一般有四種接收模式:
  廣播方式:該模式下的網卡能夠接收網絡中的廣播信息。 組播方式:設置在該模式下的網卡能夠接收組播數據。
  直接方式:在這種模式下,衹有目的網卡才能接收該數據。混雜模式:在這種模式下的網卡能夠接收一切通過它的數據,而不管該數據是否是傳給它的。
  好了,現在我們總結一下,首先,我們知道了在以太網中是基於廣播方式傳送數據的,也就是說,所有的物理信號都要經過我的機器,再次,網卡可以置於一種模式叫混雜模式(promiscuous),在這種模式下工作的網卡能夠接收到一切通過它的數據,而不管實際上數據的目的地址是不是他。這實際上就是我們sniff工作的基本原理:讓網卡接收一切他所能接收的數據。
  我們來看一個簡單的例子,如圖一所示,機器a、b、c與集綫器hub相連接,集綫器hub通過路由器router訪問外部網絡。這是一個很簡單也很常見的情況,比如說在公司大樓裏,我所在的網絡部辦公室裏的幾臺機器通過集綫器連接,而網絡部、開發部、市場部也是同樣如此,幾個部門的集綫器通過路由器連接。還是回到我們的圖一上來,值得註意的一點是機器a、b、c使用一個普通的hub連接的,不是用switch,也不是用router,使用switch和router的情況要比這復雜得多。
  我們假設一下機器a上的管理員為了維護機器c,使用了一個ftp命令嚮機器c進行遠程登陸,那麽在這個用hub連接的網絡裏數據走嚮過程是這樣的。首先機器a上的管理員輸入的登陸機器c的ftp口令經過應用層ftp協議、傳輸層tcp協議、網絡層ip協議、數據鏈路層上的以太網驅動程序一層一層的包裹,最後送到了物理層,我們的網綫上。接下來數據幀送到了hub上,現在由hub嚮每一個接點廣播由機器a發出的數據幀,機器b接收到由hub廣播發出的數據幀,並檢查在數據幀中的地址是否和自己的地址相匹配,發現不是發嚮自己的後把這數據幀丟棄,不予理睬。而機器c也接收到了數據幀,並在比較之後發現是發現自己的,接下來他就對這數據幀進行分析處理。
  在上面這個簡單的例子中,機器b上的管理員如果很好奇,他很想知道究竟登陸機器c上ftp口令是什麽?那麽他要做的很簡單,僅僅需要把自己機器上的網卡置於混雜模式,並對接收到的數據幀進行分析,從而找到包含在數據幀中的口令信息。
  四 做一個自己的sniff
  在上一節裏,我們已經知道了sniff的基本原理是怎麽一回事,這一節我們來親自動手做一個自己的sniff,畢竟,用程序代碼來說話比什麽都要來得真實,也容易加深理解。
  回頭想一想我們上面說的原理,我們要做的事情有幾件:
  1. 把網卡置於混雜模式。   2. 捕獲數據包。   3.分析數據包。
  註:下面的源代碼取至chad renfro的<< basic packet-snifferconstruction from the ground up>>一文中
  /************************tcp_sniff_2.c********************/
  1.#include
  2.#include
  3.#include
  4.#include
  5.#include
  6.#include
  7.#include
  8.#include
  9.#include "headers.h"
  #define interface "eth0"
   /*prototype area*/
  10.int open_raw_socket(void);
  11.int set_promisc(char *interface, intsock);
  12.int main() {
  13.int sock, bytes_recieved, fromlen;
  14.char buffer[65535];
  15.struct sockaddr_in from;
  16.struct ip *ip;
  17.struct tcp *tcp;
  18.sock = open_raw_socket();
  19. set_promisc(interface, sock);
  20. while(1)
  22. {
  23. fromlen = sizeof from;
  24. bytes_recieved = recvfrom(sock, buffer, sizeofbuffer, 0, (struct sockaddr *)&from, &fromlen);
  25. printf("
  bytes received :::%5d
  ",bytes_recieved);
  26. printf("source address :::%s
  ",inet_ntoa(from.sin_addr));
  27. ip = (struct ip *)buffer;
  /*see if this is a tcp packet*/
  28. if(ip->ip_protocol == 6) {
  29. printf("ip header length :::%d
  ",ip->ip_length);
  30. printf("protocol :::%d
  ",ip->ip_protocol);
  31. tcp = (struct tcp *)(buffer +(4*ip->ip_length));
  32. printf("source port :::%d
  ",ntohs(tcp->tcp_source_port));
  33. printf("dest port :::%d
  ",ntohs(tcp->tcp_dest_port));
  34. }
  35. }
  36.}
  37.int open_raw_socket() {
  38. int sock;
  39. if((sock = socket(af_inet, sock_raw, ipproto_tcp)) < 0){
  /*then the socket was not created properly and must die*/
  40. perror("the raw socket was not created");
  41. exit(0);
  42. };
  43. return(sock);
  44. }
  45.int set_promisc(char *interface, int sock ) {
  46. struct ifreq ifr;
  47. strncpy(ifr.ifr_name, interface,strnlen(interface)+1);
  48. if((ioctl(sock, siocgifflags, &ifr) == -1)) {
  /*could not retrieve flags for the interface*/
  49. perror("could not retrive flags for the interface");
  50. exit(0);
  51. }
  52. printf("the interface is ::: %s
  ", interface);
  53. perror("retrieved flags from interface successfully");
  54. ifr.ifr_flags |= iff_promisc;
  55. if (ioctl (sock, siocsifflags, &ifr) == -1 ) {
  /*could not set the flags on the interface */
  56. perror("could not set the promisc flag:");
  57. exit(0);
  58. }
  59. printf("setting interface ::: %s ::: to promisc",interface);
  60. return(0);
  61. }
  /***********************eof**********************************/
  上面這段程序中有很詳細的註解,不過我想還是有必要說一說,首先第10行--intopen_raw_socket(void); 是我們的自定義函數,具體內容如下:
  37.int open_raw_socket() {
  38. int sock;
  39. if((sock = socket(af_inet, sock_raw, ipproto_tcp)) < 0){
  /*then the socket was not created properly and must die*/
  40. perror("the raw socket was not created");
  41. exit(0);
  42. };
  43. return(sock);
  44. }
  
  第39行 if((sock = socket(af_inet, sock_raw, ipproto_tcp)) < 0) {
  這裏我們調用了socket函數,使創建瞭瞭一個原始套接口,使之收到tcp/ip信息包。
  接下來第11行-int set_promisc(char *interface, intsock),這也是我們的自定義函數,目的是把網卡置於混雜模式,具體內容如下:
  45.int set_promisc(char *interface, int sock ) {
  46. struct ifreq ifr;
  47. strncpy(ifr.ifr_name, interface,strnlen(interface)+1);
  48. if((ioctl(sock, siocgifflags, &ifr) == -1)) {
  /*could not retrieve flags for the interface*/
  49. perror("could not retrive flags for the interface");
  50. exit(0);
  51. }
  52. printf("the interface is ::: %s
  ", interface);
  53. perror("retrieved flags from interface successfully");
  54. ifr.ifr_flags |= iff_promisc;
  55. if (ioctl (sock, siocsifflags, &ifr) == -1 ) {
  /*could not set the flags on the interface */
  56. perror("could not set the promisc flag:");
  57. exit(0);
  58. }
  59. printf("setting interface ::: %s ::: to promisc",interface);
  60. return(0);
  61. }
  首先 struct ifreq ifr; 定一了一個ifrreg的結構ifr,接下來strncpy(ifr.ifr_name,interface,strnlen(interface)+1);,就是把我們網絡設備的名字填充到ifr結構中,在這裏#define interface "eth0" ,讓我們再往下看,ioctl(sock, siocgifflags,&ifr),siocgifflags請求表示需要獲取接口標志,現在到了第54行,在我們成功的獲取接口標志後把他設置成混雜模式,ifr.ifr_flags|= iff_promisc;ioctl (sock, siocsifflags,&ifr)。ok,現在我們所說的第一步已經完成--------把網卡置於混雜模式。
  現在進入第二步,捕獲數據包。從第20行開始,我們進入了一個死循環,while(1),在第24行,recvfrom(sock,buffer, sizeof buffer, 0, (struct sockaddr *)&from,&fromlen),這個函數要做的就是接收數據,冰把接收到的數據放入buffer中。就是這麽簡單,已經完成了我們要捕獲數據包的任務。
  到了第三步,分析數據包。27行,ip = (struct ip*)buffer,使我們在頭文件中的ip結構對應於所接收到的數據,接下來判斷在網絡層中是否使用的是tcp協議,if(ip->ip_protocol== 6) ,如果答案是,tcp信息包從整個ip/tcp包 buffer +(4*ip->ip_length) 地址處開始,所以31行 tcp = (struct tcp*)(buffer +(4*ip->ip_length)),然後對應結構把你所需要的信息輸出。
  /*************************headers.h**************************/
  /*structure of an ip header*/
  struct ip {
  unsigned int ip_length:4; /*little-endian*/
  unsigned int ip_version:4;
  unsigned char ip_tos;
  unsigned short ip_total_length;
  unsigned short ip_id;
  unsigned short ip_flags;
  unsigned char ip_ttl;
  unsigned char ip_protocol;
  unsigned short ip_cksum;
  unsigned int ip_source; unsigned int ip_dest;
  };
  /* structure of a tcp header */
  struct tcp {
  unsigned short tcp_source_port;
  unsigned short tcp_dest_port;
  unsigned int tcp_seqno;
  unsigned int tcp_ackno;
  unsigned int tcp_res1:4, /*little-endian*/
  tcp_hlen:4,
  tcp_fin:1,
  tcp_syn:1,
  tcp_rst:1,
  tcp_psh:1,
  tcp_ack:1,
  tcp_urg:1,
  tcp_res2:2;
  unsigned short tcp_winsize;
  unsigned short tcp_cksum;
  unsigned short tcp_urgent;
  };
  /*********************eof***********************************/
  從上面的分析我們可以清楚的認識到,認識一個sniff需要對tcp/ip協議有着詳細的瞭解,否則你根本無法找到你需要的信息。有了上面的基礎,你可以自己來做一個你需要的sniff了。
相關詞
電腦技術網絡工具流量查看監聽故障診斷sniffer性能分析嗅探工具
包含詞
嗅探器嗅探者