利用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请求时一直提示“此服务 ...
随机推荐
- 分享一款免费的工控组态软件(PCHMI)
PCHMI严格的讲它并不是一款组态软件,也不是一款SCADA软件,而是一个基于.NET构架的DLL文件,开发者可以使用微软的Visual Studio将PCHMI.DLL加载到工具箱里面进行二次开发. ...
- 在线关闭 CLOSE_WAIT状态TCP连接
1.查看某个端口的所有TCP连接: [root@Centos projects]# netstat -anp | tcp6 ::: :::* LISTEN /java tcp6 CLOSE_WAIT ...
- 前端学习(20)~css布局(十三)
常见的布局属性 (1)display 确定元素的显示类型: block:块级元素. inline:行内元素. inline-block:对外的表现是行内元素(不会独占一行),对内的表现是块级元素(可以 ...
- java#内部类和嵌套类
内容思路来自Java编程思想,个人读书做的笔记,仅个人复习之用,故他人参考请自行辨别内容是否有错误. 在类的类部可以定义类,叫做内部类.如果这个内部类被static修饰,此时内部的类叫做嵌套类. 内部 ...
- css3的伪(伪类和伪元素)大合集
本文讲css3的伪,不是讲它有多虚伪,而是说它的伪元素样式.不得不说以前虽知html伪元素,但很少用,后得知借助css3伪元素可以发挥极大的便利.故总结css3的伪如下: CSS中存在一些比较特殊的属 ...
- 盘姬工具箱WV1.10
========================================================================== {盘姬工具箱CruiserEXPforWin版是一 ...
- 08 SSM整合案例(企业权限管理系统):05.SSM整合案例的基本介绍
04.AdminLTE的基本介绍 05.SSM整合案例的基本介绍 06.产品操作 07.订单操作 08.权限控制 09.用户和角色操作 10.权限关联 11.AOP日志 05.SSM整合案例的基本介绍 ...
- Power BI 安装注册教程
把下载好的MSI包PBIDesktop_x64.msi 双击安装 点击下一步 选择文件路径 点击安装 点击完成 初始化中 安装完成界面 账号注册 https://powerbi.microsoft.c ...
- unicode字符等价探究
Demobaidu.com(\uff41)能跳转到baidu.combаidu.com(\u0430)不能跳转到baidu.com,被认为成一个其他域名 等价原因两个不同编码的Unicode字符之间可 ...
- Create Table操作
CREATE TABLE 语句 CREATE TABLE 语句用于创建数据库中的表. SQL CREATE TABLE 语法 CREATE TABLE 表名称 ( 列名称1 数据类型, 列名称2 数据 ...