`
isiqi
  • 浏览: 16036022 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

winpcap入门学习

阅读更多

1.入门使用篇
本篇讲述如何抓包
最简单的libpcap抓包程序只要有以下几句就可以了

char ebuf[PCAP_ERRBUF_SIZE];
pcap_t *pd = pcap_open_live("eth0", 68, 0, 1000, ebuf);
建立libpcap捕捉句柄,若出错,ebuf返回错误字串.ebuf可以为NULL(以后同)

struct bpf_program fcode;
pcap_compile(pd, &fcode, NULL, 1, 0);
添写过滤规则串fcode,可以为空(即第三个参数,格式在后面讲到)

pcap_setfilter(pd, &fcode);
给 pd 设置上过滤规则

pcap_loop(pd, 10, eth_printer, NULL);
主循环,开始抓包,共抓10个(由第二个参数指定),抓到包后就进入函数 eth_printer

pcap_close(pd);
结束

这个就是最简单的程序了,其中还有个不明,
在pcap_loop参数 eth_printer的类型是pcap_handler,pcap_handler定义如下:
typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *,
const u_char *);
当然要包含
#include "pcap.h"
编译要加上 -lpcap
至于怎么得到libpcap,还有安装,我就不费话了
本文版权所有:doggy(chaujy@x263.net) 欢迎转载

2.使用进阶篇

刚才我们对程序的要求是能编译通过,能运行成功
现在我们让这个程序实用点吧

2.1 其它几个函数介绍
这几个函数的特点是简单但无关紧要

现在我们隆重退出
char * pcap_lookupdev ( char * errbuf );
这个函数就是查找本机上的网络接口设备,我机器上就返回"eth0",
在pcap_open_live之前用,没什么意思吧,反正我是不爱用它

int pcap_lookupnet(char *, bpf_u_int32 *, bpf_u_int32 *, char *);
第一个参数就是pcap_lookupdev返回的接口名,二三参数都是32位无符号数,
分别是IP网段和掩码,最后那个参数还是ebuf

int pcap_datalink(pcap_t *);
它返回你的网络类型,如 DLT_EN10MB 就是10M以太网
让人ft氖钦庑┏A慷ㄒ宀辉趐cap.h中,在哪?自己找找吧;-)

int pcap_snapshot(pcap_t *);
返回最长抓多少字节,就是我们在pcap_open_live中第二个参数设置的

int pcap_stats(pcap_t *, struct pcap_stat *);
计数,共抓了多少过滤掉了多少,看看struct pcap_stat的定义就明白了
struct pcap_stat {
u_int ps_recv; /* number of packets received */
u_int ps_drop; /* number of packets dropped */
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
};

int pcap_major_version(pcap_t *);
int pcap_minor_version(pcap_t *);
版本号,你有用么?
我的eth_printer如下
void eth_printer(u_char * user, const struct pcap_pkthdr * h, const u_char * p)
{
printf("I get one packet! ");
}
简单吧 :*)

2.使用进阶篇

刚才我们对程序的要求是能编译通过,能运行成功现在我们让这个程序实用点吧

2.1 其它几个函数介绍
这几个函数的特点是简单但无关紧要

现在我们隆重退出
char * pcap_lookupdev ( char * errbuf );
这个函数就是查找本机上的网络接口设备,我机器上就返回"eth0",在pcap_open_live之前用,没什么意思吧,反正我是不爱用它

int pcap_lookupnet(char *, bpf_u_int32 *, bpf_u_int32 *, char *);
第一个参数就是pcap_lookupdev返回的接口名,二三参数都是32位无符号数,
分别是IP网段和掩码,最后那个参数还是ebuf

int pcap_datalink(pcap_t *);
它返回你的网络类型,如 DLT_EN10MB 就是10M以太网
让人ft的是这些常量定义不在pcap.h中,在哪?自己找找吧;-)

int pcap_snapshot(pcap_t *);
返回最长抓多少字节,就是我们在pcap_open_live中第二个参数设置的

int pcap_stats(pcap_t *, struct pcap_stat *);
计数,共抓了多少过滤掉了多少,看看struct pcap_stat的定义就明白了
struct pcap_stat {
u_int ps_recv; /* number of packets received */
u_int ps_drop; /* number of packets dropped */
u_int ps_ifdrop; /* drops by interface XXX not yet supported */
};

int pcap_major_version(pcap_t *);
int pcap_minor_version(pcap_t *);
版本号

2.2 现在的程序(C++)

文件名p.cxx

#ifdef __cplusplus
extern "C" {
#endif
#include
#ifdef __cplusplus
}
#endif

void printer(u_char * user, const struct pcap_pkthdr * h, const u_char * p)
{
printf("I get one packet! ");
/* 哈哈,我都想喝一杯庆祝一下了! */
}

#define DEFAULT_SNAPLEN 68
/* 别问我为什么是68,我从tcpdump看来的 */

int main()
{
char ebuf[PCAP_ERRBUF_SIZE];
char *device = pcap_lookupdev(ebuf);

bpf_u_int32 localnet, netmask;
pcap_lookupnet(device, &localnet, &netmask, ebuf);
printf("%u.%u.%u.%u", localnet&0xff, localnet>>8&0xff,
localnet>>16&0xff, localnet>>24&0xff);
printf(":%d.%d.%d.%d ", netmask&0xff, netmask>>8&0xff,
netmask>>16&0xff, netmask>>24&0xff);

struct pcap_t *pd = pcap_open_live(device, DEFAULT_SNAPLEN, 0, 1000, ebuf);

if(pcap_datalink(pd) == DLT_EN10MB)
printf("10Mb以太网 ");

struct bpf_program fcode;
pcap_compile(pd, &fcode, NULL, 1, 0);

pcap_setfilter(pd, &fcode);

pcap_loop(pd, 10, printer, NULL);

struct pcap_stat stat;
pcap_stats(pd, &stat);
printf("recv %d, drop %d. ", stat.ps_recv, stat.ps_drop);

pcap_close(pd);
}

#gcc p.cxx -lpcap
#./a.out
166.111.168.0:255.255.252.0
10Mb以太网
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
I get one packet!
recv 10, drop 0.
#

重要提示: libpcap 程序需要root权限
2.3 出错处理

象其它库一样,libpcap 也有自己的错误处理机制
基本上每个函数都有返回值,出错时返回值<0,另外有如下函数
void pcap_perror(pcap_t *, char *);
char *pcap_strerror(int);
char *pcap_geterr(pcap_t *);
前两个和 perror() strerror() 用法相同,最后一个也很简单
在 pcap_t 中有一个成员存了错误字串
struct pcap {
...
char errbuf[PCAP_ERRBUF_SIZE];
};
所以......

于是我们在刚才的程序中加上错误处理
先加一个函数
#include
int err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(-1);
}

之后处理每个函数的异常,在成功建立捕捉句柄pcap_t *pd前,使用ebuf参数
char *device = pcap_lookupdev(ebuf);
if(device == NULL)
err_quit("%s", ebuf);
有了句柄pd后
if(pcap_compile(pd, &fcode, NULL, 1, 0) < 0)
err_quit("%s", pcap_geterr(pd));
注意不是每个函数都是出错返回<0
pcap_datalink(pd)和pcap_snapshot(pd)等可不要这么处理
2.4 参数初步研究

前面使用各函数时,并没有具体说明每个函数的意义,现在来探讨一下

pcap_t * pcap_open_live(char *device, int snaplen, int promisc,
int to_ms, char *ebuf)
device指定设备,snaplen指定最长抓多少字节,ebuf出错信息,前面都说过
promisc指出是否设置为混杂模式(不懂?我也不懂,整个网都听还有什么安全性可言)
to_ms设置超时时间,单位milliseconds

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)
cnt为要抓的包数,pcap_loop在正常时抓cnt个包后返回,异常时返回值<0
user是要传入callback()的数据,例如我们把上面的程序修改几行
file://pcap_loop(pd, 10, printer, NULL);
pcap_loop(pd, 10, printer, (u_char*)pd);
再在printer()内加
pcap_stat stat;
pcap_stats((pcap_t*)user, &stat);
printf("recv %d, drop %d. ", stat.ps_recv, stat.ps_drop);
再编译运行后输出为
166.111.168.0:255.255.252.0
10Mb以太网
recv 1, drop 0.
recv 2, drop 0.
recv 3, drop 0.
recv 4, drop 0.
recv 5, drop 0.
recv 6, drop 0.
recv 7, drop 0.
recv 8, drop 0.
recv 9, drop 0.
recv 10, drop 0.
recv 10, drop 0.

int pcap_compile(pcap_t *p, struct bpf_program *program,
char *buf, int optimize, bpf_u_int32 mask)
该函数用于解析过滤规则串buf,填写bpf_program结构.
optimize为1表示对过滤规则进行优化处理
netmask指定子网掩码
buf的格式比较复杂

int pcap_setfilter(pcap_t *handle, struct bpf_program *filter)
把pcap_compile()构造的filter设置到handle上

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics