libpcap抓包并分析

2023-12-16 14:38
文章标签 分析 抓包 libpcap

本文主要是介绍libpcap抓包并分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于libpcap的数据包抓取

1.libpcap安装

前提安装gcc

然后安装输入如下命令:

yum -y install flex
yum -y install bison

在一个文件夹下下载libpcap源码并解压,在安装如下:

wget -c http://www.tcpdump.org/release/libpcap-1.7.4.tar.gz
进入libpcap-1.7.4,输入:
./configure
make
make install

这里写图片描述

2.库函数及相应功能了解

1)pcap_lookupdev();

函数用来查找网络设备,返回可被pcap_open_live()函数调用的网络设备名指针。

2)pcap_open_live(char *device,int catch_byte,int promisc,int time,char *errbuf);

函数pcap_open_live是用于获取网络接口的函数,函数返回一个指针用于后续对该接口的操作,device是设备名,第二个是每次抓包抓取多少个字节,最多65535个字节,第三个参数是将设备设置成混杂模式,1为混杂模式,其他非混杂,第四个参数是抓取时间,指定需要等地的毫秒数,超过这个时间后,获得数据包的函数会立即返回,0表示一直等待直到有数据包到来,errbuf保存错误信息。

函数返回数据类型为pcap_t指针。

3)pcap_lookupnet(char *device,bpf_u_int32 *ipaddress,bpf_u_int32 *ipmask,char *errbuf);

函数pcap_lookupnet用于查看设备ip和掩码

4)pcap_compile(pcap_t *p,struct bpf_program *fp,char *filterstr,int opt,bpf_u_int32 netmask);
5)pcap_setfilter(pcap_t *p,struct bpf_program *fp);

函数pcap_compile是用来设置过滤条件的,即设置抓取的包的条件,条件为filterstr,配合pcap_setfilter函数完成设置。

6)pcap_loop();

与pcap_next()和pcap_next_ex()两个函数一样用来捕获数据包

3.代码+实验

参考 官方文档

1.确定嗅探设备,比如ens33

可以自己传入一个string作为嗅探设备,也可以使用pcap_lookupdev()函数来让程序给我们提供嗅探设备。

代码分别如下:

#include <stdio.h>
#include <pcap.h>int main(int argc, char *argv[])
{char *dev = argv[1];//传入参数做嗅探设备printf("Device: %s\n", dev);return(0);
}

这里写图片描述

注意编译方式,见上图。
#include <stdio.h>
#include <pcap.h>int main(int argc, char *argv[])
{char *dev,errbuf[1024];dev=pcap_lookupdev(errbuf);if(dev==NULL){printf("%s\n",errbuf);return 0;}printf("Device: %s\n", dev);return 0;
}

这里写图片描述

2.打开嗅探设备,准备嗅探

函数讲解见部分2.库函数及相应功能了解

    pcap_t *pcap_handle=pcap_open_live(dev,65535,1,0,errbuf);if(pcap_handle==NULL){printf("%s\n",errbuf);return 0;}

pcap_t*这个指针很重要,后面基本所有操作都会用到。

获取网络号(ip)和掩码:int pcap_lookupnet(char *device,bpf_u_int32 *ipaddress,bpf_u_int32 *ipmask,char *errbuf);

device为设备,ipaddress记录设备ip,ipmask记录设备掩码,errbuf存储错误信息,返回-1出错,0成功

#include <stdio.h>
#include <pcap.h>
#include<time.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<string.h>int main(int argc, char *argv[])
{char *dev,errbuf[1024];//确定网络接口dev=argv[1];if(dev==NULL){printf("device is null\n");return 0;}//打开网络接口pcap_t *pcap_handle=pcap_open_live(dev,65535,1,0,errbuf);if(pcap_handle==NULL){printf("%s\n",errbuf);return 0;}//获取网络号ip和掩码struct in_addr addr;bpf_u_int32 ipaddress, ipmask;char *dev_ip,*dev_mask;if(pcap_lookupnet(dev,&ipaddress,&ipmask,errbuf)==-1){printf("%s\n",errbuf);return 0;}//输出ipaddr.s_addr=ipaddress;dev_ip=inet_ntoa(addr);printf("ip address : %s\n",dev_ip);//输出掩码addr.s_addr=ipmask;dev_mask=inet_ntoa(addr);printf("netmask : %s\n",dev_mask);return 0;
}

这里写图片描述

不太清楚最后的一个字节为什么为0…可以看看源码研究一下。

3.获取网络数据包

方法1: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; // 表示数据包的实际长度,本来应有长度
}  

使用:

    const char *p_packet_content;//实际接收数据起始地址struct pcap_pkthdr protocol_header;//数据包头p_packet_content=pcap_next(pcap_handle,&protocol_header);printf("capture time : %s",ctime((const time_t*)&protocol_header.ts.tv_sec));printf("packet length : %d\n",protocol_header.len);

这里写图片描述

方法2:int pcap_loop(pcap_t *p,int cnt,pcap_handler callback,u_char *user);

循环捕获网络数据包,直到遇到错误或者满足退出条件,每次捕获一个数据包就会调用callback指定的回调函数,所以,可以在回调函数中进行数据包的处理操作,返回值:成功返回0,失败返回负数

·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:收到的数据包数据首地址

代码:

#include <stdio.h>
#include <pcap.h>
#include<time.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<string.h>
//回调函数
void pcap_callback(unsigned char * arg,const struct pcap_pkthdr *packet_header,const unsigned char *packet_content){int *id=(int *)arg;//记录包IDprintf("id=%d\n",++(*id));printf("Packet length : %d\n",packet_header->len);printf("Number of bytes : %d\n",packet_header->caplen);printf("Received time : %s\n",ctime((const time_t*)&packet_header->ts.tv_sec));int i;for(i=0;i<packet_header->caplen;i++){printf(" %02x",packet_content[i]);if((i+1)%16==0){printf("\n");}}printf("\n\n");
}int main(int argc, char *argv[])
{char *dev,errbuf[1024];dev=argv[1];if(dev==NULL){printf("device is null\n");return 0;}pcap_t *pcap_handle=pcap_open_live(dev,65535,1,0,errbuf);if(pcap_handle==NULL){printf("%s\n",errbuf);return 0;}struct in_addr addr;bpf_u_int32 ipaddress, ipmask;char *dev_ip,*dev_mask;if(pcap_lookupnet(dev,&ipaddress,&ipmask,errbuf)==-1){printf("%s\n",errbuf);return 0;}addr.s_addr=ipaddress;dev_ip=inet_ntoa(addr);printf("ip address : %s\n",dev_ip);addr.s_addr=ipmask;dev_mask=inet_ntoa(addr);printf("netmask : %s\n",dev_mask);printf("---------packet--------\n");int id=0;//传入回调函数记录IDif(pcap_loop(pcap_handle,10,pcap_callback,(unsigned char *)&id)<0){//接收十个数据包printf("error\n");return 0;}pcap_close(pcap_handle);return 0;
}

这里写图片描述

4.过滤数据包

设置过滤条件:

举一些例子:

  • 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

使用:

    struct bpf_program filter;pcap_compile(pcap_handle,&filter,"dst port 80",1,0);pcap_setfilter(pcap_handle,&filter);

以上代码运行,过滤条件是dst port 80,打开浏览器,数据包就接收到了。

这里写图片描述

附:保存抓取到的数据,并保存至aaa.pcap文件里,可用wireshark打开

函数:

pcap_dumper_t* dumpfp=pcap_dump_open(pcap_t *p,char[] path);

pcap_dump_close(pcap_dumper_t* dumpfp);

还需使用pcap_dump(arg,packet_header,packet_content);放在回调函数里,从而保存数据

以上两个函数是一对

具体代码:

这里写图片描述

这里写图片描述

以上基本函数使用讲解完毕。

4.具体数据包

以下只提供代码,来解析数据包。不同协议分析可仿照如下:(需要学习网络协议)

#include <stdio.h>
#include <pcap.h>
#include<time.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<errno.h>
#include<string.h>typedef struct eth_hdr
{u_char dst_mac[6];u_char src_mac[6];u_short eth_type;
}eth_hdr;
eth_hdr *ethernet;typedef struct ip_hdr
{int version:4;int header_len:4;u_char tos:8;int total_len:16;int ident:16;int flags:16;u_char ttl:8;u_char protocol:8;int checksum:16;u_char sourceIP[4];u_char destIP[4];
}ip_hdr;
ip_hdr *ip;typedef struct tcp_hdr
{u_short sport;u_short dport;u_int seq;u_int ack;u_char head_len;u_char flags;u_short wind_size;u_short check_sum;u_short urg_ptr;
}tcp_hdr;
tcp_hdr *tcp;typedef struct udp_hdr
{u_short sport;u_short dport;u_short tot_len;u_short check_sum;
}udp_hdr;
udp_hdr *udp;void pcap_callback(unsigned char * arg,const struct pcap_pkthdr *packet_header,const unsigned char *packet_content){static int id=1;printf("id=%d\n",id++);pcap_dump(arg,packet_header,packet_content);printf("Packet length : %d\n",packet_header->len);printf("Number of bytes : %d\n",packet_header->caplen);printf("Received time : %s\n",ctime((const time_t*)&packet_header->ts.tv_sec));int i;for(i=0;i<packet_header->caplen;i++){printf(" %02x",packet_content[i]);if((i+1)%16==0){printf("\n");}}printf("\n\n");u_int eth_len=sizeof(struct eth_hdr);u_int ip_len=sizeof(struct ip_hdr);u_int tcp_len=sizeof(struct tcp_hdr);u_int udp_len=sizeof(struct udp_hdr);printf("analyse information:\n\n");printf("ethernet header information:\n");ethernet=(eth_hdr *)packet_content;printf("src_mac : %02x-%02x-%02x-%02x-%02x-%02x\n",ethernet->src_mac[0],ethernet->src_mac[1],ethernet->src_mac[2],ethernet->src_mac[3],ethernet->src_mac[4],ethernet->src_mac[5]);printf("dst_mac : %02x-%02x-%02x-%02x-%02x-%02x\n",ethernet->dst_mac[0],ethernet->dst_mac[1],ethernet->dst_mac[2],ethernet->dst_mac[3],ethernet->dst_mac[4],ethernet->dst_mac[5]);printf("ethernet type : %u\n",ethernet->eth_type);if(ntohs(ethernet->eth_type)==0x0800){printf("IPV4 is used\n");printf("IPV4 header information:\n");ip=(ip_hdr*)(packet_content+eth_len);printf("source ip : %d.%d.%d.%d\n",ip->sourceIP[0],ip->sourceIP[1],ip->sourceIP[2],ip->sourceIP[3]);printf("dest ip : %d.%d.%d.%d\n",ip->destIP[0],ip->destIP[1],ip->destIP[2],ip->destIP[3]);if(ip->protocol==6){printf("tcp is used:\n");tcp=(tcp_hdr*)(packet_content+eth_len+ip_len);printf("tcp source port : %u\n",tcp->sport);printf("tcp dest port : %u\n",tcp->dport);}else if(ip->protocol==17){printf("udp is used:\n");udp=(udp_hdr*)(packet_content+eth_len+ip_len);printf("udp source port : %u\n",udp->sport);printf("udp dest port : %u\n",udp->dport);}else {printf("other transport protocol is used\n");}}else {printf("ipv6 is used\n");}printf("------------------done-------------------\n");printf("\n\n");
}int main(int argc, char *argv[])
{char *dev,errbuf[1024];dev=argv[1];if(dev==NULL){printf("device is null\n");return 0;}pcap_t *pcap_handle=pcap_open_live(dev,65535,1,0,errbuf);if(pcap_handle==NULL){printf("%s\n",errbuf);return 0;}struct in_addr addr;bpf_u_int32 ipaddress, ipmask;char *dev_ip,*dev_mask;if(pcap_lookupnet(dev,&ipaddress,&ipmask,errbuf)==-1){printf("%s\n",errbuf);return 0;}addr.s_addr=ipaddress;dev_ip=inet_ntoa(addr);printf("ip address : %s\n",dev_ip);addr.s_addr=ipmask;dev_mask=inet_ntoa(addr);printf("netmask : %s\n",dev_mask);struct bpf_program filter;if(pcap_compile(pcap_handle,&filter,"dst port 80",1,0)<0){printf("error\n");return 0;}if(pcap_setfilter(pcap_handle,&filter)<0){printf("error\n");return 0;}printf("---------packet--------\n");int id=0;pcap_dumper_t* dumpfp=pcap_dump_open(pcap_handle,"./save1.pcap");if(pcap_loop(pcap_handle,20,pcap_callback,(unsigned char *)dumpfp)<0){printf("error\n");return 0;}pcap_dump_close(dumpfp);pcap_close(pcap_handle);return 0;
}

结果:

这里写图片描述

参考:

https://www.cnblogs.com/danielStudy/p/7007689.html

http://www.tcpdump.org/pcap.html

另:我的Tenkey同学用python抓包,代码如下,可以学习一波,代码链接

#!/usr/bin/env python3
"""
Use DPKT to read in a pcap file and print out the contents of the packets
This example is focused on the fields in the Ethernet Frame and IP packet
Using Pypcap module to capture the packet and Using the Dpkt to read the packet
and save it to a .pcap file by timestamp or you can name it by yourself
by Tenkey 
"""
import pcap
import dpkt
import datetime
import socket
import os
from dpkt.compat import compat_ordfile_name_time = Nonedef mac_addr(address):"""Convert a MAC address to a readable/printable stringArgs:address (str): a MAC address in hex form (e.g. '\x01\x02\x03\x04\x05\x06')Returns:str: Printable/readable MAC address"""return ':'.join('%02x' % compat_ord(b) for b in address)def inet_to_str(inet):"""Convert inet object to a stringArgs:inet (inet struct): inet network addressReturns:str: Printable/readable IP address"""# First try ipv4 and then ipv6try:return socket.inet_ntop(socket.AF_INET, inet)except ValueError:return socket.inet_ntop(socket.AF_INET6, inet)def print_packets(pcap):"""Print out information about each packet in a pcapArgs:pcap: an pcap.pcap object (a network packet) """# For each packet in the pcap process the contentsglobal file_name_timewith open('unnamed.pcap', 'wb') as file:writer = dpkt.pcap.Writer(file)for timestamp, buf in pcap:if file_name_time == None:file_name_time = str(datetime.datetime.utcfromtimestamp(timestamp))writer.writepkt(buf, timestamp)# Print out the timestamp in UTCprint('Timestamp: ', str(datetime.datetime.utcfromtimestamp(timestamp)))# Unpack the Ethernet frame (mac src/dst, ethertype)eth = dpkt.ethernet.Ethernet(buf)print('Ethernet Frame: ', mac_addr(eth.src), mac_addr(eth.dst), eth.type)# Make sure the Ethernet data contains an IP packetif not isinstance(eth.data, dpkt.ip.IP):print('Non IP Packet type not supported %s\n' %eth.data.__class__.__name__)continue# Now unpack the data within the Ethernet frame (the IP packet)# Pulling out src, dst, length, fragment info, TTL, and Protocolip = eth.data# Pull out fragment information (flags and offset all packed into off field, so use bitmasks)do_not_fragment = bool(ip.off & dpkt.ip.IP_DF)more_fragments = bool(ip.off & dpkt.ip.IP_MF)fragment_offset = ip.off & dpkt.ip.IP_OFFMASK# Print out the infoprint('IP: %s -> %s   (len=%d ttl=%d DF=%d MF=%d offset=%d)' %(inet_to_str(ip.src), inet_to_str(ip.dst), ip.len, ip.ttl, do_not_fragment, more_fragments, fragment_offset))# Print out the detials in packetif isinstance(ip.data, dpkt.tcp.TCP):tcp = ip.dataprint('TCP: SrcPort[%d] -> DstPort[%d] Seq=%d Ack=%d Win=%d\n' %(tcp.sport, tcp.dport, tcp.seq, tcp.ack, tcp.win))elif isinstance(ip.data, dpkt.udp.UDP):udp = ip.dataprint('UDP: SrcPort[%d] -> DstPort[%d] Len=%d Check=%d\n' %(udp.sport, udp.dport, udp.ulen, udp.sum))elif isinstance(ip.data, dpkt.icmp.ICMP):print("ICMP: This is ICMP packet for checking error on route\n")else:print("Other Protocol: there may be other Protocols\n")def getPcap():nic = "ens33"fil = Noneans_nc = input("do you want to set filter for network card ? [y/n]\n")if ans_nc == "y":nic = input("typing in the network card name:\n")print("......setting filter for network card sucessfully......\n")ans_fil = input("do you want to set filter in th BPF(Berkeley Packet Filter) syntax ? [y/n]\n")if ans_fil == "y":fil = input("typing the BPF syntax Filter:\n")print("......setting filter in BPF syntax sucessfully......\n")sniffer = pcap.pcap(nic)if fil != None:sniffer.setfilter(fil)return snifferdef main():"""Using Pypcap(pcap) and DPKT(dpkt) modules to capture the network packetand unpack the packet to show the detials in every layer(this demo just show parts of them)and then save it into a .pcap file which can be opened by various open-source network capturing tools such as wireshark """sniffer = getPcap()print("-------------Start to Unpack-------------\n")try:print_packets(sniffer)except KeyboardInterrupt:print("-------------Unpack Ended-------------\n")nf = input("Do you want to name your capture file ? (or it will automatically named by time) [y/n]\n")if nf == "y":file_name_user = input("Just type the name: \n")os.rename('unnamed.pcap', file_name_user+'.pcap')else:os.rename('unnamed.pcap', file_name_time+'.pcap')print("File will automatically be named after time , Bye~\n")print("-------------Saving sucessfully-------------\n")if __name__ == '__main__':main()

注:以上所有操作均在作者在网上搜集资料后,在个人电脑上实验成功,若读者实验时失败,可能由一些未知因素导致,可与作者联系。编写的教程可能由于疏忽出错,请与作者联系。

这篇关于libpcap抓包并分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/500825

相关文章

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

Spring、Spring Boot、Spring Cloud 的区别与联系分析

《Spring、SpringBoot、SpringCloud的区别与联系分析》Spring、SpringBoot和SpringCloud是Java开发中常用的框架,分别针对企业级应用开发、快速开... 目录1. Spring 框架2. Spring Boot3. Spring Cloud总结1. Sprin

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析

《MyBatis-Plus中Service接口的lambdaUpdate用法及实例分析》本文将详细讲解MyBatis-Plus中的lambdaUpdate用法,并提供丰富的案例来帮助读者更好地理解和应... 目录深入探索MyBATis-Plus中Service接口的lambdaUpdate用法及示例案例背景