Winpcap学习
Posted swhchwhdh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Winpcap学习相关的知识,希望对你有一定的参考价值。
第一个试验是:
#include <pcap.h>
#include <remote-ext.h>
int main() {
pcap_if_t *alldevs;
pcap_if_t *d;
int i = 0;
char errbuf[PCAP_ERRBUF_SIZE];
/* Retrieve the device list from the local machine*/
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* auth is not needed */, &alldevs, errbuf) == -1)
{
printf("Error in pcap_findalldevs_ex: %s/n", errbuf);
exit(1);
}
/* Print the list */
for (d = alldevs; d != NULL; d = d->next)
{
/* Print the device‘s name */
printf("%d. %s", ++ i, d->name);
/* Print the device‘s dscription */
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 0;
}
/* We don‘t need any more the device list. Free it */
pcap_freealldevs(alldevs);
return 1;
}
编译的时候又遇到问题——“无法打开pcap.h”。又查看开发手册才找到解决方法:
1.安装Winpcap驱动。下载地址:http://www.winpcap.org/install/bin/WinPcap_3_1.exe。
2.将Winpcap的Include,Lib目录添加进VC6.0的环境变量中;
3. 针对每一个项目,先用VC打开项目,然后在"Project->Settings",标签栏出选择"C/C++",在"Preprocessor definitions"的输入框里添加"WPCAP",再选择"Link",在"Object/library modules"的输入框里添加"wpcap.lib Packet.lib"。
再编译时终于OK了。之后,阅读代码并查看开发手册学到了下面的东西:
pcap_if是一个结构体,具体点它是一个链表的结点,他的定义如下:
struct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr *addresses;
u_int flags;
}
另外,在pcap.h中有一句“typedef struct pcap_if pcap_if_t;”,所以也可以用pcap_if_t代替pcap_if。
int pcap_findalldevs_ex(char * source,
struct pcap_rmtauth * auth,
pcap_if_t ** alldevs,
char * errbuf
)
这个函数是’pcap_findalldevs()’的一个超集。’pcap_findalldevs()’比较老,他只允许列出本地机器上的设备。然而,’pcap_findalldevs_ex()’除了可以列出本地及其上的设备,还可以列出远程机器上的设备。此外,它还能列出所有可用的pcap文件到指定的文件夹。’pcap_findalldevs_ex()’是平台无关的,然而它以来于标准的’pcap_findalldevs()’来获得本地机器的地址。
’pcap_findalldevs_ex()’除了可以列出本地及其上的设备,还可以列出远程机器上的设备。
那怎么知道是本地的还是远程的呢?
有什么区别呢?
第一个问题:却别是本地还是远程是由pcap_findalldevs_ex()的第一个参数(source)决定的,source指定所要监控的网络适配器。它有特定的“source语法”。pcap_findalldevs_ex()支持的source语法有一下几种:
- file://folder/[列出指定文件夹中的所有文件]
- rpcap:// [列出所有本地的适配器]
- rpcap://host:port/ [列出远程主机上的可用的设备]
其中,‘host‘和‘port‘参数既可以是数字形式(numeric),又可以是字符形式(literal),下面形式都是合法的:
- host (字符):例如: host.foo.bar
- host (数字 IPv4): 例如: 10.11.12.13
- host (IPv6型的IPv4数字形式): 例如: [10.11.12.13]
- host (数字 IPv6): 例如: [1:2:3::4]
- port: 也可以是数字 (例如:‘80‘) 或字符 (例如: ‘http‘)
下面举一些实际例子:
- rpcap://host.foo.bar/devicename [全部都是字符形式的,没有端口号]
- rpcap://host.foo.bar:1234/devicename [全部都是字符形式的,有端口号]
- rpcap://10.11.12.13/devicename [IPv4 的数字形式,没有端口号]
- rpcap://10.11.12.13:1234/devicename [IPv4 的数字形式,有端口号]
- rpcap://[10.11.12.13]:1234/devicename [IPv6格式的IPv4数字形式 ,有端口号]
- rpcap://[1:2:3::4]/devicename [IPv6数字形式,没有端口号]
- rpcap://[1:2:3::4]:1234/devicename [IPv6数字形式,有端口号]
- rpcap://[1:2:3::4]:http/devicename [IPv6数字形式,端口号是字符形式]
另外,在Winpcap里,字符串"file://"和字符串"rpcap://"都被宏定义过:
#define PCAP_SRC_FILE_STRING "file://"
#define PCAP_SRC_IF_STRING "rpcap://"
所以也可以用宏名来代替它们。
第二个问题:列出本地适配器跟远程适配器的区别(作用),本地的就不多说了,这是Winpcap的基本功能的体现;而列出远程的可用的适配器,就可以建立一个到该远程主机的控制连接,就可以使用该主机上存在的套结字,说通俗点就是可以通过远程主机上的适配器连接到该主机上并对该主机进行控制。
今天在阅读Winpcap Manual的时候发现一句话:
“This means that on shared media (like non-switched Ethernet), WinPcap will be able to capture the packets of other hosts.”
我理解为:如果在通过没有交换功能的集线器连接的网络上,只要把网卡设置为混杂(promiscuous)模式,winpcap能够捕获到其他主机通信的数据包。如果是具有交换功能的集线器连接的网络winpcap还能管用吗?这个在后边的实习中将会进行试验。
试验程序2:
/*
* 截获数据包的试验。先打印出所有网络适配器的列表,然后选择
* 想在哪个适配器上截获数据包。然后通过pcap_loop()函数将截获
* 的数据包传给回调函数packet_handler()处理。
* 通过该程序初步了解了使用winpcap截获数据包的步骤以及一些在
* 截获数据包时非常重要的函数和结构体。
* 2006-1-26
*/
#include <pcap.h>
#include <remote-ext.h>
/* Prototype of the packet handler */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
int main() {
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i = 0;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
/* Retrieve the devices list on the local machine */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s/n", errbuf);
exit(1);
}
/* Print the list */
for (d = alldevs; d; d = d->next)
{
/* Print name */
printf("%d. %s", ++ i, d->name);
/* Print description */
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;
}
/* Select an adapter */
printf("Enter the interface number (1 - %d):", i);
scanf("%d", &inum);
if (inum < 1 || inum > i)
{
printf("/nInterface number out of range./n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Jump to the selected adapter */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);
/*Open the device */
if ((adhandle = pcap_open(d->name, /* name of the device */
65536, /* portion of the packet to capture */
/* 65535 guarantees that the whole packet will be captured on all the link layers */
PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */
1000, /* read timeout */
NULL, /* authentication on the remote machine */
errbuf /* error buffer */
)) == NULL)
{
fprintf(stderr, "/nnable to open the adapter. %s is not supported by Winpcap/n", d->name);
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
printf("/nlistening on %s.../n", d->description);
/* At this point, we don‘t need any more the device list. Free it */
pcap_freealldevs(alldevs);
/* start the capture */
pcap_loop(adhandle, 0, packet_handler, NULL);
return 1;
}
/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data) {
struct tm *ltime;
char timestr[16];
/* convert the timestamp to readable format */
ltime = localtime(&header->ts.tv_sec);
strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
printf("%s, %.6d len:%d/n", timestr, header->ts.tv_usec, header->len);
}
函数1:
pcap_t *pcap_open(const char * source,
int snaplen,
int flags,
int read_timeout,
struct pcap_rmtauth * auth,
char * errbuf
)
为捕获/发送数据打开一个普通的源。pcap_open()能够替代所有的pcap_open_xxx()函数,它隐藏了不同的pcap_open_xxx()之间的差异,所以程序员不必使用不同的open函数。
source:的是包含要打开的源名称的以’/0’结尾的字符串。源名称得包含新的源规范语法(Source Specification Syntax),并且它不能为NULL。为了方便的使用源语法,请记住:(1)pcap_findalldevs_ex()返回的适配器(网卡)可以直接被pcap_open()使用;(2)万一用户想传递他自己的源字符串给pcap_open(),pcap_createsrcstr()可以创建正确的源标识。
snaplen:需要保留的数据包的长度。对每一个过滤器接收到的数据包,第一个‘snaplen’字节的内容将被保存到缓冲区,并且传递给用户程序。例如,snaplen等于100,那么仅仅每一个数据包的第一个100字节的内容被保存。简言之就是从每一个包的开头到snaplen的那段内容将被保存。
flags:保存一些由于抓包需要的标志。Winpcap定义了三种标志:
l PCAP_OPENFLAG_PROMISCUOUS:1,它定义了适配器(网卡)是否进入混杂模式(promiscuous mode)。
l PCAP_OPENFLAG_DATATX_UDP:2,它定义了数据传输(假如是远程抓包)是否用UDP协议来处理。
l PCAP_OPENFLAG_NOCAPTURE_RPCAP:4,它定义了远程探测器是否捕获它自己产生的数据包。
read_timeout:以毫秒为单位。read timeout被用来设置在遇到一个数据包的时候读操作不必立即返回,而是等待一段时间,让更多的数据包到来后从OS内核一次读多个数据包。并非所有的平台都支持read timeout;在不支持read timeout的平台上它将被忽略。
auth:一个指向’struct pcap_rmtauth’的指针,保存当一个用户登录到某个远程机器上时的必要信息。假如不是远程抓包,该指针被设置为NULL。
errbuf:一个指向用户申请的缓冲区的指针,存放当该函数出错时的错误信息。
返回值是一个’pcap_t’指针,它可以作为下一步调用(例如pcap_compile()等)的参数,并且指定了一个已经打开的Winpcap会话。在遇到问题的情况下,它返回NULL并且’errbuf’变量保存了错误信息。
函数2:
int pcap_loop( pcap_t* p,
int cnt,
pcap_hander callback,
u_char* user
)
收集一群数据包。pcap_loop()与pcap_dispatch()类似,但是它会一直保持读数据包的操作直到cnt包被处理或者发生了错误。当有活动的读超时(read timeout)时它并不返回。然而,对pcap_open_live()指定一个非0的读超时(read timeout),当发生超时的时候调用pcap_dispatch()来接收并处理到来的所有数据包更好。Cnt指明了返回之前要处理数据包的最大数目。如果cnt为负值,pcap_loop()将一直循环(直到发生错误才停止)。如果出错时返回-1;如果cnt用完时返回0;如果在任何包被处理前调用pcap_breakloop()来中止循环将返回-2。所以,如果程序中使用了pcap_breakloop(),必须准确的来判断返回值是-1还是-2,而不能简单的判断<0。
函数3:
hypedef void (* pcap_handler)(u_char* user,
const struct pcap_pkthdr* pkt_header,
const u_char* pkt_data)
接收数据包的回调函数原型。当用户程序使用pcap_dispatch()或者pcap_loop(),数据包以这种回调的方法传给应用程序。用户参数是用户自己定义的包含捕获会话状态的参数,它必须跟pcap_dispatch()和pcap_loop()的参数相一致。pkt_hader是与抓包驱动有关的头。pkt_data指向包里的数据,包括协议头。
结构体1:
struct pcap_pkthdr {
struct timeval ts;
bpf_u_int32 caplen;
bpf_u_int32 len;
}
ts:时间戳
cpalen:当前分组的长度
len:数据包的长度
试验代码3:
#include <pcap.h>
#include <remote-ext.h>
int main() {
pcap_if_t* alldevs;
pcap_if_t* d;
int inum;
int i = 0;
pcap_t* adhandle;
int res;
char errbuf[PCAP_ERRBUF_SIZE];
struct tm* ltime;
char timestr[16];
struct pcap_pkthdr* header;
u_char* pkt_data;
/* Retrieve the device list on the local machine */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s/n", errbuf);
exit(1);
}
/* Print the list */
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;
}
/* Select an adapter */
printf("Enter the interface number (1 - %d):", i);
scanf("%d", &inum);
if (inum < 1 || inum > i)
{
printf("/nInterface number out of range./n");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Jump to the selected adpater */
for (d = alldevs, i = 0; i < inum - 1; d = d->next, ++ i);
/* Open the device */
if ((adhandle = pcap_open(d->name, /* name of the device */
65536, /* portion of the packet to capture */
/* 65536 guarantees that the whole packet will be captured on all the link layers */
PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */
1000, /* read timeout */
NULL, /* authentication on the remote machine */
errbuf /* error buffer */
)) == NULL)
{
fprintf(stderr, "/nUnable to open the adapter. %s is not supported by Winpcap/n", d->name);
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
printf("/nlistening on %s .../n",d->description);
/* At this point, we don‘t need any more the device list. Free it */
pcap_freealldevs(alldevs);
/* Retrieve the packets */
while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= 0)
{
if (res == 0)
{
/* Timeout elapsed */
continue;
}
/* convert the timestamp to readable format */
ltime = localtime(&header->ts.tv_sec);
strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
printf("%s, %.6d len:%d/n", timestr, header->ts.tv_usec, header->len);
}
if (res == -1)
{
printf("Error reading the packets: %s/n", pcap_geterr(adhandle));
return -1;
}
return 1;
}
函数1:
pcap_next_ex(pcap_t* p,
struct pcap_pkthdr** pkt_header,
const u_char* pkt_data
)
从一个网络接口或离线捕获方式(例如读文件)读取一个数据包。该函数被用来重新获得下一个可用的数据包,没有使用libpcap提供的传统的回调方法。pcap_next_ex用指向头和下一个被捕获的数据包的指针为pkt_header和pkt_data参数赋值。
返回值有下列几种情况:
1,数据包被正确读取
0,pcap_open_live()设置的超时时间到。在这种情况下pkt_header和pkt_data不指向有效数据包
-1,发生错误
-2,离线捕获的时候读取到EOF
我们通常使用pcap_next_ex()而不是pcap_next(),因为pcap_next()有些缺点。首先,pcap_next()效率低,因为它隐藏了回调方法但是还是依赖于pcap_dispatch;第二,它不能检测EOF,所以当从一个文件获取数据包时它不是很有用。
函数2:
u_char* pcap_next(pcap_t* p,
struct pcap_pkthdr* h
)
返回下一个可用的数据包并且返回一个u_char指向该数据包数据部分的指针。如果发生错误或者活动的抓包没有读取到数据包(例如:数据包不能通过包过滤器而被丢弃,或者在支持读超时(read timeout)的平台上在任何数据包到来之前就超时终止,又或者是抓包设备的文件描述符在非阻塞(non-blocking)模式下并且没有数据包可以被读取),或者文件已被读完时返回NULL。不幸的是,没有办法检测是否发生错误。
Winpcap提供(libpcap也提供)的一个强大特性是过滤引擎(filtering engine)。它提供了一个非常有效的接收网络流量的方法,并且它通常与Winpcap提供的抓包机制集成在一起。用于过滤数据包的函数是pcap_complie()和pcap_setfilter()。
pcap_complie()使用一个包含高级布尔表达式的字符串并且产生一个能被过滤引擎集成到数据包驱动中的低级字节码。
pcap_setfilter()把一个过滤器与核心驱动抓包会话关联起来。一旦pcap_setfilter()被调用,相关的过滤器将被应用到所有的来自网络的数据包上,并且所有的一致的数据包将被复制给应用程序。
抓包和过滤
经过前面几天的知识准备,现在我们将把前面的知识综合后应用于一个简单的实际应用程序。下面试验的目的是如何解析和解释被捕获的数据包的协议头。应用程序运行的结果是打印出一组我们的网络上的UDP通信数据。我们之所以选择解析和显示UDP协议是因为它比起其他协议(例如:TCP)更易于理解,对于初学者更适合。
试验代码:
#include <pcap.h>
#include <remote-ext.h>
/* 4 bytes IP address */
typedef struct ip_address
{
u_char byte1;
u_char byte2;
u_char byte3;
u_char byte4;
}ip_address;
/* IPv4 header */
typedef struct ip_header
{
u_char ver_ihl; /* Version (4 bits) + Internet header length (4 bits)*/
u_char tos; /* Type of service */
u_short tlen; /* Total length */
u_short identification; /* Identification */
u_short flags_fo; /* Flags (3 bits) + Fragment offset (13 bits)*/
u_char ttl; /* Time to live */
u_char proto; /* Protocol */
u_short crc; /* Header checksum */
ip_address saddr;/* Source address */
ip_address daddr;/* Destination address */
u_int op_pad; /* Option + Padding */
}ip_header;
/* UDP header */
typedef struct udp_header
{
u_short sport; /* Source port */
u_short dport; /* Destination port */
u_short len; /* Datagram length */
u_short crc; /* Checksum */
}udp_header;
/* Prototype of the pachet handler */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data);
int main() {
pcap_if_t* alldevs;
pcap_if_t* d;
int inum;
int i = 0;
pcap_t* adhandle;
char errbuf[PCAP_ERRBUF_SIZE];
u_int netmask;
char packet_filter[] = "ip and udp";
struct bpf_program fcode;
/* Retrieve the device list */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1)
{
fprintf(stderr, "Error in pcap_findalldevs: %s/n", errbuf);
exit(1);
}
/* Print the list*/
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");
/* Free the device list */
pcap_freealldevs(alldevs);
return -1;
}
/* Jump to the selected adapter */
for (d = alldevs; d; d = d->next);
/* Open the adapter */
if ((adhandle = pcap_open(d->name, /*name of the device */
65536, /* portion of the packet to capture */
/* 65536 grants that the whole packet will be captured on all the MACs */
PCAP_OPENFLAG_PROMISCUOUS, /* promiscuous mode */
1000, /* read timeout */
NULL, /* remote authentication */
errbuf /* error buffer */
)) == NULL)
{
fprintf(stderr, "/nUnable to open the adapter. %s is not supported by Winpcap/n");
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
/* Check the link layer. We support only Ethernet for simplicity */
if (pcap_datalink(adhandle) != DLT_EN10MB)
{
fprintf(stderr, "/nThis program works only on Ethernet networks./n");
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
if (d->addresses != NULL)
{
/* Retrieve the mask of the first address of the interface */
netmask = ((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
}
else
{
/* If the interface is without addresses we suppose to be in a C class network */
netmask = 0xffffffff;
}
/* complie the filter */
if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) < 0)
{
fprintf(stderr, "/nUnable to compile the packet filter. Check the syntax./n");
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
/* set the filter */
if (pcap_setfilter(adhandle, &fcode) < 0)
{
fprintf(stderr, "/nError setting the filter./n");
/* Free the devices list */
pcap_freealldevs(alldevs);
return -1;
}
printf("/nlistening on %s .../n", d->description);
/* At this point,we don‘t need any more the device list. Free it */
pcap_freealldevs(alldevs);
/* Start the capture */
pcap_loop(adhandle, 0, packet_handler, NULL);
return 1;
}
/* Callback function invoked by libpcap for every incoming packet */
void packet_handler(u_char* param, const struct pcap_pkthdr* header, const u_char* pkt_data){
struct tm* ltime;
char timestr[16];
ip_header* ih;
udp_header* uh;
u_int ip_len;
u_short sport, dport;
/* convert the timestamp to readable format */
ltime = localtime(&header->ts.tv_sec);
strftime(timestr, sizeof(timestr), "%H:%M:%S", ltime);
/* print timestamp and length of the packet */
printf("%s.%.6d len: %d ", timestr, header->ts.tv_usec, header->len);
/* retrieve the position of the ip header */
ih = (ip_header*)(pkt_data + 14); /* length of ethernet header */
/* retrieve the position of the udp header */
ip_len = (ih->ver_ihl & 0xf) * 4;
uh = (udp_header*)((u_char*)ih + ip_len);
/* convert from network byte order to host byte order */
/*sport = ntohs(uh->sport);
dport = ntohs(uh->dport);*/
/* print ip addresses and udp ports */
printf("%d.%d.%d.%d -> %d.%d.%d.%d/n",
ih->saddr.byte1,
ih->saddr.byte2,
ih->saddr.byte3,
ih->saddr.byte4,
/*sport,*/
ih->daddr.byte1,
ih->daddr.byte2,
ih->daddr.byte3,
ih->daddr.byte4
/*dport*/);
}
函数1:
int pcap_datalink(pcap_t* p)
返回链路层上的一个适配器。返回链路层的类型,链路层的类型包括:
l DLT_NULL: BSD回路封装;链路层协议头是一个4字节的域,以主机字节顺序(host byte order),包含一个从socket.h来的PF_value。主机字节顺序(host byte order)是捕获数据包的机器的字节顺序,而PF_value是捕获数据包的机器的OS。如果一个读取一个文件,字节顺序和PF_value不一定是抓取文件的那些机器。
l DLT_EN10MB:以太网(10Mb, 100Mb, 1000Mb, 或者更高)。
l DLT_IEEE802: IEEE802.5令牌环网。
l DLT_ARCNET:ARCNET。
l DLT_SLIP:SLIP。
l DLT_PPP:PPP;如果第一个字节是0xff或0x03,它是类HDLC帧上的PPP再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://www.cnblogs.com/captainbed
以上是关于Winpcap学习的主要内容,如果未能解决你的问题,请参考以下文章