文章目录
前言
详细内容见:winpcap中文文档
这里面有时用适配器(adapter),有时用接口(interface),下文使用适配器。(其实适配器也可以是一个接口)
参考文章:WINPCAP抓包并用回调函数处理包
宏定义和结构体(工欲善其事,必先利其器)
//宏定义
#define PCAP_ERRBUF_SIZE 256
宏定义还有其他的,这个是比较常用的,用于设置一个字符缓冲区(字符串)
//一个单链表
typedef struct pcap_if pcap_if_t;
struct pcap_if {
struct pcap_if *next;
char *name;
char *description;
struct pcap_addr *addresses;
u_int flags;
};
用来保存适配器的名称及描述,遍历使用单链表的遍历方法,其它数据可以暂时忽略。
//一个已打开的捕捉实例的描述符。不透明。
typedef struct pcap pcap_t;
用来保存一个打开的适配器。使用前先定义,再用来接受相应函数的返回值即可,细节不用考虑。
//用于远程机器认证,a remote machine
struct pcap_rmtauth{
int type;
char *username;
char *password;
};
不需要的话可以设为NULL
struct pcap_pkthdr {
struct timeval ts;
bpf_u_int32 caplen;
bpf_u_int32 len;
};
winpcap自己给数据包设置的一个包头,包括时间戳、捕获长度、实际长度,相当于一个附加信息,掌握这个结构可以用来输出这些信息。
struct pcap_addr {
struct pcap_addr *next;
struct sockaddr *addr;
struct sockaddr *netmask;
struct sockaddr *broadaddr;
struct sockaddr *dstaddr;
};
适配器的地址
获取适配器列表(先有设备才可以进行抓包和发送)
//获取设备列表,即适配器列表
//保存在单链表里,错误信息放在errbuf里
int pcap_findalldevs ( pcap_if_t ** alldevsp,
char * errbuf
)
int pcap_findalldevs_ex ( char * source,
struct pcap_rmtauth * auth,
pcap_if_t ** alldevs,
char * errbuf
)
//获取之后,要释放其占用的内存资源
void pcap_freealldevs ( pcap_if_t * alldevsp )
参数 | 说明 | 补充 |
---|---|---|
alldevsp | 存放设备的地址 | 一个单链表 |
errbuf | 错误信息 | 字符串 |
auth | 用于远程认证 | 不需要可以写为NULL |
选择一个适配器打开(开始使用设备)
//打开适配器并捕获数据包
pcap_t* pcap_open ( const char * source,
int snaplen,
int flags,
int read_timeout,
struct pcap_rmtauth * auth,
char * errbuf
)
参数 | 意义 | 补充 |
---|---|---|
source | 设备名 | 获取适配器列表其中的一个,name部分 |
snaplen | 捕获数据包中的哪些部分,也就是长度 | 将值定为65535,它比我们能遇到的最大的MTU还要大。因此,我们确信我们总能收到完整的数据包。 |
flags | 指示适配器是否要被设置成混杂模式 | 一般情况下,适配器只接收发给它自己的数据包, 而那些在其他机器之间通讯的数据包,将会被丢弃。 相反,如果适配器是混杂模式,那么不管这个数据包是不是发给我的,我都会去捕获。也就是说,我会去捕获所有的数据包。 |
read_timeout | 指定读取数据的超时时间,以毫秒计(1s=1000ms) | |
返回值 | 一个打开的捕捉实例,网络适配器的描述符 | 用于后续捕获数据包 |
获取数据包及处理(处理适配器收到的数据包)
方法一
//捕获工作
int pcap_dispatch ( pcap_t * p,
int cnt,
pcap_handler callback,
u_char * user
)
int pcap_loop ( pcap_t * p,
int cnt,
pcap_handler callback,
u_char * user
)
//callback是一个回调函数,用来处理数据包,要自己实现
//user只是用来描述这次抓包,可以置为NULL
这两个函数非常的相似,区别就是 pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获,所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_loop()对于我们这个简单的范例来说,可以满足需求,不过, pcap_dispatch() 函数一般用于比较复杂的程序中。
这两个函数都有一个 回调 参数, packet_handler指向一个可以接收数据包的函数。
!!!问题来了,数据包去哪里了?
数据包被上面两个函数作为参数传递给了回调函数。
!!!回调函数要自己实现,参数列表已经定义好了,不允许改变
//回调函数
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data);
参数 | 说明 | 补充 |
---|---|---|
param | user | pcap_loop()里面的user信息 |
header | winpcap设置的一些附加信息 | |
pkt_data | 指向抓到的数据包 | 字符串 |
请注意,使用 pcap_loop() 函数可能会遇到障碍,主要因为它直接由数据包捕获驱动所调用。因此,用户程序是不能直接控制它的。
方法二
//不用回调方法捕获数据包,一次获取一个
int pcap_next_ex ( pcap_t * p,
struct pcap_pkthdr ** pkt_header,
const u_char ** pkt_data
)
参数 | 说明 | 补充 |
---|---|---|
p | 网络适配器的描述符 | |
pkt_header | 可以初始化和返回给用户的指针 | 指向 pcap_pkthdr 结构体 |
pkt_data | 可以初始化和返回给用户的指针 | 指向数据报数据的缓冲 |
返回值 | -1出错或EOF,0超时,>0成功 |
使用要点:这个方法需要用户自己先定义两个指针用来接收pcap_pkthdr和数据包。
过滤数据包(获取想要的数据包)
//过滤数据包
int pcap_compile ( pcap_t * p,
struct bpf_program * fp,
char * str,
int optimize,
bpf_u_int32 netmask
)
int pcap_setfilter ( pcap_t * p,
struct bpf_program * fp
)
发送数据包(当然也是用适配器发送了,需要打开)
//发送单个数据包
nt pcap_sendpacket ( pcap_t * p,
u_char * buf,
int size
)
buf是要发送的数据缓冲区(即字符串),size是数据的长度
!!!为什么要设置size?
在数据包中,可以有很多字节为零,而在C语言中字符数组的结尾"\0"也是一个全零的字节,不设置size就不能区分数据的长度。
同样的,C语言中的字符串函数也不适用于数据包的构建,构建数据包的时候也需要设置一个size便于操作。