利用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请求时一直提示“此服务 ...
随机推荐
- layerui上传文件
参考: http://www.layui.com/doc/modules/upload.html <1> 文件上传(以下函数必须要在js文件加载时执行) upload.render({ e ...
- 学习进度-10 python爬虫
学习爬虫的第一个案例是小说爬虫. 小说爬虫首先是解析小说页面源代码,在页面源代码中可以看到小说每章节的内容链接 爬虫的代码: import requests import re url = 'http ...
- Go语言 Note
1.简单的CURD之搭建基础框架 //路由层 func Router(rg *gin.RouterGroup){ rg.GET("/getsupplier", facility.G ...
- SQL添加列、非空、默认值
use MarcoBarcode go alter table [dbo].[WorkOrderRepairSheet] ADD needRepair int go ALTER TABLE [dbo] ...
- flutter之VSCode下Flutter常用终端命令行
https://www.cnblogs.com/lxlx1798/p/11049922.html 梁飞宇 [Flutter学习]之VSCode下Flutter常用终端命令行 Flutter 常用命令行 ...
- Arch系Linux中安装Docker
Arch系Linux中安装Docker 1. 下载最新版docker $ sudo pacman -Syu docker 2. 免sudo执行docker $ sudo gpasswd -a ${US ...
- P1054 求平均值
P1054 求平均值 转跳点:
- 2的n次幂
位运算判断2的n次幂: 举个栗子,n = 8:则二进制表示就为1000,n-1则为 0111 取&刚好等于0 嘿嘿,巧妙吧. 再举个栗子,n = 7: 则二进制为 0111,n-1则为0110 ...
- Flutter Windows下AndroidStudio环境搭建
目前同类产品比较知名的有ReactNative,Flutter还有国内那家了uniapp了,流畅度理论上Flutter最快 官网:https://flutter.dev/docs/get-starte ...
- TensorFlow2 Part1:基础
TensorFlow™是一个基于数据流编程(dataflow programming)的符号数学系统,被广泛应用于各类机器学习(machine learning)算法的编程实现,其前身是谷歌的神经网络 ...