libpcap简明教程

文章目录

  • 前言
  • tcpdump的使用
  • libpcap API的简单使用
  • libpcap API的进阶使用

前言

因为之前简单写过wireshark入门指北,所以我知道基本的抓包流程。最近尝试调用libpcap的C API接口,顺道整理下。

本文实验如下:

  1. 使用tcpdump命令抓取目标地址为百度的数据包,并将结果保存文件。
  2. 使用tcpdump加载上面的数据包。
  3. 使用libpcap的API,实现上面的两个过程。

注:本文代码仅考虑在Linux环境下运行。


tcpdump的使用

如果是第一次使用tcpdump,会感觉这个命令的参数咋这么多,令人摸不着头脑。于是,每次需要写tcpdump命令,都要去问chatgpt,就像写正则一样。下面,我们将搞懂这个命令参数的结构,以后写tcpdump命令也会比较轻松。

我们去看它的官方文档,会看到tcpdump命令参数如下:

tcpdump [ -AbdDefhHIJKlLnNOpqStuUvxX# ] [ -B buffer_size ][ -c count ] [ --count ] [ -C file_size ][ -E spi@ipaddr algo:secret,... ][ -F file ] [ -G rotate_seconds ] [ -i interface ][ --immediate-mode ] [ -j tstamp_type ][ --lengths ] [ -m module ][ -M secret ] [ --number ] [ --print ][ --print-sampling nth ] [ -Q in|out|inout ] [ -r file ][ -s snaplen ] [ -T type ] [ --version ] [ -V file ][ -w file ] [ -W filecount ] [ -y datalinktype ][ -z postrotate-command ] [ -Z user ][ --time-stamp-precision=tstamp_precision ][ --micro ] [ --nano ][ expression ]

可以看到参数分为两部分。其中一部分是tcpdump的option。另一部分是expression。expression是一个pcap-filter。(这个expression是大名鼎鼎的BPF expression。如果不知道BPF,不影响。我之前接触过点bpf,所以概念上理解起来很轻松:bpf简介1)

这张图,可以很好的显示tcpdump命令的结构。图片来自:全网最详细的 tcpdump 使用指南。

在这里插入图片描述

  1. option:tcpdump命令的选项。
  2. expressio:proto、dir和type组成一个BPF完整表达式的基元。这些基元可以通过and,or,not来组合。

下面我们实际使用tcpdump命令试试。

# 抓包内容输出到标准输出。
## -nn:不把协议和端口号转化成名字; -vv:产生比-v更详细的输出; -i:指定网卡; 引号中的是BPF表达式,表抓取目标地址为百度的数据包
tcpdump -nn -vv -i enp0s3 "dst host www.baidu.com"# -c:控制抓包个数; -w:抓包内容写入文件
tcpdump -nn -vv -i enp0s3 -c 1 -w baidu.pcap "dst host www.baidu.com"# 读取包中的内容
tcpdump -r baidu.pcap

libpcap API的简单使用

先安装下这个库。

# ubuntu
sudo apt install libpcap-dev

然后,我们使用libpcap的API写demo,练练手。

tcpdump-libpcap中已经列出了多个教程。本节参考:

  1. PCAP(3PCAP) MAN PAGE:这个是官方的API文档。
  2. Using libpcap in C | DevDungeon:这个是一个很不错的libpcap API教程文档。内容循序渐进。美中不足的是,这篇文档是2015年写的,其中使用的一些API已经deprecate。

我这里滥竽充数的写一个libpcap的demo。这个demo展示了libpcap的使用骨架流程。结构性的了解了这些API的使用后,剩下的交给chatgpt即可。

下面是demo的流程:

  • 先使用pcap_create创建一个句柄
  • 使用pcap_set_timeout, 设置包缓冲区超时为1秒。避免等待过长时间:c - Why is there a long delay between pcap_loop() and getting a packet? - Stack Overflow。
  • 句柄上其他合适的选项,也是在pcap_create之后设置。
  • 然后调用pcap_activate激活句柄。
  • 使用pcap_compile将BPF的字符串表达式编译成伪机器码。
  • pcap_setfilter将生成的伪机器码制设置为句柄的过滤器。
  • pcap_loop读取数据包,并调用回调函数。
  • pcap_dump_*则将数据包保存到文件系统中。
  • pcap_open_offline则是加载PCAP格式的文件。后续的过滤显示等操作,同上。

下面是完整的代码。

#include <netinet/ether.h>
#include <netinet/ip.h>
#include <pcap.h>
#include <stdlib.h>
#include <string.h>#define DEVICE_NAME "enp0s3"
#define PACKAGE_CAPTURE_COUNT 2
#define PCAP_DUMP_FILE_PATH "./baidu.pcap"int enum_dev() {// 查看当前有哪些网络设备char errbuf[PCAP_ERRBUF_SIZE];pcap_if_t *alldevs, *dev;// 获取可用的网络设备列表if (pcap_findalldevs(&alldevs, errbuf) == -1) {fprintf(stderr, "Error in pcap_findalldevs: %s\n", errbuf);return -1;}int count = 1;struct sockaddr_in in;// 遍历设备列表并打印信息for (dev = alldevs; dev != NULL; dev = dev->next) {printf("Device %d: %s\n", count++, dev->name);if (dev->description) {printf("   Description: %s\n", dev->description);}for (pcap_addr_t *address = dev->addresses; address != NULL;address = address->next) {char addr_str[INET6_ADDRSTRLEN];const struct sockaddr *sa = address->addr;if (sa) {char addr_str[INET6_ADDRSTRLEN];if (sa->sa_family == AF_INET) {struct sockaddr_in *ipv4 = (struct sockaddr_in *)sa;inet_ntop(AF_INET, &(ipv4->sin_addr), addr_str, sizeof(addr_str));printf("  IPv4 Address: %s\n", addr_str);} else if (sa->sa_family == AF_INET6) {struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)sa;inet_ntop(AF_INET6, &(ipv6->sin6_addr), addr_str, sizeof(addr_str));printf("  IPv6 Address: %s\n", addr_str);}}}}// 释放设备列表pcap_freealldevs(alldevs);printf("\n\n");return 0;
}int print_device_net() {// 查看dev的 network number和子网掩码// ipv4变量中并非ip值,而是 network number// ref: https://github.com/the-tcpdump-group/libpcap/issues/159char errbuf[PCAP_ERRBUF_SIZE];int ret;bpf_u_int32 netp;bpf_u_int32 maskp;char ipv4[INET_ADDRSTRLEN];char ipv4_mask[INET_ADDRSTRLEN];ret = pcap_lookupnet(DEVICE_NAME, &netp, &maskp, errbuf);if (ret < 0) {fprintf(stderr, "fail in pcap_lookupnet: %s", errbuf);return -1;}inet_ntop(AF_INET, &netp, ipv4, sizeof(ipv4));inet_ntop(AF_INET, &maskp, ipv4_mask, sizeof(ipv4_mask));printf("%s network address is: %s\n", DEVICE_NAME, ipv4);printf("%s mask is: %s\n", DEVICE_NAME, ipv4_mask);printf("\n\n");return 0;
}void print_mac_address(const u_int8_t *mac_address, char *mac_string,unsigned int size) {snprintf(mac_string, size, "%02X:%02X:%02X:%02X:%02X:%02X", mac_address[0],mac_address[1], mac_address[2], mac_address[3], mac_address[4],mac_address[5]);
}void print_packet_info(struct pcap_pkthdr packet_header, const u_char *packet) {printf("Packet capture length: %d\n", packet_header.caplen);printf("Packet total length %d\n", packet_header.len);// todo: 从const u_char *packet中,逐层解析char mac[ETH_HLEN + 5 + 1];struct ether_header *eth_header = (struct ether_header *)packet;print_mac_address(eth_header->ether_shost, mac, sizeof(mac));printf("src mac addr is: %s\n", mac);print_mac_address(eth_header->ether_dhost, mac, sizeof(mac));printf("dst mac addr is: %s\n", mac);if (ntohs(eth_header->ether_type) == ETHERTYPE_IP) {struct iphdr *ip_header =(struct iphdr *)(packet + sizeof(struct ether_header));char ipv4[INET_ADDRSTRLEN];inet_ntop(AF_INET, &ip_header->saddr, ipv4, sizeof(ipv4));printf("src ip addr is: %s\n", ipv4);inet_ntop(AF_INET, &ip_header->daddr, ipv4, sizeof(ipv4));printf("dst ip addr is: %s\n", ipv4);}
}void capture_packet_handler(u_char *args,const struct pcap_pkthdr *packet_header,const u_char *packet_body) {print_packet_info(*packet_header, packet_body);pcap_dump(args, packet_header, packet_body);return;
}void capture() {// 捕获数据包,并保存到文件int ret;char errbuf[PCAP_ERRBUF_SIZE];pcap_t *pcap_handle;struct bpf_program filter;pcap_handle = pcap_create(DEVICE_NAME, errbuf);if (pcap_handle == NULL) {fprintf(stderr, "fail to execute pcap_create: %s", errbuf);exit(-1);}// https://stackoverflow.com/questions/29875110/why-is-there-a-long-delay-between-pcap-loop-and-getting-a-packetpcap_set_timeout(pcap_handle, 1000);ret = pcap_activate(pcap_handle);if (pcap_compile(pcap_handle, &filter, "dst host www.baidu.com", 0,PCAP_NETMASK_UNKNOWN) < 0) {fprintf(stderr, "fail to compile bpf rule: %s\n", pcap_geterr(pcap_handle));exit(-1);}if (ret < 0) {fprintf(stderr, "fail to active pcap hanlde: %s", pcap_geterr(pcap_handle));exit(-1);}if (pcap_setfilter(pcap_handle, &filter) < 0) {printf("Error setting filter - %s\n", pcap_geterr(pcap_handle));exit(-1);}pcap_dumper_t *pacp_dump_point =pcap_dump_open(pcap_handle, PCAP_DUMP_FILE_PATH);if (pacp_dump_point == NULL) {printf("fail to open dump file - %s\n", pcap_geterr(pcap_handle));exit(-1);}pcap_loop(pcap_handle, PACKAGE_CAPTURE_COUNT, capture_packet_handler,(u_char *)pacp_dump_point);pcap_dump_flush(pacp_dump_point);printf("\n\nsaved package to %s\n\n", PCAP_DUMP_FILE_PATH);pcap_dump_close(pacp_dump_point);pcap_close(pcap_handle);
}void load_packet_handler(u_char *args, const struct pcap_pkthdr *packet_header,const u_char *packet_body) {print_packet_info(*packet_header, packet_body);return;
}void load_pcap_file() {printf("load %s file.\n", PCAP_DUMP_FILE_PATH);char errbuf[PCAP_ERRBUF_SIZE];pcap_t *pcap_handle = pcap_open_offline(PCAP_DUMP_FILE_PATH, errbuf);if (pcap_handle == NULL) {fprintf(stderr, "fail to execute pcap_open_offline: %s", errbuf);exit(-1);}pcap_activate(pcap_handle);pcap_loop(pcap_handle, PACKAGE_CAPTURE_COUNT, load_packet_handler, NULL);
}int main(int argc, char *argv[]) {printf("current libpcap version: %s\n", pcap_lib_version());capture();load_pcap_file();
}

输出如下:

current libpcap version: libpcap version 1.9.1 (with TPACKET_V3)
Packet capture length: 98
Packet total length 98
src mac addr is: 08:00:27:84:19:39
dst mac addr is: 80:12:DF:8F:5B:F4
src ip addr is: 192.168.18.131
dst ip addr is: 110.242.68.4
Packet capture length: 98
Packet total length 98
src mac addr is: 08:00:27:84:19:39
dst mac addr is: 80:12:DF:8F:5B:F4
src ip addr is: 192.168.18.131
dst ip addr is: 110.242.68.4saved package to ./baidu.pcapload ./baidu.pcap file.
Packet capture length: 98
Packet total length 98
src mac addr is: 08:00:27:84:19:39
dst mac addr is: 80:12:DF:8F:5B:F4
src ip addr is: 192.168.18.131
dst ip addr is: 110.242.68.4
Packet capture length: 98
Packet total length 98
src mac addr is: 08:00:27:84:19:39
dst mac addr is: 80:12:DF:8F:5B:F4
src ip addr is: 192.168.18.131
dst ip addr is: 110.242.68.4

libpcap API的进阶使用

搞懂上面流程,然后根据实际需求编码即可。比如下面目标:

  • 判断一个BPF表达式是否匹配一个在内存中的数据帧。使用pcap_open_dead+pcap_offline_filter很容易做到。
  • C语言使用libpcap输出报文到pcap文件_pcap_dump_open-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/634923.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【漏洞复现】Hikvision SPON IP网络对讲广播系统命令执行漏洞(CVE-2023-6895)

文章目录 前言声明一、系统简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 Hikvision Intercom Broadcasting System是中国海康威视&#xff08;Hikvision&#xff09;公司的一个对讲广播系统。 声明 请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播…

[已解决]前端使用el-upload,后端使用文件上传阿里云报错:异常信息:java.lang.NullPointerException: null

前端使用el-upload&#xff0c;后端使用文件上传阿里云报错&#xff1a; 报错原因&#xff1a;前端image参数未传进去 解决方法&#xff1a;在el-upload添加属性 name"image" 文件传进去了&#xff01;

回溯法:0-1背包问题

问题描述 给定种物品和一背包。 物品的重量是&#xff0c; 其价值为&#xff0c;背包的容量为 c。 问应该如何选择装入背包中的物品&#xff0c;使得装入背包中物品的总价值最大&#xff1f;注意物品不重复! 实例&#xff1a;物品价值V{12, 11, 9, 8}, 物品重量W{8, 6, 4, 3},…

手写Vue3源码

Vue3核心源码 B站视频地址&#xff1a;https://www.bilibili.com/video/BV1nW4y147Pd?p2&vd_source36bacfbaa95ea7a433650dab3f7fa0ae Monorepo介绍 Monorepo 是管理项目代码的一种方式&#xff0c;只在一个仓库中管理多个模块/包 一个仓库可以维护多个模块&#xff0c;…

Fog-Aware Adaptive YOLO for Object Detection in Adverse Weather

Abstract 提出了一种雾自适应YOLO算法。使用一种雾评估算法将图片分为有雾和无雾图片&#xff0c;随后将标准的YOLO应用于正常图片&#xff0c;自适应YOLO应用于有雾图片。 Method 目前的除雾方法去除雾霾时不考虑其雾度和发生频率。例如&#xff0c;图像自适应YOLO算法[6]试…

【已解决】Qt Creator设计模式被禁用不能点的原因及解决方案

Qt Creator 下载地址&#xff08;含历史版本&#xff09;&#xff1a;https://download.qt.io/official_releases/qtcreator/ 症状 Qt Creator 目前最新版为12.0.1&#xff0c;安装后打开.qml文件发现设计工具图标为禁用状态。 原因及解决方案 根据官网材料&#xff08;Qt C…

机器视觉检测设备在连接器外观缺陷检测中的应用

作为传输电流或信号连接两个有源器件的器件&#xff0c;连接器被广泛应用于各个行业&#xff0c;从手机、平板、电脑&#xff0c;到冰箱、空调、洗衣机&#xff0c;再到汽车、国防、航空&#xff0c;处处是它的所在。每个电子产品少了连接器将无法运作&#xff0c;因此&#xf…

Python——面向对象案列

1. class User(object):#重写__int__初始化方法&#xff0c;该方法用来初始化属&#xff0c;在构建方法的时候&#xff0c;这个方法会自动触发&#xff0c;用来初始化属性def __init__(self): #name “” 这是一个局部变量#self类似指针 this#self.name类的属性print("构…

计算机网络-甘晴void学习笔记

计算机网络 计科210X 甘晴void 202108010XXX 文章目录 计算机网络期中复习1计算机网络和因特网1.1 因特网1.2 网络边缘1.3 网络核心1.4 分组交换的时延/丢包和吞吐量1.5 协议层次与服务模型 2 应用层原理2.1 应用层协议原理2.2 Web和Http2.3 因特网中的电子邮件2.4 DNS&#x…

K8S--部署Nacos

原文网址&#xff1a;K8S--部署Nacos-CSDN博客 简介 本文介绍K8S部署Nacos的方法。Nacos版本是&#xff1a;2.2.3。 部署方案 本文为了简单&#xff0c;使用此部署方式&#xff1a;使用本地pvconfigmap&#xff0c;以embedded模式部署单机nacos。以nodePort方式暴露端口。 …

vectorCast——Probe point 功能实现故障注入,局部变量打印,断点调试。

选择一个测试用例,选择coverage窗口进行查看。点击edit probe point,如图所示绿色的小圆圈。选代码中选择需要打断点的地方进行点击。黑色的小圆点都可以选。点击黑色小圆点,小圆点变绿,表示打断点成功。此时就可以根据自己的需求在打断点的位置编写一些C语言的命令语句。点…

线程同步--生产者消费者模型--单例模式线程池

文章目录 一.条件变量pthread线程库提供的条件变量操作 二.生产者消费者模型生产者消费者模型的高效性基于环形队列实现生产者消费者模型中的数据容器基于生产者消费者模型实现单例线程池 一.条件变量 条件变量是线程间共享的全局变量,线程间可以通过条件变量进行同步控制条件…

ffmpeg 常用命令行详解

概述 ffmpeg 是一个命令行音视频后期处理软件 1. 裁剪命令 参数说明 -i 文件&#xff0c;orgin.mp3 为待处理源文件-ss 裁剪时间&#xff0c;后跟裁剪开始时间&#xff0c;或者开始的秒数-t 裁剪时间output.mp3 为处理结果文件 ffmpeg -i organ.mp3 -ss 00:00:xx -t 120 o…

无刷电机学习-原理篇

一、无刷电机的优点 使用一项东西首先就要明白为什么要使用它&#xff0c;使用它有什么优点。与有刷电机相比无刷电机除了控制繁琐几乎全是优点。 1、应用范围广&#xff1a;家用电器&#xff08;冰箱空调压缩机、洗衣机、水泵等&#xff09;、汽车、航空航天、消费品工业自动…

4个值得使用的免费爬虫工具

在信息时代&#xff0c;数据的获取对于各行业都至关重要。而在数据采集的众多工具中&#xff0c;免费的爬虫软件成为许多用户的首选。本文将专心分享四款免费爬虫工具&#xff0c;突出介绍其中之一——147采集软件&#xff0c;为您揭示这些工具的优势和应用&#xff0c;助您在数…

【c语言】扫雷(上)

先开一个test.c文件用来游戏的逻辑测试&#xff0c;在分别开一个game.c文件和game.h头文件用来实现游戏的逻辑 主要步骤&#xff1a; 游戏规则&#xff1a; 输入1&#xff08;0&#xff09;开始&#xff08;结束&#xff09;游戏&#xff0c;输入一个坐标&#xff0c;如果该坐…

nodejs前端项目的CI/CD实现(二)jenkins的容器化部署

一、背景 docker安装jenkins&#xff0c;可能你会反问&#xff0c;这太简单了&#xff0c;有什么好讲的。 我最近就接手了一个打包项目&#xff0c;它是一个nodejs的前端项目&#xff0c;jenkins已在容器里部署且运行OK。 但是&#xff0c;前端组很追求新技术&#xff0c;不…

中小企业股权质押融资(下)

股权质押融资的主要风险 由于股权资产的特殊性&#xff0c;较固定资产抵押和质押、第三方担保等方式&#xff0c;股权质押融资风险易受企业经营状况等因素的影响&#xff0c;主要包括股权价值下跌的风险、股权质押的道德风险、股权处置风险以及现行法律不完善导致的法律风险。…

前端面试题汇总大全(含答案)-- 持续更新

​一、HTML 篇 1. 简述一下你对 HTML 语义化的理解&#xff1f; 用正确的标签做正确的事情。 html 语义化让页面的内容结构化&#xff0c;结构更清晰&#xff0c;便于对浏览器、搜索引擎解析&#xff1b;即使在没有样式 CSS 情况下也以一种文档格式显示&#xff0c;并且是容易…

学习Spring的第八天

先对自定义类使用MyComponet的注解&#xff0c;在设置这个MyComponet的的属性(一个 interface接口)&#xff0c;然后&#xff0c;扫描(BaseClassScanUtils.java执行,这文件不重要)当前包下是否有这个注解的类&#xff0c;再用MyComponentBeanFactoryPostProcessor.java(后工厂…