一、
在 C 语言中实现 TCP 抓包功能,通常可以使用 libpcap 库。libpcap 是一个广泛使用的网络抓包库,它提供了捕获网络数据包的接口。
libpcap 是一个广泛使用的 C 语言库,用于捕获和过滤网络数据包。它提供了一个通用接口,用于访问数据链路层的协议,使用户能够在各种平台上实现网络流量捕获功能。
二、准备
安装 libpcap:在 Linux 系统中,可以使用以下命令安装
sudo apt-get install libpcap-dev
或者
下载、编译源码
git clone https://github.com/the-tcpdump-group/libpcap
可以查看官方文档
https://www.tcpdump.org/
三、代码
#include <stdio.h>
#include <stdlib.h>
#include <pcap/pcap.h>//头文件包含
#define SNAP_LEN 65535
#define PROMISC 1
#define TIMEOUT 1000 // ms
#define BUFSIZE 4 * 1024 * 1024 // 4MB
typedef struct {
pcap_dumper_t *dumper; // 用于保存数据包的句柄
time_t start_time; // 记录当前文件的起始时间
long written_bytes; // 当前文件已写入的字节数
int file_index; // 文件索引,用于防止同一秒生成两个文件
} capture_context_t;
//抓到包后具体的处理,根据带入的参数和自己需要的规则进行编写功能
void packet_handler(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes) {
capture_context_t *ctx = (capture_context_t *)user;
time_t now = time(NULL);
long packet_size = h->caplen;
// 更新写入字节数
ctx->written_bytes += packet_size;
// 计算是否需要切换文件
if (difftime(now, ctx->start_time) >= MAX_DURATION_SEC ||
ctx->written_bytes >= MAX_FILE_SIZE) {
// 关闭旧文件
pcap_dump_close(ctx->dumper);
// 打开新文件
char filename[256];
ctx->file_index += 1;
generate_filename(filename, sizeof(filename), ctx->file_index);
ctx->dumper = pcap_dump_open(ctx->handle, filename);
ctx->start_time = now;
ctx->written_bytes = 0;
printf("🆕 Switched to new file: %s\n", filename);
}
// 写入当前包
pcap_dump((u_char *)ctx->dumper, h, bytes);
}
int main() {
char errbuf[PCAP_ERRBUF_SIZE];//存放错误打印
pcap_t *handle;
pcap_dumper_t *dumper;
struct bpf_program fp;
bpf_u_int32 net = 0, mask = 0;
//查找当前系统中一个默认的抓包网络设备名,需要确认是否有权限
const char *dev = pcap_lookupdev(errbuf);
if (!dev) {
fprintf(stderr, "Device not found: %s\n", errbuf);
return 1;
}
const char *filter_exp = "tcp port 80"; // 设置过滤器的规则,可以换成 "udp", "ip", "host 192.168.1.1", 等等
const char *outfile = "output.pcap"; // 输出文件
printf("Using device: %s\n", dev);
// 获取网络地址 & 掩码(用于过滤器编译)
if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
fprintf(stderr, " Could not get netmask for %s: %s\n", dev, errbuf);
net = 0;
mask = 0;
}
//创建并配置抓包句柄
handle = pcap_create(dev, errbuf);
if (!handle) {
fprintf(stderr, " pcap_create failed: %s\n", errbuf);
return 1;
}
//设置抓包长度
pcap_set_snaplen(handle, SNAP_LEN);
//混杂模式,当网络接口处于混杂模式时,它会接收所有经过的数据包,而不仅仅是发送给它的包。
pcap_set_promisc(handle, PROMISC);
//设置捕获数据包的超时时间,以毫秒为单位,表示在没有接收到数据包的情况下,等待的最长时间
pcap_set_timeout(handle, TIMEOUT);
//设置缓冲区大小,增大缓冲区可以提高捕获性能,尤其是在高流量环境中,可以防止数据包丢失。
pcap_set_buffer_size(handle, BUFSIZE);
//激活刚刚设置的参数
if (pcap_activate(handle) != 0) {
fprintf(stderr, " pcap_activate failed: %s\n", pcap_geterr(handle));
return 1;
}
//编译 BPF(Berkeley Packet Filter)过滤器表达式,将过滤规则转化为库格式
if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
fprintf(stderr, " Couldn't parse filter: %s\n", pcap_geterr(handle));
return 1;
}
//应用过滤规则
if (pcap_setfilter(handle, &fp) == -1) {
fprintf(stderr, " Couldn't install filter: %s\n", pcap_geterr(handle));
return 1;
}
// 打开输出文件
dumper = pcap_dump_open(handle, outfile);
if (!dumper) {
fprintf(stderr, " Couldn't open dump file: %s\n", pcap_geterr(handle));
return 1;
}
capture_context_t ctx = { .dumper = dumper };
printf(" Saving packets to: %s\n", outfile);
//抓多少包,抓到的包怎么处理packet_handler
pcap_loop(handle, 10, packet_handler, (u_char *)&ctx); // 抓 10 个包
// 清理资源
pcap_dump_close(dumper);
pcap_freecode(&fp);
pcap_close(handle);
printf(" Capture complete. Output saved to: %s\n", outfile);
return 0;
}
编译、需要进行库的链接,以及操作权限
gcc -o pcap_loop_example pcap_loop_example.c -lpcap
sudo ./pcap_loop_example
#四、Shell 脚本(自动添加权限)
可以当做自启动脚本
#!/bin/bash
# file: grant_pcap_cap.sh
TARGET="./pcap_loop_example "
if [ ! -f "$TARGET" ]; then
echo "❌ $TARGET not found. Please compile it first."
exit 1
fi
echo "🔧 Adding capability to $TARGET ..."
sudo setcap cap_net_raw,cap_net_admin=eip $TARGET
echo "✅ Done. You can now run it as a normal user:"
echo " $TARGET"