libpcap使用

libpcap使用



libpcap是一個網絡數據包捕獲函數庫,功能非常強大,Linux下著名的tcpdump就是以它爲基礎的。今天我們利用它來完成一個我們自己的網絡嗅探器(sniffer)




首先先介紹一下本次實驗的環境:
Ubuntu 11.04,IP:192.168.1.1,廣播地址:192.168.1.255,子網掩碼:255.255.255.0
可以使用下面的命令設置:
sudo ifconfig eth0 192.168.1.1 broadcast 192.168.1.255 netmask 255.255.255.0





1.安裝
http://www.tcpdump.org/下載libpcap(tcpdump的源碼也可以從這個網站下載)
解壓
./configure
make
sudo make install





2.使用
安裝好libpcap後,我們要使用它啦,先寫一個簡單的程序,並介紹如何使用libpcap庫編譯它:
Makefile:
[plain] view plain copy
  1. all: test.c  
  2.     gcc -g -Wall -o test test.c -lpcap  
  3.   
  4. clean:  
  5.     rm -rf *.o test  

其後的程序的Makefile均類似,故不再重複


test1.c
[cpp] view plain copy
  1. #include <pcap.h>  
  2. #include <stdio.h>  
  3.   
  4. int main()  
  5. {  
  6.   char errBuf[PCAP_ERRBUF_SIZE], * device;  
  7.     
  8.   device = pcap_lookupdev(errBuf);  
  9.     
  10.   if(device)  
  11.   {  
  12.     printf("success: device: %s\n", device);  
  13.   }  
  14.   else  
  15.   {  
  16.     printf("error: %s\n", errBuf);  
  17.   }  
  18.     
  19.   return 0;  
  20. }  





可以成功編譯,不過運行的時候卻提示找不到libpcap.so.1,因爲libpcap.so.1默認安裝到了/usr/local/lib下,我們做一個符號鏈接到/usr/lib/下即可






運行test的時候輸出"no suitable device found",原因是我們沒有以root權限運行,root權限運行後就正常了







下面開始正式講解如何使用libpcap:
首先要使用libpcap,我們必須包含pcap.h頭文件,可以在/usr/local/include/pcap/pcap.h找到,其中包含了每個類型定義的詳細說明




1.獲取網絡接口
首先我們需要獲取監聽的網絡接口:
我們可以手動指定或讓libpcap自動選擇,先介紹如何讓libpcap自動選擇:
char * pcap_lookupdev(char * errbuf)
上面這個函數返回第一個合適的網絡接口的字符串指針,如果出錯,則errbuf存放出錯信息字符串,errbuf至少應該是PCAP_ERRBUF_SIZE個字節長度的。注意,很多libpcap函數都有這個參數。
pcap_lookupdev()一般可以在跨平臺的,且各個平臺上的網絡接口名稱都不相同的情況下使用。
如果我們手動指定要監聽的網絡接口,則這一步跳過,我們在第二步中將要監聽的網絡接口字符串硬編碼在pcap_open_live裏。




2.釋放網絡接口
在操作爲網絡接口後,我們應該要釋放它:
void pcap_close(pcap_t * p)
該函數用於關閉pcap_open_live()獲取的pcap_t的網絡接口對象並釋放相關資源。




3.打開網絡接口
獲取網絡接口後,我們需要打開它:
pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)
上面這個函數會返回指定接口的pcap_t類型指針,後面的所有操作都要使用這個指針。
第一個參數是第一步獲取的網絡接口字符串,可以直接使用硬編碼。
第二個參數是對於每個數據包,從開頭要抓多少個字節,我們可以設置這個值來只抓每個數據包的頭部,而不關心具體的內容。典型的以太網幀長度是1518字節,但其他的某些協議的數據包會更長一點,但任何一個協議的一個數據包長度都必然小於65535個字節。
第三個參數指定是否打開混雜模式(Promiscuous Mode),0表示非混雜模式,任何其他值表示混合模式。如果要打開混雜模式,那麼網卡必須也要打開混雜模式,可以使用如下的命令打開eth0混雜模式:
ifconfig eth0 promisc
第四個參數指定需要等待的毫秒數,超過這個數值後,第3步獲取數據包的這幾個函數就會立即返回。0表示一直等待直到有數據包到來。
第五個參數是存放出錯信息的數組。




4.獲取數據包
打開網絡接口後就已經開始監聽了,那如何知道收到了數據包呢?有下面3種方法:
a)
u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)
如果返回值爲NULL,表示沒有抓到包
第一個參數是第2步返回的pcap_t類型的指針
第二個參數是保存收到的第一個數據包的pcap_pkthdr類型的指針
pcap_pkthdr類型的定義如下:
[cpp] view plain copy
  1. struct pcap_pkthdr  
  2. {  
  3.   struct timeval ts;    /* time stamp */  
  4.   bpf_u_int32 caplen;   /* length of portion present */  
  5.   bpf_u_int32 len;      /* length this packet (off wire) */  
  6. };  

注意這個函數只要收到一個數據包後就會立即返回


b)
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
第一個參數是第2步返回的pcap_t類型的指針
第二個參數是需要抓的數據包的個數,一旦抓到了cnt個數據包,pcap_loop立即返回。負數的cnt表示pcap_loop永遠循環抓包,直到出現錯誤。
第三個參數是一個回調函數指針,它必須是如下的形式:
void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
第一個參數是pcap_loop的最後一個參數,當收到足夠數量的包後pcap_loop會調用callback回調函數,同時將pcap_loop()的user參數傳遞給它
第二個參數是收到的數據包的pcap_pkthdr類型的指針
第三個參數是收到的數據包數據




c)
int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)

這個函數和pcap_loop()非常類似,只是在超過to_ms毫秒後就會返回(to_ms是pcap_open_live()的第4個參數)


例子:

test2:

[cpp] view plain copy
  1. #include <pcap.h>  
  2. #include <time.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5.   
  6. int main()  
  7. {  
  8.   char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
  9.     
  10.   /* get a device */  
  11.   devStr = pcap_lookupdev(errBuf);  
  12.     
  13.   if(devStr)  
  14.   {  
  15.     printf("success: device: %s\n", devStr);  
  16.   }  
  17.   else  
  18.   {  
  19.     printf("error: %s\n", errBuf);  
  20.     exit(1);  
  21.   }  
  22.     
  23.   /* open a device, wait until a packet arrives */  
  24.   pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
  25.     
  26.   if(!device)  
  27.   {  
  28.     printf("error: pcap_open_live(): %s\n", errBuf);  
  29.     exit(1);  
  30.   }  
  31.   
  32.   /* wait a packet to arrive */  
  33.   struct pcap_pkthdr packet;  
  34.   const u_char * pktStr = pcap_next(device, &packet);  
  35.   
  36.   if(!pktStr)  
  37.   {  
  38.     printf("did not capture a packet!\n");  
  39.     exit(1);  
  40.   }  
  41.     
  42.   printf("Packet length: %d\n", packet.len);  
  43.   printf("Number of bytes: %d\n", packet.caplen);  
  44.   printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec));   
  45.     
  46.   pcap_close(device);  
  47.     
  48.   return 0;  
  49. }  






打開兩個終端,先ping 192.168.1.10,由於我們的ip是192.168.1.1,因此我們可以收到廣播的數據包,另一個終端運行test,就會抓到這個包。







下面的這個程序會把收到的數據包內容全部打印出來,運行方式和上一個程序一樣:
test3:
[cpp] view plain copy
  1. #include <pcap.h>  
  2. #include <time.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5.   
  6. void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
  7. {  
  8.   int * id = (int *)arg;  
  9.     
  10.   printf("id: %d\n", ++(*id));  
  11.   printf("Packet length: %d\n", pkthdr->len);  
  12.   printf("Number of bytes: %d\n", pkthdr->caplen);  
  13.   printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
  14.     
  15.   int i;  
  16.   for(i=0; i<pkthdr->len; ++i)  
  17.   {  
  18.     printf(" %02x", packet[i]);  
  19.     if( (i + 1) % 16 == 0 )  
  20.     {  
  21.       printf("\n");  
  22.     }  
  23.   }  
  24.     
  25.   printf("\n\n");  
  26. }  
  27.   
  28. int main()  
  29. {  
  30.   char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
  31.     
  32.   /* get a device */  
  33.   devStr = pcap_lookupdev(errBuf);  
  34.     
  35.   if(devStr)  
  36.   {  
  37.     printf("success: device: %s\n", devStr);  
  38.   }  
  39.   else  
  40.   {  
  41.     printf("error: %s\n", errBuf);  
  42.     exit(1);  
  43.   }  
  44.     
  45.   /* open a device, wait until a packet arrives */  
  46.   pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
  47.     
  48.   if(!device)  
  49.   {  
  50.     printf("error: pcap_open_live(): %s\n", errBuf);  
  51.     exit(1);  
  52.   }  
  53.     
  54.   /* wait loop forever */  
  55.   int id = 0;  
  56.   pcap_loop(device, -1, getPacket, (u_char*)&id);  
  57.     
  58.   pcap_close(device);  
  59.   
  60.   return 0;  
  61. }  






從上圖可以看出,如果我們沒有按Ctrl+c,test會一直抓到包,因爲我們將pcap_loop()設置爲永遠循環




由於ping屬於icmp協議,並且發出icmp協議數據包之前必須先通過arp協議獲取目的主機的mac地址,因此我們抓到的包是arp協議的,而arp協議的數據包長度正好是42字節(14字節的以太網幀頭+28字節的arp數據)。具體內容請參考相關網絡協議說明。






5.分析數據包
我們既然已經抓到數據包了,那麼我們要開始分析了,這部分留給讀者自己完成,具體內容可以參考相關的網絡協議說明。在本文的最後,我會示範性的寫一個分析arp協議的sniffer,僅供參考。要特別注意一點,網絡上的數據是網絡字節順序的,因此分析前需要轉換爲主機字節順序(ntohs()函數)




6.過濾數據包
我們抓到的數據包往往很多,如何過濾掉我們不感興趣的數據包呢?
幾乎所有的操作系統(BSD, AIX, Mac OS, Linux等)都會在內核中提供過濾數據包的方法,主要都是基於BSD Packet Filter(BPF)結構的。libpcap利用BPF來過濾數據包。
過濾數據包需要完成3件事:
a) 構造一個過濾表達式
b) 編譯這個表達式
c) 應用這個過濾器



a)

BPF使用一種類似於彙編語言的語法書寫過濾表達式,不過libpcap和tcpdump都把它封裝成更高級且更容易的語法了,具體可以man tcpdump,以下是一些例子:
src host 192.168.1.177
只接收源ip地址是192.168.1.177的數據包


dst port 80
只接收tcp/udp的目的端口是80的數據包


not tcp
只接收不使用tcp協議的數據包


tcp[13] == 0x02 and (dst port 22 or dst port 23)
只接收SYN標誌位置位且目標端口是22或23的數據包(tcp首部開始的第13個字節)


icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo
只接收icmp的ping請求和ping響應的數據包


ehter dst 00:e0:09:c1:0e:82
只接收以太網mac地址是00:e0:09:c1:0e:82的數據包


ip[8] == 5
只接收ip的ttl=5的數據包(ip首部開始的第8個字節)


b)
構造完過濾表達式後,我們需要編譯它,使用如下函數:
int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)
fp:這是一個傳出參數,存放編譯後的bpf
str:過濾表達式
optimize:是否需要優化過濾表達式
metmask:簡單設置爲0即可


c)
最後我們需要應用這個過濾表達式:
int pcap_setfilter(pcap_t * p,  struct bpf_program * fp)
第二個參數fp就是前一步pcap_compile()的第二個參數




應用完過濾表達式之後我們便可以使用pcap_loop()或pcap_next()等抓包函數來抓包了。


下面的程序演示瞭如何過濾數據包,我們只接收目的端口是80的數據包:
test4.c
[cpp] view plain copy
  1. #include <pcap.h>  
  2. #include <time.h>  
  3. #include <stdlib.h>  
  4. #include <stdio.h>  
  5.   
  6. void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)  
  7. {  
  8.   int * id = (int *)arg;  
  9.     
  10.   printf("id: %d\n", ++(*id));  
  11.   printf("Packet length: %d\n", pkthdr->len);  
  12.   printf("Number of bytes: %d\n", pkthdr->caplen);  
  13.   printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec));   
  14.     
  15.   int i;  
  16.   for(i=0; i<pkthdr->len; ++i)  
  17.   {  
  18.     printf(" %02x", packet[i]);  
  19.     if( (i + 1) % 16 == 0 )  
  20.     {  
  21.       printf("\n");  
  22.     }  
  23.   }  
  24.     
  25.   printf("\n\n");  
  26. }  
  27.   
  28. int main()  
  29. {  
  30.   char errBuf[PCAP_ERRBUF_SIZE], * devStr;  
  31.     
  32.   /* get a device */  
  33.   devStr = pcap_lookupdev(errBuf);  
  34.     
  35.   if(devStr)  
  36.   {  
  37.     printf("success: device: %s\n", devStr);  
  38.   }  
  39.   else  
  40.   {  
  41.     printf("error: %s\n", errBuf);  
  42.     exit(1);  
  43.   }  
  44.     
  45.   /* open a device, wait until a packet arrives */  
  46.   pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);  
  47.     
  48.   if(!device)  
  49.   {  
  50.     printf("error: pcap_open_live(): %s\n", errBuf);  
  51.     exit(1);  
  52.   }  
  53.     
  54.   /* construct a filter */  
  55.   struct bpf_program filter;  
  56.   pcap_compile(device, &filter, "dst port 80", 1, 0);  
  57.   pcap_setfilter(device, &filter);  
  58.     
  59.   /* wait loop forever */  
  60.   int id = 0;  
  61.   pcap_loop(device, -1, getPacket, (u_char*)&id);  
  62.     
  63.   pcap_close(device);  
  64.   
  65.   return 0;  
  66. }  




在下面的這一個例子中,客戶機通過tcp的9732端口連接服務器,發送字符'A',之後服務器將'A'+1即'B'返回給客戶機,具體實現可以參考:http://blog.csdn.net/htttw/article/details/7519964


服務器的ip是192.168.56.101,客戶機的ip是192.168.56.1
服務器:



Makefile:

[plain] view plain copy
  1. all: tcp_client.c tcp_server.c  
  2.     gcc -g -Wall -o tcp_client tcp_client.c  
  3.     gcc -g -Wall -o tcp_server tcp_server.c  
  4.   
  5. clean:  
  6.     rm -rf *.o tcp_client tcp_server  


tcp_server:

[cpp] view plain copy
  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <netinet/in.h>  
  4. #include <arpa/inet.h>  
  5. #include <unistd.h>  
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8.   
  9. #define PORT 9832  
  10. #define SERVER_IP "192.168.56.101"  
  11.   
  12. int main()  
  13. {  
  14.   /* create a socket */  
  15.   int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  16.     
  17.   struct sockaddr_in server_addr;  
  18.   server_addr.sin_family = AF_INET;  
  19.   server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);  
  20.   server_addr.sin_port = htons(PORT);  
  21.     
  22.   /* bind with the local file */  
  23.   bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));  
  24.     
  25.   /* listen */  
  26.   listen(server_sockfd, 5);  
  27.     
  28.   char ch;  
  29.   int client_sockfd;  
  30.   struct sockaddr_in client_addr;  
  31.   socklen_t len = sizeof(client_addr);  
  32.   while(1)  
  33.   {  
  34.     printf("server waiting:\n");  
  35.       
  36.     /* accept a connection */  
  37.     client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);  
  38.       
  39.     /* exchange data */  
  40.     read(client_sockfd, &ch, 1);  
  41.     printf("get char from client: %c\n", ch);  
  42.     ++ch;  
  43.     write(client_sockfd, &ch, 1);  
  44.       
  45.     /* close the socket */  
  46.     close(client_sockfd);  
  47.   }  
  48.     
  49.   return 0;  
  50. }  


tcp_client:

[cpp] view plain copy
  1. #include <sys/types.h>  
  2. #include <sys/socket.h>  
  3. #include <netinet/in.h>  
  4. #include <arpa/inet.h>  
  5. #include <unistd.h>  
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8.   
  9. #define PORT 9832  
  10. #define SERVER_IP "192.168.56.101"  
  11.   
  12. int main()  
  13. {  
  14.   /* create a socket */  
  15.   int sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  16.     
  17.   struct sockaddr_in address;  
  18.   address.sin_family = AF_INET;  
  19.   address.sin_addr.s_addr = inet_addr(SERVER_IP);  
  20.   address.sin_port = htons(PORT);  
  21.     
  22.   /* connect to the server */  
  23.   int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));  
  24.   if(result == -1)  
  25.   {  
  26.     perror("connect failed: ");  
  27.     exit(1);  
  28.   }  
  29.     
  30.   /* exchange data */  
  31.   char ch = 'A';  
  32.   write(sockfd, &ch, 1);  
  33.   read(sockfd, &ch, 1);  
  34.   printf("get char from server: %c\n", ch);  
  35.     
  36.   /* close the socket */  
  37.   close(sockfd);  
  38.     
  39.   return 0;  
  40. }  



運行方法如下,首先在服務器上運行tcp_server,然後運行我們的監聽器,然後在客戶機上運行tcp_client,注意,我們可以先清空arp緩存,這樣就可以看到整個通信過程(包括一開始的arp廣播)
在客戶機上運行下列命令來清空記錄服務器的arp緩存:
sudo arp -d 192.168.56.101
arp -a
後發現已經刪除了記錄服務器的arp緩存




抓包的結果如下所示,由於包太多了,無法全部截圖,因此我把所有內容保存在下面的文本中了:



全部的包如下:

[plain] view plain copy
  1. hutao@hutao-VirtualBox:~/test3$ sudo ./test  
  2. success: device: eth0  
  3. id: 1  
  4. Packet length: 60  
  5. Number of bytes: 60  
  6. Recieved time: Sat Apr 28 19:57:50 2012  
  7.  ff ff ff ff ff ff 0a 00 27 00 00 00 08 06 00 01  
  8.  08 00 06 04 00 01 0a 00 27 00 00 00 c0 a8 38 01  
  9.  00 00 00 00 00 00 c0 a8 38 65 00 00 00 00 00 00  
  10.  00 00 00 00 00 00 00 00 00 00 00 00  
  11.   
  12. id: 2  
  13. Packet length: 42  
  14. Number of bytes: 42  
  15. Recieved time: Sat Apr 28 19:57:50 2012  
  16.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 06 00 01  
  17.  08 00 06 04 00 02 08 00 27 9c ff b1 c0 a8 38 65  
  18.  0a 00 27 00 00 00 c0 a8 38 01  
  19.   
  20. id: 3  
  21. Packet length: 74  
  22. Number of bytes: 74  
  23. Recieved time: Sat Apr 28 19:57:50 2012  
  24.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  25.  00 3c d4 af 40 00 40 06 74 55 c0 a8 38 01 c0 a8  
  26.  38 65 8e 20 26 68 79 e1 63 8c 00 00 00 00 a0 02  
  27.  39 08 d4 13 00 00 02 04 05 b4 04 02 08 0a 00 14  
  28.  b7 23 00 00 00 00 01 03 03 06  
  29.   
  30. id: 4  
  31. Packet length: 74  
  32. Number of bytes: 74  
  33. Recieved time: Sat Apr 28 19:57:50 2012  
  34.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
  35.  00 3c 00 00 40 00 40 06 49 05 c0 a8 38 65 c0 a8  
  36.  38 01 26 68 8e 20 b6 c4 e6 e5 79 e1 63 8d a0 12  
  37.  38 90 f1 e5 00 00 02 04 05 b4 04 02 08 0a 00 57  
  38.  a1 2c 00 14 b7 23 01 03 03 05  
  39.   
  40. id: 5  
  41. Packet length: 66  
  42. Number of bytes: 66  
  43. Recieved time: Sat Apr 28 19:57:50 2012  
  44.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  45.  00 34 d4 b0 40 00 40 06 74 5c c0 a8 38 01 c0 a8  
  46.  38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 10  
  47.  00 e5 fb c1 00 00 01 01 08 0a 00 14 b7 24 00 57  
  48.  a1 2c  
  49.   
  50. id: 6  
  51. Packet length: 67  
  52. Number of bytes: 67  
  53. Recieved time: Sat Apr 28 19:57:50 2012  
  54.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  55.  00 35 d4 b1 40 00 40 06 74 5a c0 a8 38 01 c0 a8  
  56.  38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 18  
  57.  00 e5 ba b7 00 00 01 01 08 0a 00 14 b7 25 00 57  
  58.  a1 2c 41  
  59.   
  60. id: 7  
  61. Packet length: 66  
  62. Number of bytes: 66  
  63. Recieved time: Sat Apr 28 19:57:50 2012  
  64.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
  65.  00 34 47 cb 40 00 40 06 01 42 c0 a8 38 65 c0 a8  
  66.  38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 10  
  67.  01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
  68.  b7 25  
  69.   
  70. id: 8  
  71. Packet length: 67  
  72. Number of bytes: 67  
  73. Recieved time: Sat Apr 28 19:57:50 2012  
  74.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
  75.  00 35 47 cc 40 00 40 06 01 40 c0 a8 38 65 c0 a8  
  76.  38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 18  
  77.  01 c5 f1 de 00 00 01 01 08 0a 00 57 a1 2e 00 14  
  78.  b7 25 42  
  79.   
  80. id: 9  
  81. Packet length: 66  
  82. Number of bytes: 66  
  83. Recieved time: Sat Apr 28 19:57:50 2012  
  84.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
  85.  00 34 47 cd 40 00 40 06 01 40 c0 a8 38 65 c0 a8  
  86.  38 01 26 68 8e 20 b6 c4 e6 e7 79 e1 63 8e 80 11  
  87.  01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
  88.  b7 25  
  89.   
  90. id: 10  
  91. Packet length: 66  
  92. Number of bytes: 66  
  93. Recieved time: Sat Apr 28 19:57:50 2012  
  94.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  95.  00 34 d4 b2 40 00 40 06 74 5a c0 a8 38 01 c0 a8  
  96.  38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 10  
  97.  00 e5 fb bc 00 00 01 01 08 0a 00 14 b7 25 00 57  
  98.  a1 2e  
  99.   
  100. id: 11  
  101. Packet length: 66  
  102. Number of bytes: 66  
  103. Recieved time: Sat Apr 28 19:57:50 2012  
  104.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  105.  00 34 d4 b3 40 00 40 06 74 59 c0 a8 38 01 c0 a8  
  106.  38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 11  
  107.  00 e5 fb bb 00 00 01 01 08 0a 00 14 b7 25 00 57  
  108.  a1 2e  
  109.   
  110. id: 12  
  111. Packet length: 66  
  112. Number of bytes: 66  
  113. Recieved time: Sat Apr 28 19:57:50 2012  
  114.  0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00  
  115.  00 34 47 ce 40 00 40 06 01 3f c0 a8 38 65 c0 a8  
  116.  38 01 26 68 8e 20 b6 c4 e6 e8 79 e1 63 8f 80 10  
  117.  01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14  
  118.  b7 25  
  119.   
  120. id: 13  
  121. Packet length: 66  
  122. Number of bytes: 66  
  123. Recieved time: Sat Apr 28 19:57:50 2012  
  124.  08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00  
  125.  00 34 d4 b4 40 00 40 06 74 58 c0 a8 38 01 c0 a8  
  126.  38 65 8e 20 26 68 79 e1 63 8f b6 c4 e6 e8 80 10  
  127.  00 e5 fb b9 00 00 01 01 08 0a 00 14 b7 26 00 57  
  128.  a1 2e  



仔細研究即可發現服務器與客戶機是如何通過tcp通信的。









下面的這個程序可以獲取eth0的ip和子網掩碼等信息:

test5:

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <pcap.h>  
  4. #include <errno.h>  
  5. #include <netinet/in.h>  
  6. #include <arpa/inet.h>  
  7.   
  8. int main()  
  9. {  
  10.   /* ask pcap to find a valid device for use to sniff on */  
  11.   char * dev;   /* name of the device */   
  12.   char errbuf[PCAP_ERRBUF_SIZE];  
  13.   dev = pcap_lookupdev(errbuf);  
  14.   
  15.   /* error checking */  
  16.   if(!dev)  
  17.   {  
  18.     printf("pcap_lookupdev() error: %s\n", errbuf);  
  19.     exit(1);  
  20.   }  
  21.   
  22.   /* print out device name */  
  23.   printf("dev name: %s\n", dev);  
  24.   
  25.   /* ask pcap for the network address and mask of the device */  
  26.   bpf_u_int32 netp;   /* ip */  
  27.   bpf_u_int32 maskp;  /* subnet mask */  
  28.   int ret;            /* return code */  
  29.   ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);  
  30.   
  31.   if(ret == -1)  
  32.   {  
  33.     printf("pcap_lookupnet() error: %s\n", errbuf);  
  34.     exit(1);  
  35.   }  
  36.   
  37.   /* get the network address in a human readable form */  
  38.   char * net;   /* dot notation of the network address */  
  39.   char * mask;  /* dot notation of the network mask */  
  40.   struct in_addr addr;  
  41.   
  42.   addr.s_addr = netp;  
  43.   net = inet_ntoa(addr);  
  44.   
  45.   if(!net)  
  46.   {  
  47.     perror("inet_ntoa() ip error: ");  
  48.     exit(1);  
  49.   }  
  50.   
  51.   printf("ip: %s\n", net);  
  52.   
  53.   /* do the same as above for the device's mask */  
  54.   addr.s_addr = maskp;  
  55.   mask = inet_ntoa(addr);  
  56.     
  57.   if(!mask)  
  58.   {  
  59.     perror("inet_ntoa() sub mask error: ");  
  60.     exit(1);  
  61.   }  
  62.     
  63.   printf("sub mask: %s\n", mask);  
  64.   
  65.   return 0;  
  66. }  


結果如圖:





int pcap_lookupnet(const char * device, bpf_u_int32 * netp, bpf_u_int32 * maskp, char * errbuf)
可以獲取指定設備的ip地址,子網掩碼等信息

netp:傳出參數,指定網絡接口的ip地址

maskp:傳出參數,指定網絡接口的子網掩碼

pcap_lookupnet()失敗返回-1
我們使用inet_ntoa()將其轉換爲可讀的點分十進制形式的字符串






本文的絕大部分來源於libpcap的官方文檔:libpcapHakin9LuisMartinGarcia.pdf,可以在官網下載,文檔只有9頁,不過很詳細,還包括了數據鏈路層,網絡層,傳輸層,應用層等的分析。很好!


更多參考可以man pcap


最後爲了方便大家,本文的所有代碼和上述的pdf文檔都一併上傳上來了:

http://download.csdn.net/detail/htttw/4264686


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章