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.hqwc.cn/news/415922.html

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

相关文章

RabbitMQ交换机(2)-Direct

1.Direct 直连(路由)交换机,生产者将消息发送到交换机&#xff0c;并指定消息的Routing Key&#xff08;路由键&#xff09;。交换机会将Routing Key与队列绑定进行匹配&#xff0c;如果匹配成功&#xff0c;则将该消息路由到对应的队列中。如果没有匹配成功&#xff0c;该消息…

Unity XR 设置VR设备手柄按键按下事件

一、Unity设置 1、导入XR Interaction Toolkit插件&#xff0c;导入示例资源&#xff08;如下图&#xff09;。 2、设置新版XR输入事件 ①打开XRI Default Input Action 面板。 ②设置左手柄上的按键就点击Action Maps 列表下的 XRI LeftHand Interaction选项&#xff0c;设置…

机器学习:何为监督学习和无监督学习

目录 一、监督学习 &#xff08;一&#xff09;回归 &#xff08;二&#xff09;分类 二、无监督学习 聚类 一、监督学习 介绍&#xff1a;监督学习是指学习输入到输出&#xff08;x->y&#xff09;映射的机器学习算法&#xff0c;监督即理解为&#xff1a;已知正确答案…

Jmter提取返回结果中的数据以及跨线程组使用数据 jmter提取请求返回结果在其他线程调用

Jmter提取返回结果中的数据以及跨线程组使用数据 jmter提取请求返回结果在其他线程调用 1、示例要提取的接口2、跨线程组调用 1、示例要提取的接口 假设提取 登录接口请求结果数据中的 token 用于其他线程使用&#xff0c;登录接口返回数据格式 {"code": 0,"m…

知识库工具不知道选什么?HelpLook和Intercom就很不错啊

随着数字化转型的加速&#xff0c;在线帮助中心和知识库工具已成为企业提供优质客户服务的关键。在这篇文章中&#xff0c;我们将深入探讨两个广泛使用的知识库工具——HelpLook和Intercom&#xff0c;通过比较它们的特性和功能&#xff0c;帮助你做出最合适的选择。 | 一、Hel…

CentOS 7.9 安装图解

特特特别的说明 CentOS发行版已经不再适合应用于生产环境&#xff0c;客观条件不得不用的话&#xff0c;优选7.9版本&#xff0c;8.5版本次之&#xff0c;最次6.10版本&#xff08;比如说Oracle 11GR2就建议在6版本上部署&#xff09;&#xff01; 引导和开始安装 选择倒计时结…

ChatGPT给出的前端面试考点(Vue.js)

ChatGPT给出的前端面试考点&#xff08;Vue.js&#xff09; 答案 1. Vue.js是什么&#xff1f;它的主要特点是什么&#xff1f; Vue.js是一个渐进式JavaScript框架&#xff0c;用于构建用户界面。它的主要特点包括&#xff1a; 数据绑定&#xff1a;Vue.js使用双向数据绑定&…

Linux 为何不把图形用户界面写入内核?

Linux 为何不把图形用户界面写入内核&#xff1f; 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「Linux的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#…

如何利用小程序介绍公司品牌形象?

企业小程序的建设对于现代企业来说已经成为了一项必不可少的工作。随着移动互联网的快速发展&#xff0c;越来越多的职场人士和创业老板希望通过小程序来提升企业形象&#xff0c;增强与用户的互动&#xff0c;实现更好的商业效果。在这个过程中&#xff0c;使用第三方制作平台…

如何利用在线网络靶场将安全提升至新水平

在 Standoff 365 的在线网络靶场中&#xff0c;任何公司都可以试验信息安全手段和企业网络设置&#xff0c;优化攻击检测、响应和事件调查的技能。 2023 年&#xff0c;我们不仅准许攻击者使用&#xff0c;也准许防御者使用。我们可以根据客户要求轻松部署 10 个细分行业中的任…

数字前端/FPGA设计——握手与反压问题

声明&#xff1a;本文来自0431大小回 前言&#xff1a;在芯片设计或者FPGA设计过程中&#xff0c;流水设计是经常用到的&#xff0c;但是考虑数据安全性&#xff0c;需要与前后级模块进行握手通信&#xff0c;这时候就需要对流水数据进行反压处理&#xff0c;本文将具体介绍握手…

C++大学教程(第九版)5.25去除break语句 5.27去除cintinue语句

5.25题目 (去除break和continue)break和continue 语句遭到质疑的原因是它们的非结构化性。实际上,break和continue 语句总能用结构化的语句取代。请详述如何从程序的一条循环语中去除break语句&#xff0c;并用某种结构化的手段替代。提示:break 语句用于在循环体内离开一个循…