raw socket 使用 BPF 过滤报文
抓包
使用 raw socket 进行网络底层抓包,想必大家都清楚(仔细想想tcpdump原理)。这里不赘述,网上许多资料。
注意,网卡需要开启混杂模式、其次程序运行需要root权限。
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>unsigned char buffer[102];
void main()
{int fd, n, i;fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if (fd < 0 ) {perror("socket fail\n");exit(1);}/*block*/n = recvfrom(fd, buffer, sizeof(buffer), 0, NULL, NULL);if (n < 0) {perror("recvfrom none\n");exit(1);}for (i = 0; i < n ;i++) {printf("%02x ", buffer[i]);}
}
希望能够过滤
法1:抓所有的包,在用户态recvfrom后,自己使用代码逻辑进行过滤。
法2:告诉底层 raw socket, 哪些包是自己想要的,直接在底层被过滤掉。
法1,行得通,但是性能肯定扛不住,所有的包需要从内核拷贝至用户态,这个消耗巨大。
法2,即使用bpf。
首先,获得 过滤规则 对应的的bpf code。比如,我想过滤端口为 9090 的tcp报文,使用tcpdump的-dd
参数,就能获得。
$sudo tcpdump -dd -i eth0 tcp port 9090 -s 0
{ 0x28, 0, 0, 0x0000000c },
{ 0x15, 0, 6, 0x000086dd },
{ 0x30, 0, 0, 0x00000014 },
{ 0x15, 0, 15, 0x00000006 },
{ 0x28, 0, 0, 0x00000036 },
{ 0x15, 12, 0, 0x00002382 },
{ 0x28, 0, 0, 0x00000038 },
{ 0x15, 10, 11, 0x00002382 },
{ 0x15, 0, 10, 0x00000800 },
{ 0x30, 0, 0, 0x00000017 },
{ 0x15, 0, 8, 0x00000006 },
{ 0x28, 0, 0, 0x00000014 },
{ 0x45, 6, 0, 0x00001fff },
{ 0xb1, 0, 0, 0x0000000e },
{ 0x48, 0, 0, 0x0000000e },
{ 0x15, 2, 0, 0x00002382 },
{ 0x48, 0, 0, 0x00000010 },
{ 0x15, 0, 1, 0x00002382 },
{ 0x6, 0, 0, 0x00040000 },
{ 0x6, 0, 0, 0x00000000 },
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <net/if_arp.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>#include <linux/filter.h>unsigned char buffer[102];
void main()
{int fd, n, i;struct sock_fprog filter;struct sock_filter code[] = {{ 0x28, 0, 0, 0x0000000c },{ 0x15, 0, 6, 0x000086dd },{ 0x30, 0, 0, 0x00000014 },{ 0x15, 0, 15, 0x00000006 },{ 0x28, 0, 0, 0x00000036 },{ 0x15, 12, 0, 0x00002382 },{ 0x28, 0, 0, 0x00000038 },{ 0x15, 10, 11, 0x00002382 },{ 0x15, 0, 10, 0x00000800 },{ 0x30, 0, 0, 0x00000017 },{ 0x15, 0, 8, 0x00000006 },{ 0x28, 0, 0, 0x00000014 },{ 0x45, 6, 0, 0x00001fff },{ 0xb1, 0, 0, 0x0000000e },{ 0x48, 0, 0, 0x0000000e },{ 0x15, 2, 0, 0x00002382 },{ 0x48, 0, 0, 0x00000010 },{ 0x15, 0, 1, 0x00002382 },{ 0x6, 0, 0, 0x00040000 },{ 0x6, 0, 0, 0x00000000 },};filter.len = sizeof(code)/sizeof(struct sock_filter); filter.filter = code;fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));if (fd < 0 ) {perror("socket fail\n");exit(1);}//设置 sk_filterif (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0) {perror("setsockopt fail\n"); exit(1); }/*block*/n = recvfrom(fd, buffer, sizeof(buffer), 0, NULL, NULL);if (n < 0) {perror("recvfrom none\n");exit(1);}for (i = 0; i < n ;i++) {printf("%02x ", buffer[i]);}
}
此时运行程序,只有9090端口的tcp报文到达本机,recvfrom
才会返回。
原理
static int packet_rcv(struct sk_buff *skb, struct net_device *dev,struct packet_type *pt, struct net_device *orig_dev)
{........res = run_filter(skb, sk, snaplen);if (!res)goto drop_n_restore;
在包被raw socket处理时,会执行run_filter
,而这过滤规则正是 上文 tcpdump
输出的code,这些code由bpf解释程序运行。
实际上,可议使用tcpdump -d
查看具体code的行为,如下,类似汇编语言,相对而言可读性高一点了(当然前提你需要熟悉二三层协议)。
$sudo tcpdump -d -i eth0 tcp port 9090 -s 0
(000) ldh [12] #加载12字节后的自己,即偏去6字节的smac 和 dmac
(001) jeq #0x86dd jt 2 jf 8 # 如果 网络层类型不是0x86dd(IPV6),则跳转到 8
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 19
(004) ldh [54]
(005) jeq #0x2382 jt 18 jf 6
(006) ldh [56]
(007) jeq #0x2382 jt 18 jf 19
(008) jeq #0x800 jt 9 jf 19 #如果 网络层类型不是0x0800(IPV4),则跳转到 19,不处理
(009) ldb [23] #ip头中的protocol字段
(010) jeq #0x6 jt 11 jf 19 #不是6表示不是tcp,不处理
(011) ldh [20]
(012) jset #0x1fff jt 19 jf 13 #检测是否是ip分片,是的就不处理
(013) ldxb 4*([14]&0xf) #ip头中的length字段,偏过这个长度,就是tcp头
(014) ldh [x + 14]
(015) jeq #0x2382 jt 18 jf 16 #0x2382 是9090.匹配sport是否是9090
(016) ldh [x + 16]
(017) jeq #0x2382 jt 18 jf 19 #0x2382 是9090.匹配dport是否是9090
(018) ret #262144
(019) ret #0
raw socket 使用 BPF 过滤报文相关推荐
- BPF高阶 - 使用BPF过滤固定特征报文
这篇文章主要介绍如何使用BPF过滤固定特征报文 参考文章:https://www.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&manpath ...
- Raw Socket编程
原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首 先来说,普通的套接字无法处理ICMP.IGMP等网络报 ...
- linux raw socket 例子,raw socket编程例子
raw socket编程例子内容安排: 1.原始套接字介绍 1.1 原始套接字工作原理与规则 1.2 简单应用 2 FTP密码窃取器实现(简单的rootkit) 2.1 设计思路 2.2 实现 2.3 ...
- Linux小知识--原始套接字(raw socket)之模拟ping
原始套接字-raw socket 最近在研究高并发下扫描存活主机,基本想法是通过socket来模拟ICMP报文,然后就发现了socket的一片新天地----原始套接字(raw socket). raw ...
- 基于原始套接字(raw socket)的网络抓包工具
基于raw socket的网络抓包工具 1. 原始套接字(raw socket)简介 原始套接字可以接收本机网卡上的数据帧或者数据包,利用raw socket可以编写基于IP协议的程序.一般的TCP/ ...
- python实现syn半扫描_python 使用raw socket进行TCP SYN扫描实例
1. TCP SYN扫描 端口扫描常用于用于探测服务器或主机开放端口情况,被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务.端口扫描是向一定范围的服务器端口发送对应请求 ...
- RAW SOCKET探索
在Linux实现抓取以太网络上的数据包主要有libpcap,raw socket以及从内核中获取的方式. 我尝试选择使用raw socket来抓取以太网络上的数据包. 一.socket地址域(协议簇) ...
- 关于windows xp sp2/sp3 中tcpip.sys对于Raw socket的限制
WINDOWS XP SP2修改了TCPIP.SYS,增加了对于RAW SOCKET的发送和伪造源地的限制,详情如下 : 1) 不能通过raw socket发送TCP报文.做此尝试时会得到10004号 ...
- linux raw限制端口访出,使用Linux raw socket时需要注意的一些问题
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载.但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途. 作者:gfree.win ...
- 用C#的Raw Socket实现网络封包监视
<script language="javascript" src="/ad/js/edu_left_300-300.js" type="tex ...
最新文章
- Xilinx zynq的资料获取总结
- 用Groovy思考 第一章 用Groovy简化Java代码
- 欠阿里云一分钱,会是什么样的后果。。。
- Qt之QLineEdit详解(附源码)
- spring+redis自主实现分布式session(非spring-session方式)
- java 关于JDBC和DAO模式使用
- php相册上传和删除吗,php上传与删除图片的简单范例
- 2018北理复试机试题
- Linux虚拟文件系统(节点路径搜索)
- 【聚沙成塔】将Mon Jun 01 2020 00:00:00 GMT+0800 (中国标准时间) 转换为 2020-06
- 电脑版我的世界java_我的世界pc JAVA版
- matlab谐波分析fft,fft谐波分析_matlab进行fft谐波分析_交流电谐波?
- apple script to 1s screen capture snapshot w/ windowsill
- git bash批量dos2unix
- 机器学习与数据挖掘 编程实现k均值算法
- 选择小程序的8大理由,让你拒绝说No
- 麋鹿微博工具箱 · 批量删除微博、设置仅自己可见、仅好友可见等
- 根据输入的三角形的三条边a、b、c,如果可以构成三角形,计算并输出该三角形的面积和周长,否则输出“Not a Valid Triangle”。
- 【游戏设计】3D坦克大战
- 替代人工操作,模拟浏览器的“行为”