WinpCap的详解(一)

转自 : http://www.cnblogs.com/yingfang18/archive/2010/11/27/1889596.html


首先来百科一下Winpcap是一个什么东东。Winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。

  它有如下几个功能:

  1、捕获原始数据包,包括在共享网络上各主机发送/接收的以及相互之间交换的数据;

  2、在数据包发往应用程序之前,按照自定义的规则将某些特殊的数据包过滤掉;

  3、在网络上发送原始的数据包;

  4、收集网络通信过程中的统计信息。

      从上面的功能来看,这个库文件提供了许多的API函数,可以让我们捕获网络上的数据包以及统计网络通信的信息。为了更直观的反应这个库文件的作用,我们来看看利用这个库文件写出来的一个应用软件,wireshark。界面如下图所示,这个界面只是捕获数据的一个小界面,里面有很多的设置,有兴趣可以下载一个试试。他能统计在一个局域网的所有网络信息。

  这里面重要一点,需要提醒的是:winpcap的主要功能在于独立于主机协议(如TCP-IP)而发送和接收原始数据包。也就是说,winpcap不能阻塞,过滤或控制其他应用程序数据包的发收,它仅仅只是监听共享网络上传送的数据包。也就是说,WinpCap主要功能不能截取网络中的数据,他只能监听里面的数据。

  对于WinpCap的结构以及原理,我们自然可以不用理会啦,我们只需要知道他的用途就行啦!

一、安装WinpCap

  1、首先我们来看看如何安装WinpCap这个库,首先是下载WinpCap安装文件,这里有许多的版本,可以在官网上下载,http://www.winpcap.org/,这里重点提醒一下,特别需要注意一下版本,如果你的版本是4.02,那么你的安装包也必须下载对应的版本,这里特别注意下,你可以下载当前比较稳定的版本。下载之后安装就ok啦!这里我用的是WinpCap4.02.

  2、下载WinpCap Develop‘s Packs,这里我也提供相同的版本WpdPack4.02.

  3、解压后会得一个目录WpdPack四个子目录:
  docs
  Examples-pcap
  Examples-remote
  Include
  Lib
  然后配置VC++
  tools --> options --> Projects and Solutions --> VC++ Directories :

  Include files :WpdPackPath\include

  Library files: WpdPackPath\lib

  4、经过上面的步骤之后,你的WinpCap应该就安装成功啦,之后就是运行一下里面提供的例程啦,如果有什么问题,就对应的把问题在网上查一查,总体来说有以下几个问题:第一个就是需要在工程的链接库上添加wpcap.lib链接库;第二个就是你的SDK太老了,需要添加更新你的SDK,相应的到官方网站上下载适合你电脑的SDK。这里面查错的能力就是大家查找相关的网站,只要把错误的英语输入google和百度就大部分能找到原因。这里提供一个别人的博客,里面写的挺详细的WinPcap 常见安装和运行错误

二、各种功能的实现

  这里面有许多的例子,但是大部分例子都是建立在一定的基础上的,首先我们来看看几个基本的函数。这里推荐一个好的网站,里面有这个库所有解释。

  1、pcap_if,和pcap_if_t是一样的

/*
 * Item in a list of interfaces.
 */
struct pcap_if {
 struct pcap_if *next;
 char *name;  /* name to hand to "pcap_open_live()" */
 char *description; /* textual description of interface, or NULL */
 struct pcap_addr *addresses;
 bpf_u_int32 flags; /* PCAP_IF_ interface flags */
};

  从上面的结构体定义可以看到,有五个元素。第一是一个pcap_if的链表指向下一个设备接口;第二个是设备的实际的名字,这个名字是机器能识别的名字,供pcap_open_live()调用;第三个是设备的文本描述符,这个描述符就是人们能够识别的文本符号;第四个是一个地址指针,指向的是一系列接口的第一个指针;第五个是一个标志位,目前这个标志位主要是不是loopback设备。

  2 用户定义了两个类型

typedef struct pcap pcap_t; 一个已打开的捕获实例描述符,这个结构体对用户来说是不透明的,他提供wpcap.dll的函数来维护他的内容。
typedef struct pcap_addr pcap_addr_t;接口地址。

  3 一个数据包在堆文件中的文件头,包括时间戳,目前部分的长度和数据包的长度

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) */
};

 1、获取设备列表

  通常,编写基于WinPcap应用程序的第一件事情,就是获得已连接的网络适配器列表。libpcap和WinPcap都提供了 pcap_findalldevs_ex() 函数来实现这个功能: 这个函数返回一个 pcap_if 结构的链表, 每个这样的结构都包含了一个适配器的详细信息。值得注意的是,数据域 name 和 description 表示一个适配器名称和一个可以让人们理解的描述。

  来看看下面简单的一个代码:

  这个代码很简单首先利用一个API函数获得机器的所有网络设备列表,接着一个个打印出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "pcap.h"
 
main()
{
     pcap_if_t *alldevs;
     pcap_if_t *d;
     int  i=0;
     char  errbuf[PCAP_ERRBUF_SIZE];
     
     /* 获取本地机器设备列表 */
     if  (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */ , &alldevs, errbuf) == -1)
     {
         fprintf (stderr, "Error in pcap_findalldevs_ex: %s\n" , errbuf);
         exit (1);
     }
     
     /* 打印列表 */
     for (d= alldevs; d != NULL; d= d->next)
     {
         printf ( "%d. %s" , ++i, d->name);
         if  (d->description)
             printf ( " (%s)\n" , d->description);
         else
             printf ( " (No description available)\n" );
     }
     
     if  (i == 0)
     {
         printf ( "\nNo interfaces found! Make sure WinPcap is installed.\n" );
         return ;
     }
 
     /* 不再需要设备列表了,释放它 */
     pcap_freealldevs(alldevs);
}

  2、获取已安装设备的高级信息

  在第1讲中, (获取设备列表) 我们展示了如何获取适配器的基本信息 (如设备的名称和描述)。 事实上,WinPcap提供了其他更高级的信息。 特别需要指出的是, 由 pcap_findalldevs_ex() 返回的每一个 pcap_if 结构体,都包含一个 pcap_addr 结构体,这个结构体由如下元素组成:

  • 一个地址列表
  • 一个掩码列表 (each of which corresponds to an entry in the addresses list).
  • 一个广播地址列表 (each of which corresponds to an entry in the addresses list).
  • 一个目的地址列表 (each of which corresponds to an entry in the addresses list).

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "pcap.h"
 
#ifndef WIN32
     #include <sys/socket.h>
     #include <netinet/in.h>
#else
     #include <winsock.h>
#endif
 
 
// 函数原型
void  ifprint(pcap_if_t *d);
char  *iptos(u_long in);
char * ip6tos( struct  sockaddr *sockaddr, char  *address, int  addrlen);
 
 
int  main()
{
   pcap_if_t *alldevs;
   pcap_if_t *d;
   char  errbuf[PCAP_ERRBUF_SIZE+1];
   char  source[PCAP_ERRBUF_SIZE+1];
 
   printf ( "Enter the device you want to list:\n"
             "rpcap://              ==> lists interfaces in the local machine\n"
             "rpcap://hostname:port ==> lists interfaces in a remote machine\n"
             "                          (rpcapd daemon must be up and running\n"
             "                           and it must accept 'null' authentication)\n"
             "file://foldername     ==> lists all pcap files in the give folder\n\n"
             "Enter your choice: " );
 
   fgets (source, PCAP_ERRBUF_SIZE, stdin);
   source[PCAP_ERRBUF_SIZE] = '\0' ;
 
   /* 获得接口列表 */
   if  (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1)
   {
     fprintf (stderr, "Error in pcap_findalldevs: %s\n" ,errbuf);
     exit (1);
   }
 
   /* 扫描列表并打印每一项 */
   for (d=alldevs;d;d=d->next)
   {
     ifprint(d);
   }
 
   pcap_freealldevs(alldevs);
 
   return  1;
}
 
 
 
/* 打印所有可用信息 */
void  ifprint(pcap_if_t *d)
{
   pcap_addr_t *a;
   char  ip6str[128];
 
   /* 设备名(Name) */
   printf ( "%s\n" ,d->name);
 
   /* 设备描述(Description) */
   if  (d->description)
     printf ( "\tDescription: %s\n" ,d->description);
 
   /* Loopback Address*/
   printf ( "\tLoopback: %s\n" ,(d->flags & PCAP_IF_LOOPBACK)? "yes" : "no" );
 
   /* IP addresses */
   for (a=d->addresses;a;a=a->next) {
     printf ( "\tAddress Family: #%d\n" ,a->addr->sa_family);
   
     switch (a->addr->sa_family)
     {
       case  AF_INET:
         printf ( "\tAddress Family Name: AF_INET\n" );
         if  (a->addr)
           printf ( "\tAddress: %s\n" ,iptos((( struct  sockaddr_in *)a->addr)->sin_addr.s_addr));
         if  (a->netmask)
           printf ( "\tNetmask: %s\n" ,iptos((( struct  sockaddr_in *)a->netmask)->sin_addr.s_addr));
         if  (a->broadaddr)
           printf ( "\tBroadcast Address: %s\n" ,iptos((( struct  sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
         if  (a->dstaddr)
           printf ( "\tDestination Address: %s\n" ,iptos((( struct  sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
         break ;
 
       case  AF_INET6:
         printf ( "\tAddress Family Name: AF_INET6\n" );
         if  (a->addr)
           printf ( "\tAddress: %s\n" , ip6tos(a->addr, ip6str, sizeof (ip6str)));
        break ;
 
       default :
         printf ( "\tAddress Family Name: Unknown\n" );
         break ;
     }
   }
   printf ( "\n" );
}
 
 
 
/* 将数字类型的IP地址转换成字符串类型的 */
#define IPTOSBUFFERS    12
char  *iptos(u_long in)
{
     static  char  output[IPTOSBUFFERS][3*4+3+1];
     static  short  which;
     u_char *p;
 
     p = (u_char *)&in;
     which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
     sprintf (output[which], "%d.%d.%d.%d" , p[0], p[1], p[2], p[3]);
     return  output[which];
}
 
char * ip6tos( struct  sockaddr *sockaddr, char  *address, int  addrlen)
{
     socklen_t sockaddrlen;
 
     #ifdef WIN32
     sockaddrlen = sizeof ( struct  sockaddr_in6);
     #else
     sockaddrlen = sizeof ( struct  sockaddr_storage);
     #endif
 
 
     if (getnameinfo(sockaddr,
         sockaddrlen,
         address,
         addrlen,
         NULL,
         0,
         NI_NUMERICHOST) != 0) address = NULL;
 
     return  address;
}

  代码里面都有解释,很好理解,而这些API函数如果不明白,都可以MSDN就行啦!

3、打开适配器并捕获数据包(利用回调函数实现数据包捕获)

  现在,我们已经知道如何获取适配器的信息了,那我们就开始一项更具意义的工作,打开适配器并捕获数据包。在这讲中,我们会编写一个程序,将每一个通过适配器的数据包打印出来。

  打开设备的函数是 pcap_open()。下面是参数 snaplenflags 和 to_ms 的解释说明。

snaplen 制定要捕获数据包中的哪些部分。 在一些操作系统中 (比如 xBSD 和 Win32), 驱动可以被配置成只捕获数据包的初始化部分: 这样可以减少应用程序间复制数据的量,从而

    提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。

flags:  最最重要的flag是用来指示适配器是否要被设置成混杂模式。 一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃。 相反,如果适

    配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。 这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他

    主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。

to_ms 指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用 pcap_dispatch() 或 pcap_next_ex()) 都会在 to_ms 毫秒时间内响应,即使在网络上没

    有可用的数据包。 在统计模式下,to_ms 还可以用来定义统计的时间间隔。 将 to_ms 设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。 如果设

    置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。

 

来看看下面的代码,利用回调函数实现数据捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include "pcap.h"
 
/* packet handler 函数原型 */
void  packet_handler(u_char *param, const  struct  pcap_pkthdr *header, const  u_char *pkt_data);
 
main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int  inum;
int  i=0;
pcap_t *adhandle;
char  errbuf[PCAP_ERRBUF_SIZE];
     
     /* 获取本机设备列表 */
     if  (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
     {
         fprintf (stderr, "Error in pcap_findalldevs: %s\n" , errbuf);
         exit (1);
     }
     
     /* 打印列表 */
     for (d=alldevs; d; d=d->next)
     {
         printf ( "%d. %s" , ++i, d->name);
         if  (d->description)
             printf ( " (%s)\n" , d->description);
         else
             printf ( " (No description available)\n" );
     }
     
     if (i==0)
     {
         printf ( "\nNo interfaces found! Make sure WinPcap is installed.\n" );
         return  -1;
     }
     
     printf ( "Enter the interface number (1-%d):" ,i);
     scanf ( "%d" , &inum);
     
     if (inum < 1 || inum > i)
     {
         printf ( "\nInterface number out of range.\n" );
         /* 释放设备列表 */
         pcap_freealldevs(alldevs);
         return  -1;
     }
     
     /* 跳转到选中的适配器 */
     for (d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
     
     /* 打开设备 */
     if  ( (adhandle= pcap_open(d->name,          // 设备名
                               65536,            // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                                  PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
                               1000,             // 读取超时时间
                               NULL,             // 远程机器验证
                               errbuf            // 错误缓冲池
                               ) ) == NULL)
     {
         fprintf (stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n" , d->name);
         /* 释放设备列表 */
         pcap_freealldevs(alldevs);
         return  -1;
     }
     
     printf ( "\nlistening on %s...\n" , d->description);
     
     /* 释放设备列表 */
     pcap_freealldevs(alldevs);
     
     /* 开始捕获 */
     pcap_loop(adhandle, 0, packet_handler, NULL);
     
     return  0;
}
 
 
/* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
void  packet_handler(u_char *param, const  struct  pcap_pkthdr *header, const  u_char *pkt_data)
{
     struct  tm  *ltime;
     char  timestr[16];
     time_t  local_tv_sec;
     
     /* 将时间戳转换成可识别的格式 */
     local_tv_sec = header->ts.tv_sec;
     ltime= localtime (&local_tv_sec);
     strftime ( timestr, sizeof  timestr, "%H:%M:%S" , ltime);
     
     printf ( "%s,%.6d len:%d\n" , timestr, header->ts.tv_usec, header->len);
     
}

 待续......

首先来百科一下Winpcap是一个什么东东。Winpcap(windows packet capture)是windows平台下一个免费,公共的网络访问系统。

  它有如下几个功能:

  1、捕获原始数据包,包括在共享网络上各主机发送/接收的以及相互之间交换的数据;

  2、在数据包发往应用程序之前,按照自定义的规则将某些特殊的数据包过滤掉;

  3、在网络上发送原始的数据包;

  4、收集网络通信过程中的统计信息。

      从上面的功能来看,这个库文件提供了许多的API函数,可以让我们捕获网络上的数据包以及统计网络通信的信息。为了更直观的反应这个库文件的作用,我们来看看利用这个库文件写出来的一个应用软件,wireshark。界面如下图所示,这个界面只是捕获数据的一个小界面,里面有很多的设置,有兴趣可以下载一个试试。他能统计在一个局域网的所有网络信息。

  这里面重要一点,需要提醒的是:winpcap的主要功能在于独立于主机协议(如TCP-IP)而发送和接收原始数据包。也就是说,winpcap不能阻塞,过滤或控制其他应用程序数据包的发收,它仅仅只是监听共享网络上传送的数据包。也就是说,WinpCap主要功能不能截取网络中的数据,他只能监听里面的数据。

  对于WinpCap的结构以及原理,我们自然可以不用理会啦,我们只需要知道他的用途就行啦!

一、安装WinpCap

  1、首先我们来看看如何安装WinpCap这个库,首先是下载WinpCap安装文件,这里有许多的版本,可以在官网上下载,http://www.winpcap.org/,这里重点提醒一下,特别需要注意一下版本,如果你的版本是4.02,那么你的安装包也必须下载对应的版本,这里特别注意下,你可以下载当前比较稳定的版本。下载之后安装就ok啦!这里我用的是WinpCap4.02.

  2、下载WinpCap Develop‘s Packs,这里我也提供相同的版本WpdPack4.02.

  3、解压后会得一个目录WpdPack四个子目录:
  docs
  Examples-pcap
  Examples-remote
  Include
  Lib
  然后配置VC++
  tools --> options --> Projects and Solutions --> VC++ Directories :

  Include files :WpdPackPath\include

  Library files: WpdPackPath\lib

  4、经过上面的步骤之后,你的WinpCap应该就安装成功啦,之后就是运行一下里面提供的例程啦,如果有什么问题,就对应的把问题在网上查一查,总体来说有以下几个问题:第一个就是需要在工程的链接库上添加wpcap.lib链接库;第二个就是你的SDK太老了,需要添加更新你的SDK,相应的到官方网站上下载适合你电脑的SDK。这里面查错的能力就是大家查找相关的网站,只要把错误的英语输入google和百度就大部分能找到原因。这里提供一个别人的博客,里面写的挺详细的WinPcap 常见安装和运行错误

二、各种功能的实现

  这里面有许多的例子,但是大部分例子都是建立在一定的基础上的,首先我们来看看几个基本的函数。这里推荐一个好的网站,里面有这个库所有解释。

  1、pcap_if,和pcap_if_t是一样的

/*
 * Item in a list of interfaces.
 */
struct pcap_if {
 struct pcap_if *next;
 char *name;  /* name to hand to "pcap_open_live()" */
 char *description; /* textual description of interface, or NULL */
 struct pcap_addr *addresses;
 bpf_u_int32 flags; /* PCAP_IF_ interface flags */
};

  从上面的结构体定义可以看到,有五个元素。第一是一个pcap_if的链表指向下一个设备接口;第二个是设备的实际的名字,这个名字是机器能识别的名字,供pcap_open_live()调用;第三个是设备的文本描述符,这个描述符就是人们能够识别的文本符号;第四个是一个地址指针,指向的是一系列接口的第一个指针;第五个是一个标志位,目前这个标志位主要是不是loopback设备。

  2 用户定义了两个类型

typedef struct pcap pcap_t; 一个已打开的捕获实例描述符,这个结构体对用户来说是不透明的,他提供wpcap.dll的函数来维护他的内容。
typedef struct pcap_addr pcap_addr_t;接口地址。

  3 一个数据包在堆文件中的文件头,包括时间戳,目前部分的长度和数据包的长度

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) */
};

 1、获取设备列表

  通常,编写基于WinPcap应用程序的第一件事情,就是获得已连接的网络适配器列表。libpcap和WinPcap都提供了 pcap_findalldevs_ex() 函数来实现这个功能: 这个函数返回一个 pcap_if 结构的链表, 每个这样的结构都包含了一个适配器的详细信息。值得注意的是,数据域 name 和 description 表示一个适配器名称和一个可以让人们理解的描述。

  来看看下面简单的一个代码:

  这个代码很简单首先利用一个API函数获得机器的所有网络设备列表,接着一个个打印出来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include "pcap.h"
 
main()
{
     pcap_if_t *alldevs;
     pcap_if_t *d;
     int  i=0;
     char  errbuf[PCAP_ERRBUF_SIZE];
     
     /* 获取本地机器设备列表 */
     if  (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */ , &alldevs, errbuf) == -1)
     {
         fprintf (stderr, "Error in pcap_findalldevs_ex: %s\n" , errbuf);
         exit (1);
     }
     
     /* 打印列表 */
     for (d= alldevs; d != NULL; d= d->next)
     {
         printf ( "%d. %s" , ++i, d->name);
         if  (d->description)
             printf ( " (%s)\n" , d->description);
         else
             printf ( " (No description available)\n" );
     }
     
     if  (i == 0)
     {
         printf ( "\nNo interfaces found! Make sure WinPcap is installed.\n" );
         return ;
     }
 
     /* 不再需要设备列表了,释放它 */
     pcap_freealldevs(alldevs);
}

  2、获取已安装设备的高级信息

  在第1讲中, (获取设备列表) 我们展示了如何获取适配器的基本信息 (如设备的名称和描述)。 事实上,WinPcap提供了其他更高级的信息。 特别需要指出的是, 由 pcap_findalldevs_ex() 返回的每一个 pcap_if 结构体,都包含一个 pcap_addr 结构体,这个结构体由如下元素组成:

  • 一个地址列表
  • 一个掩码列表 (each of which corresponds to an entry in the addresses list).
  • 一个广播地址列表 (each of which corresponds to an entry in the addresses list).
  • 一个目的地址列表 (each of which corresponds to an entry in the addresses list).

  

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include "pcap.h"
 
#ifndef WIN32
     #include <sys/socket.h>
     #include <netinet/in.h>
#else
     #include <winsock.h>
#endif
 
 
// 函数原型
void  ifprint(pcap_if_t *d);
char  *iptos(u_long in);
char * ip6tos( struct  sockaddr *sockaddr, char  *address, int  addrlen);
 
 
int  main()
{
   pcap_if_t *alldevs;
   pcap_if_t *d;
   char  errbuf[PCAP_ERRBUF_SIZE+1];
   char  source[PCAP_ERRBUF_SIZE+1];
 
   printf ( "Enter the device you want to list:\n"
             "rpcap://              ==> lists interfaces in the local machine\n"
             "rpcap://hostname:port ==> lists interfaces in a remote machine\n"
             "                          (rpcapd daemon must be up and running\n"
             "                           and it must accept 'null' authentication)\n"
             "file://foldername     ==> lists all pcap files in the give folder\n\n"
             "Enter your choice: " );
 
   fgets (source, PCAP_ERRBUF_SIZE, stdin);
   source[PCAP_ERRBUF_SIZE] = '\0' ;
 
   /* 获得接口列表 */
   if  (pcap_findalldevs_ex(source, NULL, &alldevs, errbuf) == -1)
   {
     fprintf (stderr, "Error in pcap_findalldevs: %s\n" ,errbuf);
     exit (1);
   }
 
   /* 扫描列表并打印每一项 */
   for (d=alldevs;d;d=d->next)
   {
     ifprint(d);
   }
 
   pcap_freealldevs(alldevs);
 
   return  1;
}
 
 
 
/* 打印所有可用信息 */
void  ifprint(pcap_if_t *d)
{
   pcap_addr_t *a;
   char  ip6str[128];
 
   /* 设备名(Name) */
   printf ( "%s\n" ,d->name);
 
   /* 设备描述(Description) */
   if  (d->description)
     printf ( "\tDescription: %s\n" ,d->description);
 
   /* Loopback Address*/
   printf ( "\tLoopback: %s\n" ,(d->flags & PCAP_IF_LOOPBACK)? "yes" : "no" );
 
   /* IP addresses */
   for (a=d->addresses;a;a=a->next) {
     printf ( "\tAddress Family: #%d\n" ,a->addr->sa_family);
   
     switch (a->addr->sa_family)
     {
       case  AF_INET:
         printf ( "\tAddress Family Name: AF_INET\n" );
         if  (a->addr)
           printf ( "\tAddress: %s\n" ,iptos((( struct  sockaddr_in *)a->addr)->sin_addr.s_addr));
         if  (a->netmask)
           printf ( "\tNetmask: %s\n" ,iptos((( struct  sockaddr_in *)a->netmask)->sin_addr.s_addr));
         if  (a->broadaddr)
           printf ( "\tBroadcast Address: %s\n" ,iptos((( struct  sockaddr_in *)a->broadaddr)->sin_addr.s_addr));
         if  (a->dstaddr)
           printf ( "\tDestination Address: %s\n" ,iptos((( struct  sockaddr_in *)a->dstaddr)->sin_addr.s_addr));
         break ;
 
       case  AF_INET6:
         printf ( "\tAddress Family Name: AF_INET6\n" );
         if  (a->addr)
           printf ( "\tAddress: %s\n" , ip6tos(a->addr, ip6str, sizeof (ip6str)));
        break ;
 
       default :
         printf ( "\tAddress Family Name: Unknown\n" );
         break ;
     }
   }
   printf ( "\n" );
}
 
 
 
/* 将数字类型的IP地址转换成字符串类型的 */
#define IPTOSBUFFERS    12
char  *iptos(u_long in)
{
     static  char  output[IPTOSBUFFERS][3*4+3+1];
     static  short  which;
     u_char *p;
 
     p = (u_char *)&in;
     which = (which + 1 == IPTOSBUFFERS ? 0 : which + 1);
     sprintf (output[which], "%d.%d.%d.%d" , p[0], p[1], p[2], p[3]);
     return  output[which];
}
 
char * ip6tos( struct  sockaddr *sockaddr, char  *address, int  addrlen)
{
     socklen_t sockaddrlen;
 
     #ifdef WIN32
     sockaddrlen = sizeof ( struct  sockaddr_in6);
     #else
     sockaddrlen = sizeof ( struct  sockaddr_storage);
     #endif
 
 
     if (getnameinfo(sockaddr,
         sockaddrlen,
         address,
         addrlen,
         NULL,
         0,
         NI_NUMERICHOST) != 0) address = NULL;
 
     return  address;
}

  代码里面都有解释,很好理解,而这些API函数如果不明白,都可以MSDN就行啦!

3、打开适配器并捕获数据包(利用回调函数实现数据包捕获)

  现在,我们已经知道如何获取适配器的信息了,那我们就开始一项更具意义的工作,打开适配器并捕获数据包。在这讲中,我们会编写一个程序,将每一个通过适配器的数据包打印出来。

  打开设备的函数是 pcap_open()。下面是参数 snaplenflags 和 to_ms 的解释说明。

snaplen 制定要捕获数据包中的哪些部分。 在一些操作系统中 (比如 xBSD 和 Win32), 驱动可以被配置成只捕获数据包的初始化部分: 这样可以减少应用程序间复制数据的量,从而

    提高捕获效率。本例中,我们将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。

flags:  最最重要的flag是用来指示适配器是否要被设置成混杂模式。 一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃。 相反,如果适

    配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。 这意味着在一个共享媒介(比如总线型以太网),WinPcap能捕获其他

    主机的所有的数据包。 大多数用于数据捕获的应用程序都会将适配器设置成混杂模式,所以,我们也会在下面的范例中,使用混杂模式。

to_ms 指定读取数据的超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取操作(比如用 pcap_dispatch() 或 pcap_next_ex()) 都会在 to_ms 毫秒时间内响应,即使在网络上没

    有可用的数据包。 在统计模式下,to_ms 还可以用来定义统计的时间间隔。 将 to_ms 设置为0意味着没有超时,那么如果没有数据包到达的话,读操作将永远不会返回。 如果设

    置成-1,则情况恰好相反,无论有没有数据包到达,读操作都会立即返回。

 

来看看下面的代码,利用回调函数实现数据捕获。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include "pcap.h"
 
/* packet handler 函数原型 */
void  packet_handler(u_char *param, const  struct  pcap_pkthdr *header, const  u_char *pkt_data);
 
main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int  inum;
int  i=0;
pcap_t *adhandle;
char  errbuf[PCAP_ERRBUF_SIZE];
     
     /* 获取本机设备列表 */
     if  (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
     {
         fprintf (stderr, "Error in pcap_findalldevs: %s\n" , errbuf);
         exit (1);
     }
     
     /* 打印列表 */
     for (d=alldevs; d; d=d->next)
     {
         printf ( "%d. %s" , ++i, d->name);
         if  (d->description)
             printf ( " (%s)\n" , d->description);
         else
             printf ( " (No description available)\n" );
     }
     
     if (i==0)
     {
         printf ( "\nNo interfaces found! Make sure WinPcap is installed.\n" );
         return  -1;
     }
     
     printf ( "Enter the interface number (1-%d):" ,i);
     scanf ( "%d" , &inum);
     
     if (inum < 1 || inum > i)
     {
         printf ( "\nInterface number out of range.\n" );
         /* 释放设备列表 */
         pcap_freealldevs(alldevs);
         return  -1;
     }
     
     /* 跳转到选中的适配器 */
     for (d=alldevs, i=0; i< inum-1 ;d=d->next, i++);
     
     /* 打开设备 */
     if  ( (adhandle= pcap_open(d->name,          // 设备名
                               65536,            // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
                                  PCAP_OPENFLAG_PROMISCUOUS,    // 混杂模式
                               1000,             // 读取超时时间
                               NULL,             // 远程机器验证
                               errbuf            // 错误缓冲池
                               ) ) == NULL)
     {
         fprintf (stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n" , d->name);
         /* 释放设备列表 */
         pcap_freealldevs(alldevs);
         return  -1;
     }
     
     printf ( "\nlistening on %s...\n" , d->description);
     
     /* 释放设备列表 */
     pcap_freealldevs(alldevs);
     
     /* 开始捕获 */
     pcap_loop(adhandle, 0, packet_handler, NULL);
     
     return  0;
}
 
 
/* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
void  packet_handler(u_char *param, const  struct  pcap_pkthdr *header, const  u_char *pkt_data)
{
     struct  tm  *ltime;
     char  timestr[16];
     time_t  local_tv_sec;
     
     /* 将时间戳转换成可识别的格式 */
     local_tv_sec = header->ts.tv_sec;
     ltime= localtime (&local_tv_sec);
     strftime ( timestr, sizeof  timestr, "%H:%M:%S" , ltime);
     
     printf ( "%s,%.6d len:%d\n" , timestr, header->ts.tv_usec, header->len);
     
}

 待续......


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值