libpcap介绍(一)

本文详细介绍了libpcap,一个开源的C函数库,用于网络报文捕获和分析。涵盖了其基本功能、网络报文构建、工作原理、常用函数以及过滤机制,展示了libpcap在网络嗅探中的应用和工作流程。
摘要由CSDN通过智能技术生成

一、libpcap的简介

Libpcap是Packet Capture Libray的英文缩写,即数据包捕获开源的C函数库,用于捕获网卡数据或分析pcap格式的抓包报文。Tcpdump和wireshark均是以此为基础的。

主要功能有:网络报文抓取;网络报文的构建;抓包文件的分析;自定义BFP过滤。

二、网络报文

网络报文是按照协议层次逐层构建的,简单按照四层协议来理解就是在应用层数据和协议的基础上构建传输层报文,在传输层协议的基础上构建网络层报文,在网络层报文的基础上构建数据链路/物理层报文。

图1.网络分层图
在这里插入图片描述

三、应用说明

libpcap主要用于网络嗅探,如下图:
在这里插入图片描述
基于libpcap的应用一般处理过程:
在这里插入图片描述

四、libpcap工作原理

Libpcap 主要由两部份组成:网络分接头(Network Tap)和数据过滤器(Packet Filter)。网络分接头从网络设备驱动程序中收集数据拷贝,过滤器决定是否接收该数据包。Libpcap利用BSD Packet Filter(BPF)算法对网卡接收到的链路层数据包进行过滤。BPF算法的基本思想是在有BPF监听的网络中,网卡驱动将接收到的数据包复制一份交给 BPF过滤器,过滤器根据用户定义的规则决定是否接收此数据包以及需要拷贝该数据包的那些内容,然后将过滤后的数据给与过滤器相关联的上层应用程序。BPF的架构如图所示:
在这里插入图片描述
Libpcap 的包捕获机制就是在数据链路层加一个旁路处理。当一个数据包到达网络接口时,Libpcap 首先利用已经创建的Socket从链路层驱动程序中获得该数据包的拷贝,再通过Tap函数将数据包发给BPF过滤器。BPF过滤器根据用户已经定义好的过 滤规则对数据包进行逐一匹配,匹配成功则放入内核缓冲区,并传递给用户缓冲区,匹配失败则直接丢弃。如果没有设置过滤规则,所有数据包都将放入内核缓冲 区,并传递给用户层缓冲区。

五、常用函数说明

调用 libpcap 库函数前要包含的头文件:

#include <pcap/pcap.h>
  • 1

调用 libpcap 库抓包的流程:

  1. 查找网卡,目的是发现可用的网卡,实现的函数为 pcap_lookupdev() 。
  2. 获得网卡参数,这里是利用 pcap_lookupnet() 函数,获得指定网卡的 IP 地址和子网掩码。
  3. 打开网卡,利用第一步的返回值,决定使用哪个网卡,调用pcap_open_live() 将其打开。
  4. 编译过滤策略,Lipcap 的重要功能就是提供数据包的过滤,实现的函数是pcap_compile() 。
  5. 设置过滤器,调用 pcap_setfilter() 函数将编译好的过滤策略设置到相应网卡。
  6. 开始捕获数据包,有多种实现函数,具有不同的特性。
  7. 关闭网卡,释放资源。

如果是用源码包编译安装的话,在 tests 目录下有几个例程可以参考。

1、获取网络接口

首先我们需要获取监听的网络接口,我们可以手动指定或让libpcap自动选择,先介绍如何让libpcap自动选择:

//这个函数返回第一个合适的网络接口的字符串指针,如果出错,则errbuf存放出错信息字符串,errbuf至少应该是PCAP_ERRBUF_SIZE个字节长度的,注意,很多libpcap函数都有这个参数。
//pcap_lookupdev()一般可以在跨平台的,且各个平台上的网络接口名称都不相同的情况下使用。如果我们手动指定要监听的网络接口,则这一步跳过,我们在第二步中将要监听的网络接口字符串硬编码在pcap_open_live里。
char * pcap_lookupdev(char * errbuf)
  • 1
  • 2
  • 3

2、释放网络接口

void pcap_close(pcap_t * p)
//该函数用于关闭pcap_open_live()获取的pcap_t的网络接口对象并释放相关资源。
  • 1
  • 2

3、打开网络接口

//这个函数会返回指定接口的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: 输出参数,打开网络接口失败原因。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4、获取数据包

打开网络接口后就已经开始监听了,那如何知道收到了数据包呢?有下面3种方法:

1)pcap_next
u_char * pcap_next(pcap_t * p, struct pcap_pkthdr * h)
//如果返回值为NULL,表示没有抓到包
//第一个参数是第2步返回的pcap_t类型的指针
//第二个参数是保存收到的第一个数据包的pcap_pkthdr类型的指针
//pcap_pkthdr类型的定义如下:
struct pcap_pkthdr
{
  struct timeval ts;    /* time stamp */
  bpf_u_int32 caplen;   /* length of portion present */
  bpf_u_int32 len;      /* length this packet (off wire) */
};
//注意这个函数只要收到一个数据包后就会立即返回
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
2)pcap_loop
int pcap_loop(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
//第一个参数是第2步返回的pcap_t类型的指针
//第二个参数是需要抓的数据包的个数,一旦抓到了cnt个数据包,pcap_loop立即返回。负数的cnt表示pcap_loop永远循环抓包,直到出现错误。
//第三个参数是一个回调函数指针,它必须是如下的形式:
void callback(u_char * userarg, const struct pcap_pkthdr * pkthdr, const u_char * packet)
//第一个参数是pcap_loop的最后一个参数,当收到足够数量的包后pcap_loop会调用callback回调函数,同时将pcap_loop()的user参数传递给它
//第二个参数是收到的数据包的pcap_pkthdr类型的指针
//第三个参数是收到的数据包数据
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
3)pcap_dispatch
int pcap_dispatch(pcap_t * p, int cnt, pcap_handler callback, u_char * user)
//这个函数和pcap_loop()非常类似,只是在超过to_ms毫秒后就会返回(to_ms是pcap_open_live()的第4个参数)
  • 1
  • 2

5、分析数据包

我们既然已经抓到数据包了,那么我们要开始分析了,这部分留给读者自己完成,具体内容可以参考相关的网络协议说明。在本文的最后,我会示范性的写一个分析arp协议的sniffer,仅供参考。要特别注意一点,网络上的数据是网络字节顺序的,因此分析前需要转换为主机字节顺序(ntohs()函数)。

6、过滤数据包

我们抓到的数据包往往很多,如何过滤掉我们不感兴趣的数据包呢?
几乎所有的操作系统(BSD, AIX, Mac OS, Linux等)都会在内核中提供过滤数据包的方法,主要都是基于BSD Packet Filter(BPF)结构的。libpcap利用BPF来过滤数据包。
过滤数据包需要完成3件事:

  1. 构造一个过滤表达式
  2. 编译这个表达式
  3. 应用这个过滤器
1)构造一个过滤表达式

BPF使用一种类似于汇编语言的语法书写过滤表达式,不过libpcap和tcpdump都把它封装成更高级且更容易的语法了,具体可以man tcpdump,以下是一些例子:

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个字节)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
2)编译这个表达式

构造完过滤表达式后,我们需要编译它,使用如下函数:

int pcap_compile(pcap_t * p, struct bpf_program * fp, char * str, int optimize, bpf_u_int32 netmask)
//fp:这是一个传出参数,存放编译后的bpf
//str:过滤表达式
//optimize:是否需要优化过滤表达式
//metmask:简单设置为0即可
  • 1
  • 2
  • 3
  • 4
  • 5
3)应用这个过滤器

最后我们需要应用这个过滤表达式:

int pcap_setfilter(pcap_t * p,  struct bpf_program * fp)
//第二个参数fp就是前一步pcap_compile()的第二个参数
  • 1
  • 2

应用完过滤表达式之后我们便可以使用pcap_loop()或pcap_next()等抓包函数来抓包了。

7、打开离线的pcap文件

pcap_t *pcap_open_offline(const char *fname, char *errbuf)
//fname :文件名称。
//errbuf :打开失败的错误信息。
  • 1
  • 2
  • 3

8、打开网络包保存文件

pcap_dumper_t *pcap_dump_open(pcap_t *p, const char *fname)
//p:是我们已经打开的网络设备,从这个设备接收数据包。
//fname:是我们要写入的文件名,随便起。
//return: 如果出错,会返回NULL。可以借此检查这个文件有没有打开。
  • 1
  • 2
  • 3
  • 4

9、将网络包写入文件

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
  • 1
  • 2
  • 3
  • 4

10、网络包文件关闭

pcap_dump_close(pcap_dumper_t * t);
  • 1

数据结构说明:

struct pcap_pkthdr {
    struct timeval ts;  /* time stamp */
    bpf_u_int32 caplen; /* 抓到的数据包实际长度 */
    bpf_u_int32 len;    /*数据包的长度 */
};
  • 1
  • 2
  • 3
  • 4
  • 5

原文链接:
libcap介绍 - 简书
libpcap使用_修改pcap数据包的mac地址-CSDN博客


Libpcap 学习笔记 · Shaocheng.Li

libpcap介绍(一)-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
libpcap是一个用于在Unix和Linux系统上捕获网络数据包的库。它是网络包分析工具Wireshark和tcpdump所使用的底层库之一。下面简单介绍一下libpcap的源码结构和实现原理。 1. 源码结构 libpcap的源码结构主要分为以下几个部分: - include目录:包含libpcap的头文件。 - libpcap目录:libpcap的核心代码,包括主要的函数和数据结构定义,以及一些实现文件。 - pcap-linux.c:Linux系统下的实现文件,包括对网络接口的抓包、过滤规则的实现等。 - pcap-bpf.c:BPF虚拟机的实现文件,用于对网络数据包进行过滤。BPF虚拟机是一种基于RISC指令集的虚拟机,可以在内核中编译和执行BPF程序。BPF程序可以用于在内核中过滤和修改网络数据包。 - pcap-dbus.c:DBus接口的实现文件,用于通过DBus接口与其他应用程序通信。 2. 实现原理 libpcap的实现原理主要分为以下几个步骤: - 打开网络接口:调用pcap_open_live()函数打开指定的网络接口。 - 设置过滤规则:调用pcap_compile()函数编译用户指定的过滤规则,并将其传递给内核。 - 抓包:使用pcap_loop()函数或pcap_next()函数从网络接口中抓取数据包。 - 过滤数据包:使用BPF虚拟机对抓到的数据包进行过滤。 - 分析数据包:根据协议类型和格式对数据包进行解析,并提取出需要的信息。 总体来说,libpcap的实现原理比较简单,主要是通过对网络接口的控制和对BPF虚拟机的使用来实现对网络数据包的捕获和分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值