0.
这一次具体讲抓包的两种方法。
(建议)清除ARP表,最好自己写个批处理命令。快一点。
1.0 抓包步骤
步骤很简单:先打开适配器列表 --> 选择适配器 --> 通过遍历链表的方式到达你选择的适配器位置 --> 打开设备 --> 开始抓包。
每一个步骤都是一个函数。了解步骤,后面就好办。
首先,了解一个数据类型pcap/pcap_t。它代表一个打开的设备,理解成它就是适配器就行(实际上是这个适配器的描述符)。这个结构体对用户来说是不透明的,它通过wpcap.dll提供的函数,维护了它的内容。
然后,跳转至你的设备在WinPcap编程(一)中存在,就不多说。
之后,打开设备的函数:
pcap_t* pcap_open ( const char * source, int snaplen, int flags, int read_timeout, struct pcap_rmtauth * auth, char * errbuf )
备注:
第一个参数:source是我们所要打开的设备。当我们获取所有的设备之后,这个source就是d->name。不能为NULL。
第二个参数:snaplen是我们抓取的数据包的大小,100就是抓取整个数据包的前100B,65536最大,把整个包都包括了。
第三个参数:flags设置为混杂模式(PCAP_OPENFLAG_PROMISCUOUS ),即不管数据包是否给我,我都去捕获。这样可以捕捉局域网内所有的包。
第四个参数: read_timeout设置超时时间,以毫秒计(1s=1000ms)。在适配器上进行读取数据操作的时候,不管网络上有没有包,都会在 read_timeout这个时间内响应。
设置为0意味着没有超时。没有数据包到达,读操作将永远不会返回。
设置成-1,无论有没有数据包到达,读操作都会立即返回。
第五个参数:auth远程机登录信息,若本地则为NULL;
第六个参数:errbuf 出错信息。
1.1抓包的两种方式:
1.1.1 回调函数的方法进行抓包
每次抓到包就用回调函数进行处理。处理完接着抓包(如果设置了num的话)。
抓包函数:
int pcap_loop ( pcap_t * p, int cnt, pcap_handler callback, u_char * user )
备注0:
第一个参数:P即是打开的设备。
第二个参数:cnt表示捕捉个数。
第三个参数:回调函数指针。对捕捉的数据包进行操作 。
第四个参数:用户信息,一般为NULL。
函数返回值:
返回值为0,捕捉了cnt次,正常返回。
返回值为-1,发生错误。
返回值为-2,使用pcap_breakloop()结束循环。
回调函数抓包还有一种方法, pcap_dispatch() 。
区别是: pcap_ dispatch() 当超时时间到了(timeout expires)就返回 (尽管不能保证) ,而 pcap_loop() 不会因此而返回,只有当 cnt 数据包被捕获。
所以,pcap_loop()会在一小段时间内,阻塞网络的利用。pcap_dispatch() 函数一般用于比较复杂的程序中。
1.1.2 不利用回调函数
回调函数比较好,但是有时候需要不用回调函数的方法,特别在多线程里面。
我用C# + WPF写的,用回调函数的话一直出现错误。仔细考虑,应该是托管与非托管的问题。看了SharpPcap的源码,貌似也没有好的解决方案。
【有好的方案的看见了希望能够提供一下,感谢。】
不利用回调函数抓包的话,方法比较简单,每次只能抓一个包,加个while()循环就OK了。
int pcap_next_ex (
pcap_t * p,
struct pcap_pkthdr ** pkt_header,
const u_char ** pkt_data
)
备注:
第一个参数:打开的设备。
第二个参数:WinPcap添加的一些信息。(时间戳,捕捉到包的长度,实际包的长度)。
第三个参数:实际数据包。
函数返回值:
返回值为1,正常返回;
返回值为0,超时后返回一个无效的包;
返回值为-1,发生错误;
返回值为-2,读到EOF。
源码:
#define WIN32
#include "pcap.h"
#include "winsock.h"
#include "time.h" /* 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 = ;
pcap_t *adhandle;
char errbuf[PCAP_ERRBUF_SIZE]; /* 获取本机设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit();
} /* 打印列表 */
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 == )
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -;
} printf("Enter the interface number (1-%d):", i);
scanf_s("%d", &inum); if (inum < || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
} /* 跳转到选中的适配器 */
for (d = alldevs, i = ; i< inum - ; d = d->next, i++); /* 打开设备 */
if ((adhandle = pcap_open(d->name, // 设备名
, // 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
, // 读取超时时间
NULL, // 远程机器验证
errbuf // 错误缓冲池
)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
} printf("\nlistening on %s...\n", d->description); /* 释放设备列表 */
pcap_freealldevs(alldevs); /* 开始捕获 */
pcap_loop(adhandle, , packet_handler, NULL); system("pause");
return ;
} /* 每次捕获到数据包时,libpcap都会自动调用这个回调函数 */
void packet_handler(u_char *param, const struct pcap_pkthdr *header, const u_char *pkt_data)
{
struct tm ltime = {};
char timestr[];
time_t local_tv_sec; /* 将时间戳转换成可识别的格式 */
local_tv_sec = header->ts.tv_sec;
localtime_s(<ime, &local_tv_sec);
strftime(timestr, sizeof timestr, "%H:%M:%S", <ime); printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
}
回调函数
#define WIN32
#include "pcap.h" int main()
{
pcap_if_t *alldevs;
pcap_if_t *d;
int inum;
int i = ;
pcap_t *adhandle;
int res;
char errbuf[PCAP_ERRBUF_SIZE];
struct tm ltime;
char timestr[];
struct pcap_pkthdr *header;
const u_char *pkt_data;
time_t local_tv_sec; int j = ;//限制只抓10个包 /* 获取本机设备列表 */
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -)
{
fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);
exit();
} /* 打印列表 */
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 == )
{
printf("\nNo interfaces found! Make sure WinPcap is installed.\n");
return -;
} printf("Enter the interface number (1-%d):", i);
scanf_s("%d", &inum); if (inum < || inum > i)
{
printf("\nInterface number out of range.\n");
/* 释放设备列表 */
pcap_freealldevs(alldevs);
return -;
} /* 跳转到已选中的适配器 */
for (d = alldevs, i = ; i< inum - ; d = d->next, i++); /* 打开设备 */
if ((adhandle = pcap_open(d->name, // 设备名
, // 要捕捉的数据包的部分
// 65535保证能捕获到不同数据链路层上的每个数据包的全部内容
PCAP_OPENFLAG_PROMISCUOUS, // 混杂模式
, // 读取超时时间
NULL, // 远程机器验证
errbuf // 错误缓冲池
)) == NULL)
{
fprintf(stderr, "\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);
/* 释放设列表 */
pcap_freealldevs(alldevs);
return -;
} printf("\nlistening on %s...\n", d->description); /* 释放设备列表 */
pcap_freealldevs(alldevs); /* 获取数据包 */
while ((res = pcap_next_ex(adhandle, &header, &pkt_data)) >= &&j>){ if (res == )
/* 超时时间到 */
continue; /* 将时间戳转换成可识别的格式 */
local_tv_sec = header->ts.tv_sec;
localtime_s(<ime,&local_tv_sec);
strftime(timestr, sizeof timestr, "%H:%M:%S", <ime);
j--; printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
} if (res == -){
printf("Error reading the packets: %s\n", pcap_geterr(adhandle));
return -;
}
system("pause");
return ;
}
非回调函数