利用libpcap抓取数据包
- 转载自:http://blog.csdn.net/tennysonsky/article/details/44811899
- 概述
- libpcap是一个网络数据包捕获函数库,tcpdump就是以libpcap为基础的。
- 主要作用:
- 捕获各种数据包,例如:网络流量统计
- 过滤网络数据包,例如:过滤掉本地上的一些数据,类似防火墙
- 分析网络数据包,例如:分析网络协议,数据的采集
- 存储网络数据包,例如:保存捕获的数据以为将来进行分析
- libpcap的抓包框架
- pcap_lookupdev():函数用来查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。
- pcap_lookupnet():函数获得指定网络设备的网络号和掩码。
- pcap_open_live():函数用于打开设备,并且返回用于捕获网络数据包的数据包捕获描述字。对于此网络设备的操作都要基于此网络设备描述字。
- pcap_compile():函数用于将用户制定的过滤策略编译到过滤程序中
- pcap_setfilter():函数用于设置过滤器
- pcap_loop():与pcap_next()和pcap_next_ex()两个函数一样用来捕获数据包
- pcap_close():函数用于关闭网络设备,释放资源
- 利用libpcap函数库开发应用程序的步骤:
- 打开网络设备
- 设置过滤规则
- 捕获数据
- 关闭网络设备
- 详细步骤:
- 首先要使用libpcap,需要包含pcap.h头文件。
- 获取网络设备接口:
- char *pcap_lookupdev(char * errbuf);
- 功能:自动获取可用的网络设备名指针
- 参数:errbuf,存放出错信息字符串,有宏定义缓冲区大小,PCAP_ERRBUF_SIZE
- 返回值:成功返回设备名指针(第一个合适的网络接口的字符串指针),失败则返回NULL,同时,errbuf存放出错信息字符串
//自动获取网络接口形式
char errBuf[PCAP_ERRBUF_SIZE], *devStr;
devStr = pcap_lookupdev(errBuf); //手动获取网络接口形式只需要被devStr赋值即可
char errBuf[PCAP_ERRBUF_SIZE], *devStr = “eth0”;
- 获取网络号(ip地址)和掩码
- int pcap_lookupnet(char* device,bpf_u_int32 *netp,bpf_u_int32 *maskp,char *errbuf);
- 功能:获取指定网卡的ip地址,子网掩码
- 参数:device:网络设备名,为第一步获取的网络接口字符串,也可以人为指定,如“eth0”;
- netp:存放ip地址的指针,buf_u_int32为32位无符号整型
- maskp:存放子网掩码的指针
- errbuf:存放出错信息
- 返回值:成功返回0,失败返回1
char error_content[PCAP_ERRBUF_SIZE] = {}; // 出错信息
char *dev = pcap_lookupdev(error_content);
if(NULL == dev)
{
printf(error_content);
exit(-);
} bpf_u_int32 netp = , maskp = ;
pcap_t * pcap_handle = NULL;
int ret = ; //获得网络号和掩码
ret = pcap_lookupnet(dev, &netp, &maskp, error_content);
if(ret == -)
{
printf(error_content);
exit(-);
}
- 打开网络接口:
- pcap_t *pcap_open_live(const char * device,int snaplen,int promisc,int to_ms,char *errbuf);
- 功能:打开一个用于捕获数据的网络端口
- 参数:device:网络接口的名字,为第一步获取的网络接口字符串,也可以人为指定,如:”eth0“
- snaplen:捕获数据包的长度,不能大于65535个字节
- promise:”1“代表混杂模式,其他值代表非混杂模式
- to_ms:指定需要等地啊的毫秒数,超过这个时间后,获得数据包的函数会立即返回,0表示一直等待直到有数据包到来
- errbuf:存储错误信息
- 返回值:返回pcap_t类型指针,后面的所有操作都要使用这个指针。
char error_content[PCAP_ERRBUF_SIZE] = {}; // 出错信息
char *dev = pcap_lookupdev(error_content); // 获取网络接口
if(NULL == dev)
{
printf(error_content);
exit(-);
} // 打开网络接口
pcap_t * pcap_handle = pcap_open_live(dev, , , , error_content);
if(NULL == pcap_handle)
{
printf(error_content);
exit(-);
}
- 获取数据包:
- 方法一:const u_char *pcap_next(pcap_t *p,struct pcap_pkthdr *h);
- 功能:捕获一个网络数据包,收到一个数据包立即返回
- 参数:p:pcap_open_live()返回的pcap_t类型的指针
- h:数据包头
- pcap_pkthdr类型的定义如下:
struct pcap_pkthdr
{
struct timeval ts; // 抓到包的时间
bpf_u_int32 caplen; // 表示抓到的数据长度
bpf_u_int32 len; // 表示数据包的实际长度
}- len和caplen的区别:因为在某些情况下不能保证捕获的包是完整的,例如一个包长1480,但是你捕获到1000的时候,可能因为某些原因就终止捕获了,所以caplen是记录实际捕获的包长,也就是1000,而len就是1480
- 返回值:成功则返回捕获数据包的地址,失败返回NULL
const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
pcap_t *pcap_handle = NULL;
struct pcap_pkthdr protocol_header; pcap_handle = pcap_open_live("eth0", , , ,NULL); p_packet_content = pcap_next(pcap_handle, &protocol_header);
//p_packet_content 所捕获数据包的地址 printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec)); // 时间
printf("Packet Lenght is :%d\n",protocol_header.len); // 数据包的实际长度- 方法二:int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
- 功能:循环捕获网络数据包,直到遇到错误或者满足退出条件,每次捕获一个数据包就会调用callback指定的回调函数,所以,可以在回调函数中进行数据包的处理操作。
- 参数:p:pcap_open_live()返回的pcap_t类型的指针
- cnt:指定捕获数据包的个数,一旦抓到cnt个数据包,pcap_loop立即返回,如果是-1,就会一直捕获直到出错
- callback:回调函数,名字任意,根据需要自行取名
- user:向回调函数中传递的参数
- callback回调函数的定义:
- void callback(u_char *userarg,const struct pcap_pkthdr *pkthdr,const u_char *packet)
- userarg:pcap_loop()的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
- pkthdr:是收到数据包的pcap_pkthdr类型的指针,和pcap_next()第二个参数是一样的
- packet:收到的数据包数据
- 返回值:成功返回0,失败返回负数
- 方法三:int pcap_dispatch(pcap_t *p,int cnt,pcap_handler callback,u_char *user);
- 这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第四个参数)
- 释放网络接口:
- void pcap_close(pcap_t *p);
- 功能:关闭pcap_open_live()打开的网络接口,并释放相关资源
- 参数:p:需要关闭的网络接口,pcap_open_live()的返回值
// 打开网络接口
pcap_t * pcap_handle = pcap_open_live("eth0", , , , error_content);
if(NULL == pcap_handle)
{
printf(error_content);
exit(-);
} //// ……
//// …… pcap_close(pcap_handle); //释放网络接口
- void pcap_close(pcap_t *p);
//接收一个数据包
#include <stdio.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdlib.h>
struct ether_header
{
unsigned char ether_dhost[]; //目的mac
unsigned char ether_shost[]; //源mac
unsigned short ether_type; //以太网类型
};
#define BUFSIZE 1514 int main(int argc,char *argv[])
{
pcap_t * pcap_handle = NULL;
char error_content[] = ""; // 出错信息
const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
unsigned char *p_mac_string = NULL; // 保存mac的地址,临时变量
unsigned short ethernet_type = ; // 以太网类型
char *p_net_interface_name = NULL; // 接口名字
struct pcap_pkthdr protocol_header;
struct ether_header *ethernet_protocol; //获得接口名
p_net_interface_name = pcap_lookupdev(error_content);
if(NULL == p_net_interface_name)
{
perror("pcap_lookupdev");
exit(-);
} //打开网络接口
pcap_handle = pcap_open_live(p_net_interface_name,BUFSIZE,,,error_content);
p_packet_content = pcap_next(pcap_handle,&protocol_header); printf("------------------------------------------------------------------------\n");
printf("capture a Packet from p_net_interface_name :%s\n",p_net_interface_name);
printf("Capture Time is :%s",ctime((const time_t *)&protocol_header.ts.tv_sec));
printf("Packet Lenght is :%d\n",protocol_header.len); /*
*分析以太网中的 源mac、目的mac
*/
ethernet_protocol = (struct ether_header *)p_packet_content;
p_mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac
printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+));
p_mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+),*(p_mac_string+)); /*
*获得以太网的数据包的地址,然后分析出上层网络协议的类型
*/
ethernet_type = ntohs(ethernet_protocol->ether_type);
printf("Ethernet type is :%04x\t",ethernet_type);
switch(ethernet_type)
{
case 0x0800:printf("The network layer is IP protocol\n");break;//ip
case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
default:printf("The network layer unknow!\n");break;
} pcap_close(pcap_handle);
return ;
}//接收多个数据包 #include <stdio.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdlib.h> #define BUFSIZE 1514 struct ether_header
{
unsigned char ether_dhost[]; //目的mac
unsigned char ether_shost[]; //源mac
unsigned short ether_type; //以太网类型
}; /*******************************回调函数************************************/
void ethernet_protocol_callback(unsigned char *argument,const struct pcap_pkthdr *packet_heaher,const unsigned char *packet_content)
{
unsigned char *mac_string; //
struct ether_header *ethernet_protocol;
unsigned short ethernet_type; //以太网类型
printf("----------------------------------------------------\n");
printf("%s\n", ctime((time_t *)&(packet_heaher->ts.tv_sec))); //转换时间
ethernet_protocol = (struct ether_header *)packet_content; mac_string = (unsigned char *)ethernet_protocol->ether_shost;//获取源mac地址
printf("Mac Source Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+));
mac_string = (unsigned char *)ethernet_protocol->ether_dhost;//获取目的mac
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+),*(mac_string+)); ethernet_type = ntohs(ethernet_protocol->ether_type);//获得以太网的类型
printf("Ethernet type is :%04x\n",ethernet_type);
switch(ethernet_type)
{
case 0x0800:printf("The network layer is IP protocol\n");break;//ip
case 0x0806:printf("The network layer is ARP protocol\n");break;//arp
case 0x0835:printf("The network layer is RARP protocol\n");break;//rarp
default:break;
}
usleep(*);
} int main(int argc, char *argv[])
{
char error_content[]; //出错信息
pcap_t * pcap_handle;
unsigned char *mac_string;
unsigned short ethernet_type; //以太网类型
char *net_interface = NULL; //接口名字
struct pcap_pkthdr protocol_header;
struct ether_header *ethernet_protocol; //获取网络接口
net_interface = pcap_lookupdev(error_content);
if(NULL == net_interface)
{
perror("pcap_lookupdev");
exit(-);
} pcap_handle = pcap_open_live(net_interface,BUFSIZE,,,error_content);//打开网络接口 if(pcap_loop(pcap_handle,-,ethernet_protocol_callback,NULL) < )
{
perror("pcap_loop");
} pcap_close(pcap_handle);
return ;
}- 过滤数据包:
- 设置过滤条件:举一些例子:
- src host 192.168.1.177:只接收源ip地址是192.168.1.177的数据包
- dst port 80:只接收tcp、udp的目的端口是80的数据包
- not tcp:只接收不使用tcp协议的数据包
- tcp[13] == 0x02 and (dst port 22 or dst port 23) :只接收 SYN 标志位置位且目标端口是 22 或 23 的数据包( tcp 首部开始的第 13 个字节)
- icmp[icmptype] == icmp-echoreply or icmp[icmptype] == icmp-echo:只接收 icmp 的 ping 请求和 ping 响应的数据包
- ehter dst 00:e0:09:c1:0e:82:只接收以太网 mac 地址是 00:e0:09:c1:0e:82 的数据包
- ip[8] == 5:只接收 ip 的 ttl=5 的数据包(ip首部开始的第8个字节)
- 编译BPF过滤规则:
- int pcap_compile(pcap_t *p,struct bpf_program *fp,char *buf,int optimize,bpf_u_int32 mask);
- 参数:
- p:pcap_open_live()返回的pcap_t类型的指针
- fp:存放编译后的bpf,应用过来规则时需要使用这个指针
- buf:过滤规则
- optimize:是否需要优化过滤表达式
- mask:指定本地网络的网络掩码,不需要时可写0
- 返回值:成功返回0,失败返回-1
- 应用BPF过滤规则:
- int pcap_setfilter(pcap_t *p,struct bpf_program *fp);
- 功能:应用BPF过滤规则
- 参数:p:pcap_open_live()返回的pcap_t类型的指针
- fp:pcap_compile()的第二个参数
- 返回值:成功返回0,失败返回-1
#include <pcap.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h> void getPacket(u_char * arg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
{
int * id = (int *)arg; printf("id: %d\n", ++(*id));
printf("Packet length: %d\n", pkthdr->len);
printf("Number of bytes: %d\n", pkthdr->caplen);
printf("Recieved time: %s", ctime((const time_t *)&pkthdr->ts.tv_sec)); int i;
for(i=; i<pkthdr->len; ++i)
{
printf(" %02x", packet[i]);
if( (i + ) % == )
{
printf("\n");
}
} printf("\n\n");
} int main()
{
char errBuf[PCAP_ERRBUF_SIZE], * devStr; /* get a device */
devStr = pcap_lookupdev(errBuf); if(devStr)
{
printf("success: device: %s\n", devStr);
}
else
{
printf("error: %s\n", errBuf);
exit();
} /* open a device, wait until a packet arrives */
pcap_t * device = pcap_open_live(devStr, , , , errBuf); if(!device)
{
printf("error: pcap_open_live(): %s\n", errBuf);
exit();
} /* construct a filter */
struct bpf_program filter;
pcap_compile(device, &filter, "dst port 80", , );
pcap_setfilter(device, &filter); /* wait loop forever */
int id = ;
pcap_loop(device, -, getPacket, (u_char*)&id); pcap_close(device); return ;
}
利用libpcap抓取数据包的更多相关文章
- 利用Fiddler抓取websocket包
一.利用fiddler抓取websockt包 打开Fiddler,点开菜单栏的Rules,选择Customize Rules... 这时会打开CustomRules.js文件,在class Handl ...
- Android移动网络如何抓取数据包
1)下载tcpdump工具 tcpdump(dump the traffic on a network)是Linux中强大的网络数据采集分析工具之一,可以将网络中传送的数据包头完全截获下来提供分析.它 ...
- tcpdump 基于mac地址抓取数据包
1.刚刚接触tcpdump时,常用tcpdump -i eth1 host 192.168.1.1 这个命令基于ip地址抓取数据包信息. tcpdump -i eth1(接口名称) host 192. ...
- Fiddler抓取数据包分析案例
案例:利用Fiddler抓取苏宁易购网站数据包分析 抓包软件:Fiddler4 请求名字:www.suning.com 详细内容: 一.了解数据包区域的字段含义 图1数据包区域 #:顺序号,按照抓包的 ...
- 利用tcpdump抓取网络包
1.下载并安装tcpdump 下载地址:tcpdump 安装tcpdump,连接adb adb push tcpdump /data/local/tcpdump adb shell chmod 675 ...
- 利用libpcap抓取QQ号码信息
最近想在QQ登录时把QQ号码信息记录下来,百度了很多都没有找到具体方式,最近用Wireshark分析报文+libpcap库嗅探实现了这个小功能. 通讯背景: QQ客户端在通讯时使用UDP协议,其中数据 ...
- 使用Wireshark 抓取数据包
Wireshark 是一个网络封包分析软件.网络封包分析软件的功能是获取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与网卡进行数据报文交换. 一 ...
- WinPcap抓取数据包
#WinPcap和Libpcap的最强大的特性之一,是拥有过滤数据包的引擎. 它提供了有效的方法去获取网络中的某些数据包,这也是WinPcap捕获机制中的一个组成部分. 用来过滤数据包的函数是#
- iPhone 手机用 Fiddler 抓取数据包 问题
近日公司服务升级,将所有的接口请求由HTTP升级为了HTTPS,升级后在手机中安装了Fiddler的证书,Android端抓取HTTPS请求一切正常,可是在ios端抓取HTTPS请求时一直提示“此服务 ...
随机推荐
- 【capstone/ropgadget】环境配置
具体环境配置可参考 https://github.com/JonathanSalwan/ROPgadget/tree/master 作者给出的安装方式 但具体配置中出现了问题,如引用时出现如下错误: ...
- 10. Regular Expression Matching正则表达式匹配
Implement regular expression matching with support for '.' and '*'. '.' Matches any single character ...
- /etc/fstab 只读无法修改的解决办法
在做saltstack的时候不小心误把/etc/fstab给注释了 在命令补全的时候开始报错:[root@kafka2 ~]# cat /et-bash: cannot create temp fil ...
- jpa自定义sql语句
/** * 查询还没生成索引的帖子 * @return */ @Query(value = "SELECT * FROM t_article WHERE index_state=0" ...
- 060、Java中定义有返回值有参数的方法
01.代码如下: package TIANPAN; /** * 此处为文档注释 * * @author 田攀 微信382477247 */ public class TestDemo { public ...
- 原生JS 实现 dom ready
记录一下项目技术问题: 记得:放在head标签内的脚本,第一时间执行 var baseTools = { // dom ready ready: function( f ){ var ie = !!( ...
- 五、Vue:使用axios库进行get和post、用拦截器对请求和响应进行预处理、Mock(数据模拟)
一.axios [应用]进行请求和传表单 [axios中文档]:https://www.kancloud.cn/yunye/axios/234845 [vue-axios]:https://cn.vu ...
- MQTT 协议学习:005-发布消息 与 对应报文 (PUBLISH、PUBACK、PUBREC、PUBREL)
背景 当有订阅者订阅了有关的主题以后,通过发布消息的消息的动作,可以让订阅者收到对应主题的消息. 根据不同的QoS 等级,通信的动作也略有不同. PUBLISH – 发布消息 报文 PUBLISH控制 ...
- P1081 检查密码
P1081 检查密码 转跳点:
- 洛谷 P2549 计算器写作文
题目传送门 解题思路: 背包,f[i]表示计算器位数为i时,可获得的最大分值. 本题与01背包不同的地方在于,物品的摆放顺序对答案是有影响的,例如两个字符串a,b,那么就会出现a+b和b+a两种情况( ...