天天看點

libpcap 使用

libpcap使用

轉載自:http://blog.csdn.net/htttw/article/details/7521053

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

libpcap 使用

1.安裝

在http://www.tcpdump.org/下載下傳libpcap(tcpdump的源碼也可以從這個網站下載下傳)

解壓

./configure

make

sudo make install

2.使用

安裝好libpcap後,我們要使用它啦,先寫一個簡單的程式,并介紹如何使用libpcap庫編譯它:

Makefile:

[plain] view plain copy print ?

  1. all: test.c
  2. gcc -g -Wall -o test test.c -lpcap
  3. clean:
  4. rm -rf *.o test
all: test.c
	gcc -g -Wall -o test test.c -lpcap

clean:
	rm -rf *.o test
           

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

test1.c

[cpp] view plain copy print ?

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

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * device;
  
  device = pcap_lookupdev(errBuf);
  
  if(device)
  {
    printf("success: device: %s\n", device);
  }
  else
  {
    printf("error: %s\n", errBuf);
  }
  
  return 0;
}
           

可以成功編譯,不過運作的時候卻提示找不到libpcap.so.1,因為libpcap.so.1預設安裝到了/usr/local/lib下,我們做一個符号連結到/usr/lib/下即可:

libpcap 使用

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

libpcap 使用

下面開始正式講解如何使用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 print ?

  1. struct pcap_pkthdr
  2. {
  3. struct timeval ts;
  4. bpf_u_int32 caplen;
  5. bpf_u_int32 len;
  6. };
struct pcap_pkthdr
{
  struct timeval ts;    /* time stamp */
  bpf_u_int32 caplen;   /* length of portion present */
  bpf_u_int32 len;      /* length this packet (off wire) */
};
           

注意這個函數隻要收到一個資料包後就會立即傳回

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 print ?

  1. #include <pcap.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. int main()
  6. {
  7. char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  8. devStr = pcap_lookupdev(errBuf);
  9. if(devStr)
  10. {
  11. printf("success: device: %s\n", devStr);
  12. }
  13. else
  14. {
  15. printf("error: %s\n", errBuf);
  16. exit(1);
  17. }
  18. pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  19. if(!device)
  20. {
  21. printf("error: pcap_open_live(): %s\n", errBuf);
  22. exit(1);
  23. }
  24. struct pcap_pkthdr packet;
  25. const u_char * pktStr = pcap_next(device, &packet);
  26. if(!pktStr)
  27. {
  28. printf("did not capture a packet!\n");
  29. exit(1);
  30. }
  31. printf("Packet length: %d\n", packet.len);
  32. printf("Number of bytes: %d\n", packet.caplen);
  33. printf("Recieved time: %s\n", ctime((consttime_t *)&packet.ts.tv_sec));
  34. pcap_close(device);
  35. return 0;
  36. }
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }

  /* wait a packet to arrive */
  struct pcap_pkthdr packet;
  const u_char * pktStr = pcap_next(device, &packet);

  if(!pktStr)
  {
    printf("did not capture a packet!\n");
    exit(1);
  }
  
  printf("Packet length: %d\n", packet.len);
  printf("Number of bytes: %d\n", packet.caplen);
  printf("Recieved time: %s\n", ctime((const time_t *)&packet.ts.tv_sec)); 
  
  pcap_close(device);
  
  return 0;
}
           
libpcap 使用

打開兩個終端,先ping 192.168.1.10,由于我們的ip是192.168.1.1,是以我們可以收到廣播的資料包,另一個終端運作test,就會抓到這個包。

下面的這個程式會把收到的資料包内容全部列印出來,運作方式和上一個程式一樣:

test3:

[cpp] view plain copy print ?

  1. #include <pcap.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr,const u_char * packet)
  6. {
  7. int * id = (int *)arg;
  8. printf("id: %d\n", ++(*id));
  9. printf("Packet length: %d\n", pkthdr->len);
  10. printf("Number of bytes: %d\n", pkthdr->caplen);
  11. printf("Recieved time: %s", ctime((consttime_t *)&pkthdr->ts.tv_sec));
  12. int i;
  13. for(i=0; i<pkthdr->len; ++i)
  14. {
  15. printf(" %02x", packet[i]);
  16. if( (i + 1) % 16 == 0 )
  17. {
  18. printf("\n");
  19. }
  20. }
  21. printf("\n\n");
  22. }
  23. int main()
  24. {
  25. char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  26. devStr = pcap_lookupdev(errBuf);
  27. if(devStr)
  28. {
  29. printf("success: device: %s\n", devStr);
  30. }
  31. else
  32. {
  33. printf("error: %s\n", errBuf);
  34. exit(1);
  35. }
  36. pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  37. if(!device)
  38. {
  39. printf("error: pcap_open_live(): %s\n", errBuf);
  40. exit(1);
  41. }
  42. int id = 0;
  43. pcap_loop(device, -1, getPacket, (u_char*)&id);
  44. pcap_close(device);
  45. return 0;
  46. }
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
  int * id = (int *)arg;
  
  printf("id: %d\n", ++(*id));
  printf("Packet length: %d\n", pkthdr->len);
  printf("Number of bytes: %d\n", pkthdr->caplen);
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); 
  
  int i;
  for(i=0; i<pkthdr->len; ++i)
  {
    printf(" %02x", packet[i]);
    if( (i + 1) % 16 == 0 )
    {
      printf("\n");
    }
  }
  
  printf("\n\n");
}

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }
  
  /* wait loop forever */
  int id = 0;
  pcap_loop(device, -1, getPacket, (u_char*)&id);
  
  pcap_close(device);

  return 0;
}
           
libpcap 使用

從上圖可以看出,如果我們沒有按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 print ?

  1. #include <pcap.h>
  2. #include <time.h>
  3. #include <stdlib.h>
  4. #include <stdio.h>
  5. void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr,const u_char * packet)
  6. {
  7. int * id = (int *)arg;
  8. printf("id: %d\n", ++(*id));
  9. printf("Packet length: %d\n", pkthdr->len);
  10. printf("Number of bytes: %d\n", pkthdr->caplen);
  11. printf("Recieved time: %s", ctime((consttime_t *)&pkthdr->ts.tv_sec));
  12. int i;
  13. for(i=0; i<pkthdr->len; ++i)
  14. {
  15. printf(" %02x", packet[i]);
  16. if( (i + 1) % 16 == 0 )
  17. {
  18. printf("\n");
  19. }
  20. }
  21. printf("\n\n");
  22. }
  23. int main()
  24. {
  25. char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  26. devStr = pcap_lookupdev(errBuf);
  27. if(devStr)
  28. {
  29. printf("success: device: %s\n", devStr);
  30. }
  31. else
  32. {
  33. printf("error: %s\n", errBuf);
  34. exit(1);
  35. }
  36. pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  37. if(!device)
  38. {
  39. printf("error: pcap_open_live(): %s\n", errBuf);
  40. exit(1);
  41. }
  42. struct bpf_program filter;
  43. pcap_compile(device, &filter, "dst port 80", 1, 0);
  44. pcap_setfilter(device, &filter);
  45. int id = 0;
  46. pcap_loop(device, -1, getPacket, (u_char*)&id);
  47. pcap_close(device);
  48. return 0;
  49. }
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>

void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
  int * id = (int *)arg;
  
  printf("id: %d\n", ++(*id));
  printf("Packet length: %d\n", pkthdr->len);
  printf("Number of bytes: %d\n", pkthdr->caplen);
  printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); 
  
  int i;
  for(i=0; i<pkthdr->len; ++i)
  {
    printf(" %02x", packet[i]);
    if( (i + 1) % 16 == 0 )
    {
      printf("\n");
    }
  }
  
  printf("\n\n");
}

int main()
{
  char errBuf[PCAP_ERRBUF_SIZE], * devStr;
  
  /* get a device */
  devStr = pcap_lookupdev(errBuf);
  
  if(devStr)
  {
    printf("success: device: %s\n", devStr);
  }
  else
  {
    printf("error: %s\n", errBuf);
    exit(1);
  }
  
  /* open a device, wait until a packet arrives */
  pcap_t * device = pcap_open_live(devStr, 65535, 1, 0, errBuf);
  
  if(!device)
  {
    printf("error: pcap_open_live(): %s\n", errBuf);
    exit(1);
  }
  
  /* construct a filter */
  struct bpf_program filter;
  pcap_compile(device, &filter, "dst port 80", 1, 0);
  pcap_setfilter(device, &filter);
  
  /* wait loop forever */
  int id = 0;
  pcap_loop(device, -1, getPacket, (u_char*)&id);
  
  pcap_close(device);

  return 0;
}
           

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

伺服器的ip是192.168.56.101,客戶機的ip是192.168.56.1

伺服器:

libpcap 使用

Makefile:

[plain] view plain copy print ?

  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. clean:
  5. rm -rf *.o tcp_client tcp_server
all: tcp_client.c tcp_server.c
	gcc -g -Wall -o tcp_client tcp_client.c
	gcc -g -Wall -o tcp_server tcp_server.c

clean:
	rm -rf *.o tcp_client tcp_server
           

tcp_server:

[cpp] view plain copy print ?

  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. #define PORT 9832
  9. #define SERVER_IP "192.168.56.101"
  10. int main()
  11. {
  12. int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  13. struct sockaddr_in server_addr;
  14. server_addr.sin_family = AF_INET;
  15. server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
  16. server_addr.sin_port = htons(PORT);
  17. bind(server_sockfd, (struct sockaddr *)&server_addr,sizeof(server_addr));
  18. listen(server_sockfd, 5);
  19. char ch;
  20. int client_sockfd;
  21. struct sockaddr_in client_addr;
  22. socklen_t len = sizeof(client_addr);
  23. while(1)
  24. {
  25. printf("server waiting:\n");
  26. client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
  27. read(client_sockfd, &ch, 1);
  28. printf("get char from client: %c\n", ch);
  29. ++ch;
  30. write(client_sockfd, &ch, 1);
  31. close(client_sockfd);
  32. }
  33. return 0;
  34. }
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PORT 9832
#define SERVER_IP "192.168.56.101"

int main()
{
  /* create a socket */
  int server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
  
  struct sockaddr_in server_addr;
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = inet_addr(SERVER_IP);
  server_addr.sin_port = htons(PORT);
  
  /* bind with the local file */
  bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  
  /* listen */
  listen(server_sockfd, 5);
  
  char ch;
  int client_sockfd;
  struct sockaddr_in client_addr;
  socklen_t len = sizeof(client_addr);
  while(1)
  {
    printf("server waiting:\n");
    
    /* accept a connection */
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);
    
    /* exchange data */
    read(client_sockfd, &ch, 1);
    printf("get char from client: %c\n", ch);
    ++ch;
    write(client_sockfd, &ch, 1);
    
    /* close the socket */
    close(client_sockfd);
  }
  
  return 0;
}
           

tcp_client:

[cpp] view plain copy print ?

  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. #define PORT 9832
  9. #define SERVER_IP "192.168.56.101"
  10. int main()
  11. {
  12. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  13. struct sockaddr_in address;
  14. address.sin_family = AF_INET;
  15. address.sin_addr.s_addr = inet_addr(SERVER_IP);
  16. address.sin_port = htons(PORT);
  17. int result = connect(sockfd, (struct sockaddr *)&address,sizeof(address));
  18. if(result == -1)
  19. {
  20. perror("connect failed: ");
  21. exit(1);
  22. }
  23. char ch = 'A';
  24. write(sockfd, &ch, 1);
  25. read(sockfd, &ch, 1);
  26. printf("get char from server: %c\n", ch);
  27. close(sockfd);
  28. return 0;
  29. }
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define PORT 9832
#define SERVER_IP "192.168.56.101"

int main()
{
  /* create a socket */
  int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  
  struct sockaddr_in address;
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = inet_addr(SERVER_IP);
  address.sin_port = htons(PORT);
  
  /* connect to the server */
  int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));
  if(result == -1)
  {
    perror("connect failed: ");
    exit(1);
  }
  
  /* exchange data */
  char ch = 'A';
  write(sockfd, &ch, 1);
  read(sockfd, &ch, 1);
  printf("get char from server: %c\n", ch);
  
  /* close the socket */
  close(sockfd);
  
  return 0;
}
           

運作方法如下,首先在伺服器上運作tcp_server,然後運作我們的監聽器,然後在客戶機上運作tcp_client,注意,我們可以先清空arp緩存,這樣就可以看到整個通信過程(包括一開始的arp廣播)

在客戶機上運作下列指令來清空記錄伺服器的arp緩存:

sudo arp -d 192.168.56.101

arp -a後發現已經删除了記錄伺服器的arp緩存

抓包的結果如下所示,由于包太多了,無法全部截圖,是以我把所有内容儲存在下面的文本中了:

libpcap 使用

全部的包如下:

[plain] view plain copy print ?

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

id: 2
Packet length: 42
Number of bytes: 42
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 06 00 01
 08 00 06 04 00 02 08 00 27 9c ff b1 c0 a8 38 65
 0a 00 27 00 00 00 c0 a8 38 01

id: 3
Packet length: 74
Number of bytes: 74
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 3c d4 af 40 00 40 06 74 55 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8c 00 00 00 00 a0 02
 39 08 d4 13 00 00 02 04 05 b4 04 02 08 0a 00 14
 b7 23 00 00 00 00 01 03 03 06

id: 4
Packet length: 74
Number of bytes: 74
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 3c 00 00 40 00 40 06 49 05 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e5 79 e1 63 8d a0 12
 38 90 f1 e5 00 00 02 04 05 b4 04 02 08 0a 00 57
 a1 2c 00 14 b7 23 01 03 03 05

id: 5
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b0 40 00 40 06 74 5c c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 10
 00 e5 fb c1 00 00 01 01 08 0a 00 14 b7 24 00 57
 a1 2c

id: 6
Packet length: 67
Number of bytes: 67
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 35 d4 b1 40 00 40 06 74 5a c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8d b6 c4 e6 e6 80 18
 00 e5 ba b7 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2c 41

id: 7
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 cb 40 00 40 06 01 42 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 10
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 8
Packet length: 67
Number of bytes: 67
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 35 47 cc 40 00 40 06 01 40 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e6 79 e1 63 8e 80 18
 01 c5 f1 de 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25 42

id: 9
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 cd 40 00 40 06 01 40 c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e7 79 e1 63 8e 80 11
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 10
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b2 40 00 40 06 74 5a c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 10
 00 e5 fb bc 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2e

id: 11
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b3 40 00 40 06 74 59 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8e b6 c4 e6 e7 80 11
 00 e5 fb bb 00 00 01 01 08 0a 00 14 b7 25 00 57
 a1 2e

id: 12
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 0a 00 27 00 00 00 08 00 27 9c ff b1 08 00 45 00
 00 34 47 ce 40 00 40 06 01 3f c0 a8 38 65 c0 a8
 38 01 26 68 8e 20 b6 c4 e6 e8 79 e1 63 8f 80 10
 01 c5 f1 dd 00 00 01 01 08 0a 00 57 a1 2e 00 14
 b7 25

id: 13
Packet length: 66
Number of bytes: 66
Recieved time: Sat Apr 28 19:57:50 2012
 08 00 27 9c ff b1 0a 00 27 00 00 00 08 00 45 00
 00 34 d4 b4 40 00 40 06 74 58 c0 a8 38 01 c0 a8
 38 65 8e 20 26 68 79 e1 63 8f b6 c4 e6 e8 80 10
 00 e5 fb b9 00 00 01 01 08 0a 00 14 b7 26 00 57
 a1 2e
           

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

下面的這個程式可以擷取eth0的ip和子網路遮罩等資訊:

test5:

[cpp] view plain copy print ?

  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. int main()
  8. {
  9. char * dev;
  10. char errbuf[PCAP_ERRBUF_SIZE];
  11. dev = pcap_lookupdev(errbuf);
  12. if(!dev)
  13. {
  14. printf("pcap_lookupdev() error: %s\n", errbuf);
  15. exit(1);
  16. }
  17. printf("dev name: %s\n", dev);
  18. bpf_u_int32 netp;
  19. bpf_u_int32 maskp;
  20. int ret;
  21. ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);
  22. if(ret == -1)
  23. {
  24. printf("pcap_lookupnet() error: %s\n", errbuf);
  25. exit(1);
  26. }
  27. char * net;
  28. char * mask;
  29. struct in_addr addr;
  30. addr.s_addr = netp;
  31. net = inet_ntoa(addr);
  32. if(!net)
  33. {
  34. perror("inet_ntoa() ip error: ");
  35. exit(1);
  36. }
  37. printf("ip: %s\n", net);
  38. addr.s_addr = maskp;
  39. mask = inet_ntoa(addr);
  40. if(!mask)
  41. {
  42. perror("inet_ntoa() sub mask error: ");
  43. exit(1);
  44. }
  45. printf("sub mask: %s\n", mask);
  46. return 0;
  47. }
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
  /* ask pcap to find a valid device for use to sniff on */
  char * dev;   /* name of the device */ 
  char errbuf[PCAP_ERRBUF_SIZE];
  dev = pcap_lookupdev(errbuf);

  /* error checking */
  if(!dev)
  {
    printf("pcap_lookupdev() error: %s\n", errbuf);
    exit(1);
  }

  /* print out device name */
  printf("dev name: %s\n", dev);

  /* ask pcap for the network address and mask of the device */
  bpf_u_int32 netp;   /* ip */
  bpf_u_int32 maskp;  /* subnet mask */
  int ret;            /* return code */
  ret = pcap_lookupnet(dev, &netp, &maskp, errbuf);

  if(ret == -1)
  {
    printf("pcap_lookupnet() error: %s\n", errbuf);
    exit(1);
  }

  /* get the network address in a human readable form */
  char * net;   /* dot notation of the network address */
  char * mask;  /* dot notation of the network mask */
  struct in_addr addr;

  addr.s_addr = netp;
  net = inet_ntoa(addr);

  if(!net)
  {
    perror("inet_ntoa() ip error: ");
    exit(1);
  }

  printf("ip: %s\n", net);

  /* do the same as above for the device's mask */
  addr.s_addr = maskp;
  mask = inet_ntoa(addr);
  
  if(!mask)
  {
    perror("inet_ntoa() sub mask error: ");
    exit(1);
  }
  
  printf("sub mask: %s\n", mask);

  return 0;
}
           

結果如圖:

libpcap 使用

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

繼續閱讀