1.libcap的简介
Libpcap是Packet Capture Libray的英文缩写,即数据包捕获开源的C函数库,用于捕获网卡数据或分析pcap格式的抓包报文。Tcpdump和wireshark均是以此为基础的。
主要功能有:网络报文抓取;网络报文的构建;抓包文件的分析;自定义BFP过滤。
2.网络报文
网络报文是按照协议层次逐层构建的,简单按照四层协议来理解就是在应用层数据和协议的基础上构建传输层报文,在传输层协议的基础上构建网络层报文,在网络层报文的基础上构建数据链路/物理层报文。
展示如下图:
3.应用说明
libcap主要用于网络嗅探,如下图:
基于libcap的应用一般处理过程:
4.常用函数说明
1、打开网络接口
//这个函数会返回指定接口的pcap_t类型指针,后面的所有操作都要使用这个指针。
pcap_t * pcap_open_live(const char * device, int snaplen, int promisc, int to_ms, char * errbuf)
device:网络接口字符串,可以直接使用硬编码,比如eth0。
snaplen:对于每个数据包,从开头要抓多少个字节,我们可以设置这个值来只抓每个数据包的头部,而不关心具体的内容。典型的以太网帧长度是1518字节,但其他的某些协议的数据包会更长一点,但任何一个协议的一个数据包长度都必然小于65535个字节。
promisc:指定是否打开混杂模式(Promiscuous Mode),0表示非混杂模式,任何其他值表示混合模式。如果要打开混杂模式,那么网卡必须也要打开混杂模式,可以使用如下的命令打开eth0混杂模式:ifconfig eth0
to_ms:抓包时长单位为毫秒,0标示一直等待。
errbuf: 输出参数,打开网络接口失败原因。
2、打开离线的pcap文件
pcap_t * pcap_open_offline (const char *fname, char *errbuf)
fname :文件名称。
errbuf :打开失败的错误信息。
3、抓包函数
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
p: 打开的pcap_t类型指针。
cnt:一共抓多少个包,如果为负数就一直循环。
callback:回调函数指针
user:传递给回调函数的参数。
void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
userarg:是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
pkthdr: 抓到的报文头信息。
packet:收到的包的数据。
4、过滤函数编译
int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)
//fp:这是一个传出参数,存放编译后的bpf
//str:过滤表达式
//optimize:是否需要优化过滤表达式
//metmask:简单设置为0即可
5、设置过滤函数
int pcap_setfilter(pcap_t * p, struct bpf_program * fp)
//参数fp就是pcap_compile()的第二个参数,存放编译后的bpf
6、释放网络接口
void pcap_close(pcap_t * p)
//该函数用于关闭pcap_open_live()获取的pcap_t的网络接口对象并释放相关资源。
7、打开网络包保存文件
pcap_dumper_t * pcap_dump_open (pcap_t *p, const char *fname)
//p:是我们已经打开的网络设备,从这个设备接收数据包。
// fname:是我们要写入的文件名,随便起。
//return: 如果出错,会返回NULL。可以借此检查这个文件有没有打开。
8、将网络包写入文件
void pcap_dump (u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
user:就是文件描述符dumpfp,只不过要做一下类型转换。
由于这个函数一般在pcap_loop()的函数指针所指向的packet_handler中使用,所以packet_handler中的user就是这里的user。
h:就是pkt_header
9、网络包文件关闭
pcap_dump_close(pcap_dumper_t * t);
数据结构说明:
struct pcap_pkthdr {
struct timeval ts; /* time stamp */
bpf_u_int32 caplen; /* 抓到的数据包实际长度 */
bpf_u_int32 len; /*数据包的长度 */
};
程序实例
#include
#include
#include
#include
#include
#include
#include
//链路层数据包格式 以太网雷系
typedef struct {
//目的mac地址
u_char DestMac[6];
//源mac地址
u_char SrcMac[6];
//协议类型 0x0800为IPV4
u_char Etype[2];
}ETHHEADER;
//IP层数据包格式
typedef struct {
//头长度
int header_len:4;
//版本
int version:4;
u_char tos:8;
//总长度
int total_len:16;
int ident:16;
int flags:16;
//生存时间ttl
u_char ttl:8;
u_char proto:8;
int checksum:16;
u_char sourceIP[4];
u_char destIP[4];
}IPHEADER;
//协议映射表
char *Proto[]={
"Reserved","ICMP","IGMP","GGP","IP","ST","TCP"
};
void writeToFile(u_char*user,const struct pcap_pkthdr* header,const u_char* pkt_data)
{
pcap_dump((char *)user, header, pkt_data);
}
//回调函数
void pcap_handle(u_char* user,const struct pcap_pkthdr* header,const u_char* pkt_data)
{
writeToFile(user,header,pkt_data);
ETHHEADER *eth_header=(ETHHEADER*)pkt_data;
printf("---------------Begin Analysis-----------------\n");
printf("----------------------------------------------\n");
printf("Packet length: %d \n",header->len);
//解析数据包IP头部
if(header->len>=14){
IPHEADER *ip_header=(IPHEADER*)(pkt_data+14);
//解析协议类型
char strType[100];
if(ip_header->proto>7)
strcpy(strType,"IP/UNKNWN");
else
strcpy(strType,Proto[ip_header->proto]);
printf("Source MAC : %02X-%02X-%02X-%02X-%02X-%02X==>",eth_header->SrcMac[0],eth_header->SrcMac[1],eth_header->SrcMac[2],eth_header->SrcMac[3],eth_header->SrcMac[4],eth_header->SrcMac[5]);
printf("Dest MAC : %02X-%02X-%02X-%02X-%02X-%02X\n",eth_header->DestMac[0],eth_header->DestMac[1],eth_header->DestMac[2],eth_header->DestMac[3],eth_header->DestMac[4],eth_header->DestMac[5]);
printf("Source IP : %d.%d.%d.%d==>",ip_header->sourceIP[0],ip_header->sourceIP[1],ip_header->sourceIP[2],ip_header->sourceIP[3]);
printf("Dest IP : %d.%d.%d.%d\n",ip_header->destIP[0],ip_header->destIP[1],ip_header->destIP[2],ip_header->destIP[3]);
printf("Protocol : %s\n",strType);
//显示数据帧内容
int i;
for(i=0; i<(int)header->len; ++i) {
printf("%02X ", pkt_data[i]);
if( (i + 1) % 16 == 0 )
printf("\n");
}
printf("\n\n");
printf("-------------ASCII------------------");
u_char * data = (u_char *)malloc((header->len- 14 - sizeof(IPHEADER))*sizeof(u_char* ));
strncpy(data,(char*)(pkt_data+14+sizeof(IPHEADER)),header->len- 14 - sizeof(IPHEADER));
printf("%s",data);
}
}
int main(int argc, char **argv)
{
char *device="eth0";
char errbuf[1024];
pcap_t *phandle;
bpf_u_int32 ipaddress,ipmask;
struct bpf_program fcode;
int datalink;
if((device=pcap_lookupdev(errbuf))==NULL){
perror(errbuf);
return 1;
}
else
printf("device: %s\n",device);
phandle=pcap_open_live(device,200,0,500,errbuf);
if(phandle==NULL){
perror(errbuf);
return 1;
}
if(pcap_lookupnet(device,&ipaddress,&ipmask,errbuf)==-1){
perror(errbuf);
return 1;
}
else{
char ip[INET_ADDRSTRLEN],mask[INET_ADDRSTRLEN];
if(inet_ntop(AF_INET,&ipaddress,ip,sizeof(ip))==NULL)
perror("inet_ntop error");
else if(inet_ntop(AF_INET,&ipmask,mask,sizeof(mask))==NULL)
perror("inet_ntop error");
printf("IP address: %s, Network Mask: %s\n",ip,mask);
}
int flag=1;
while(flag){
//input the design filter
printf("Input packet Filter: ");
char filterString[1024];
scanf("%s",filterString);
if(pcap_compile(phandle,&fcode,filterString,0,ipmask)==-1)
fprintf(stderr,"pcap_compile: %s,please input again....\n",pcap_geterr(phandle));
else
flag=0;
}
if(pcap_setfilter(phandle,&fcode)==-1){
fprintf(stderr,"pcap_setfilter: %s\n",pcap_geterr(phandle));
return 1;
}
if((datalink=pcap_datalink(phandle))==-1){
fprintf(stderr,"pcap_datalink: %s\n",pcap_geterr(phandle));
return 1;
}
printf("datalink= %d\n",datalink);
pcap_dumper_t *t = pcap_dump_open(dev, "./test.pcap");
if (NULL == t){
fprintf(stderr, "pcap_dump_open failed.\n");
return 1;
}
pcap_loop(phandle,-1,pcap_handle,( u_char * )t);
pcap_dump_close(t);
return 0;
}