粉丝不过W
socket 原始套接字回顾
原始套接字
开发者可发送任意的数据包到网上
开发网络攻击等特殊软件
需要开发者手动组织数据、各种协议包头、校验和计算
创建方法:
//创建数据链路层的原始套接字
int sock_raw_fd = 0;
sock_raw_fd = socket(PF_PACKET, SOCK_RAM, htons(ETH_P_ALL));
Libpcap 概念
一个网络数据捕获开发包
平台独立具有强大功能
一套高层的编程接口的集合;隐藏了操作系统的细节,可捕获网上的所有,包括到达其他主机的数据包
使用非常广泛,几乎只要涉及到网络数据包的捕获的功能,都可以用它开发,如 wireshark
开发语言为 C 语言,也有基于其它语言的开发包,如 Python 语言的开发包 pycap
Libpcap 主要的作用
捕获各种数据包
如:网络流量统计
过滤网络数据包
如:过滤掉本地上的一些数据,类似防火墙
分析网络数据包
如:分析网络协议,数据的采集
存储网络数据包
如:保存捕获的数据以为将来进行分析
Libpcap 的安装:
sudo apt-get install libpcap-dev
libpcap 开发实例
libpcap 函数库开发应用程序基本步骤:
打开网络设备
设置过滤规则
捕获数据
关闭网络设备
捕获网络数据包常用函数:
pcap_lookupdev( )
pcap_open_live()
pcap_lookupnet( )
pcap_compile( )
pcap_setfilter( )
pcap_next( )
pcap_loop( )
pcap_close( )
pcap_lookupdev
/*
*function:
* 得到可用的网络设备名指针
*parameter:
* errbuf: 存放相关的错误信息
*return:
* 成功:设备名指针
* 失败: NULL
*/
char *pcap_lookupdev(char *errbuf)
如:
char *dev = NULL;
char err_buf[100] = "";
dev = pcap_lookupdev(err_buf);
if(NULL == dev)
{
perror("pcap_lookupdev");
exit(-1);
}
pcap_open_live
/*
*function:
* 打开一个用于捕获数据的网络接口
*parameter:
* device: 网络接口的名字
* snaplen:捕获数据包的长度
* promise:1 代表混杂模式, 其它非混杂模式
* to_ms: 等待时间
* ebuf: 存储错误信息
*return:
* 返回一个 Libpcap 句柄
*/
pcap_t *pcap_open_live(const char *device,int snaplen,int promisc,int to_ms,har *ebuf);
如:
char error_content[PCAP_ERRBUF_SIZE] = "";
pcap_t *pcap_handle = NULL;
pcap_handle = pcap_open_live("eth0", 1024, 1, 0, error_content);
pcap_lookupnet
/*
*function:
* 获得指定网络设备的网络号和掩码
*parameter:
* device:网络设备名
* netp:存放网络号的指针
* maskp:存放掩码的指针
* errbuf:存放出错信息
*retrun:
* 成功: 0
* 失败: -1
*/
int pcap_lookupnet(char *device, buf_u_int32 *netp, bpf_u_int32 *maskp, char *errbuf);
unsigned int net_ip; //网络地址
unsigned int net_mask; //子网掩码
res = pcap_lookupnet(dev, &net_ip, &net_mask, error_content);
if(res == -1)
{
perror("pcap_lookupnet");
exit(-1);
}
pcap_compile
/*
*function:
* 编译 BPF 过滤规则
*parameter:
* p: Libpcap 句柄
* program: bpf 过滤规则
* buf: 过滤规则字符串
* optimize:优化
* mask: 掩码
*retrun:
* 成功: 0
* 失败: -1
*/
int pcap_compile(pcap_t *p, struct bpf_program *program, char *buf, int optimize, bpf_u_int32 mask);
如:
struct buf_program buf_filter;
char *bpf_filter_string = "arp or ip";
//编译BPF过滤规则
if(pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, 0xffffff00) < 0)
{
perror("pcap_compile");
}
pcap_setfilter
/*
*function:
* 设置 BPF 过滤规则
*parameter:
* p:Libpcap 句柄
* fp:BPF 过滤规则
*return:
* 成功: 0
* 失败: -1
*/
int pcap_setfilter(pcap *p, struct bpf_program *fp);
//设置过滤规则
if(pcap_setfilter(pcap_handle, &bpf_filter) < 0)
{
perror("pcap_setfilter");
}
pcap_next
/*
*function:
* 捕获一个网络数据包
*parameter:
* p:Libpcap 句柄
* h:数据包头
*return:
* 捕获的数据包的地址
*/
const u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h);
struct pcap_pkthdr protocol_header;
unsigned char *p_packet_content = NULL;
p_packet_content = pcap_next(pcap_handle, &protocol_header);
pcap_loop
/*
*function:
* 循环捕获网络数据包,直到遇到错误或者满足退出条件;
*每次捕获一个数据包就会调用 callback 指示的回调函数,所以可在回调函数中进行数据包的处理操作
*parameter:
* p: Libpcap 句柄
* cnt: 指定捕获数据包的个数,如 -1,会永无休止的捕获
* callback:回调函数
* user: 向回调函数中传递的参数
*/
int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user);
if(pcap_loop(pcap_handle, -1, ethernet_protocol_callback, NULL) < 0)
{
perror("pcap_loop");
}
pcap_close
/*
*function:
* 关闭 Libpcap 操作,并销毁相应的资源
*parameter:
* p:关闭的 Libpcap 句柄
*/
void pcap_close(pcap_t *p);
pcap_close(pcap_handle);
捕获第一个网络数据包
/*
*function:
* 通过捕获一个网络数据包,然后对其进行数据的解析分析
*/
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <time.h>
#define BUFSIZE 1514
struct ether_header
{
unsigned char ether_dhost[6]; //目的mac
unsigned char ether_shost[6]; //源mac
unsigned short ether_type; //以太网类型
};
/*
*function:
* 通过使用libpcap接收一个数据包,然后对数据包进行解析
*/
int main(int argc,char *argv[])
{
pcap_t * pcap_handle = NULL;
char error_content[100] = ""; // 出错信息
unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
unsigned char *p_mac_string = NULL; // 保存mac的地址,临时变量
unsigned short ethernet_type = 0; // 以太网类型
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(-1);
}
//打开网络接口
pcap_handle = pcap_open_live(p_net_interface_name, BUFSIZE, 1, 0, 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 + 0),
*(p_mac_string + 1),
*(p_mac_string + 2),
*(p_mac_string + 3),
*(p_mac_string + 4),
*(p_mac_string + 5));
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 + 0),
*(p_mac_string + 1),
*(p_mac_string + 2),
*(p_mac_string + 3),
*(p_mac_string + 4),
*(p_mac_string + 5));
//获得以太网的数据包的地址,然后分析出上层网络协议的类型
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"); //ip
break;
case 0x0806 :
printf("The network layer is ARP protocol\n"); //arp
break;
case 0x0835 :
printf("The network layer is RARP protocol\n"); //rarp
break;
default :
printf("The network layer unknow!\n");
break;
}
pcap_close(pcap_handle);
return 0;
}
gcc 编译时需要加上-lpcap
运行时需要使用超级权限
捕获指定类型数据包,如 ARP
/*
*function:
* 安排规则,接收一个数据包
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <arpa/inet.h>
#include <pcap.h>
#define BUFSIZE 1514
struct ether_header
{
unsigned char ether_dhost[6]; //目的mac
unsigned char ether_shost[6]; //源mac
unsigned short ether_type; //以太网类型
};
int main(int argc,char *argv[])
{
pcap_t * pcap_handle;
int ret = 0;
char error_content[512] = ""; // 出错信息
const unsigned char *p_packet_content = NULL; // 保存接收到的数据包的起始地址
unsigned char *p_mac_string = NULL; // 保存mac的地址,临时变量
unsigned short ethernet_type = 0; // 以太网类型
char *p_net_interface_name = "eth0"; // 接口名字
struct pcap_pkthdr protocol_header;
struct ether_header *ethernet_protocol = NULL;
struct bpf_program bpf_filter;
char *bpf_filter_string = "arp or ip";
bpf_u_int32 netp = 0, maskp = 0;
//打开网络接口
p_net_interface_name = pcap_lookupdev(error_content);
if(NULL == p_net_interface_name)
{
perror("pcap_lookupdev");
exit(-1);
}
pcap_handle = pcap_open_live(p_net_interface_name, 1024, 1, 0, error_content);
//获得网络号和掩码
ret = pcap_lookupnet(p_net_interface_name, &netp, &maskp, error_content);
if(ret == -1)
{
perror("pcap_lookupnet");
exit(-1);
}
//编译BPF过滤规则
if(pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, maskp) < 0)
{
perror("pcap_compile");
}
//设置过滤规则
if(pcap_setfilter(pcap_handle,&bpf_filter) < 0)
{
perror("pcap_setfilter");
}
while(1)
{
//所捕获的数据包的地址
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 + 0),
*(p_mac_string + 1),
*(p_mac_string + 2),
*(p_mac_string + 3),
*(p_mac_string + 4),
*(p_mac_string + 5));
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 + 0),
*(p_mac_string + 1),
*(p_mac_string + 2),
*(p_mac_string + 3),
*(p_mac_string + 4),
*(p_mac_string + 5));
//获得以太网的数据包的地址,然后分析出上层网络协议的类型
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"); //ip
break;
case 0x0806:
printf("The network layer is ARP protocol\n"); //arp
break;
case 0x0835:
printf("The network layer is RARP protocol\n"); //rarp
break;
default:
printf("The network layer unknow!!!\n");
break;
}
}
pcap_close(pcap_handle);
return 0;
}
捕获多个网络数据包
/*
*function:
* 通过回调函数的方式,来抓取多个网络数据包
*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pcap.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BUFSIZE 1514
struct ether_header
{
unsigned char ether_dhost[6]; //目的mac
unsigned char ether_shost[6]; //源mac
unsigned short ether_type; //以太网类型
};
void ethernet_protocol_callback(unsigned char *argument,
const struct pcap_pkthdr *packet_heaher,
const unsigned char *packet_content);
int main(int argc, char *argv[])
{
char error_content[100]; //出错信息
pcap_t * pcap_handle;
unsigned char *mac_string;
unsigned short ethernet_type; //以太网类型
unsigned int net_ip; //网络地址
unsigned int net_mask; //子网掩码
char *net_interface = NULL; //接口名字
int res = 0;
struct pcap_pkthdr protocol_header;
struct ether_header *ethernet_protocol;
struct bpf_program bpf_filter;
char bpf_filter_string[] = "ip";
//获取网络接口
net_interface = pcap_lookupdev(error_content);
if(NULL == net_interface)
{
perror("pcap_lookupdev");
exit(-1);
}
res = pcap_lookupnet(net_interface, &net_ip, &net_mask, error_content);
if(-1 == res)
{
perror("pcap_loopupnet");
exit(-1);
}
//打开网络接口
pcap_handle = pcap_open_live(net_interface, BUFSIZE, 1, 0, error_content);
//编译BPF过滤规则
pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_mask);
pcap_setfilter(pcap_handle, &bpf_filter);//设置过滤规则
if(pcap_loop(pcap_handle,-1,ethernet_protocol_callback,NULL) < 0)
{
perror("pcap_loop");
}
pcap_close(pcap_handle);
return 0;
}
// 回调函数
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 + 0),
*(mac_string + 1),
*(mac_string + 2),
*(mac_string + 3),
*(mac_string + 4),
*(mac_string + 5));
mac_string = (unsigned char *)ethernet_protocol->ether_dhost; //获取目的mac
printf("Mac Destination Address is %02x:%02x:%02x:%02x:%02x:%02x\n",*(mac_string + 0),
*(mac_string + 1),
*(mac_string + 2),
*(mac_string + 3),
*(mac_string + 4),
*(mac_string + 5));
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"); //ip
break;
case 0x0806:
printf("The network layer is ARP protocol\n"); //arp
break;
case 0x0835:
printf("The network layer is RARP protocol\n"); //rarp
break;
default:
break;
}
usleep(800*1000);
}