Sniffer 总述
Sniffer,中文可以翻译为嗅探器,也叫抓数据包软件,是一种基于被动侦听原理的网络分析方式
- 使用这种技术方式,可以监视网络的状态、数据流动情况以及网络上传输的信息
与 Sniffer 相关的知识
网络信息通过 TCP/IP
来定位网络中计算机的位置:
- 应用程序的 IP 报文被封装成以太网帧(底层链路层报文上面的一层报文,包含有源地址,报文和一些需要用来传送至目标主机的信息)
- 目的 IP 地址对应着一个6字节的目的以太网址(MAC 地址),它们之间通过 ARP/RARP 协议进行映射
- ARP(Address Resolution Protocol)地址解析协议:IP地址转换为MAC物理地址
- RARP(Reverse Address Resolution Protocol)反向地址转换协议:将MAC物理地址转换为IP地址
- 包含着以太网帧的报文从源主机传输到目的主机,中间经过一些网络设备,如交换机,路由器等
源主机发出的太网帧基于广播方式传播(网络中的所有网卡都能看到它的传输)
- 每个网卡会检查帧开始的6个字节(目的主机的 MAC 地址),但是只有一个网卡会发现自己的地址和其相符合,然后它接收这个帧
- 读取该太网帧中的 IP 报文
- 网络驱动程序会检查帧中报文头部的协议标识,以确定接收数据的上层协议
- 然后通过对应的网络协议栈传送至接收的应用程序
- 大多数情况下,上层就是 IP 协议,所以接收机制将去掉 IP 报文头部,然后把剩下的传送至 UDP 或者 TCP 接收机制,这些协议将把报文送到
socket-handling
机制 - 然后把报文数据变成应用程序可接收的方式发送出去,在这个过程中,报文将失去所有的和其有关的网络信息(比如:IP,MAC,端口号,IP 选择,TCP 参数 ……),所以如果目的主机没有一个包含正确参数的打开端口,那么这个报文将被丢弃而且永远不会被送到应用层去
- 大多数情况下,上层就是 IP 协议,所以接收机制将去掉 IP 报文头部,然后把剩下的传送至 UDP 或者 TCP 接收机制,这些协议将把报文送到
数据传输经过的各层协议过程
- ARP/RARP 用于实现 MAC/IP 之间的切换
太网帧大致结构
1 | [MAC头,---------------{数据}----------------,MAC尾] >>>> 以太网驱动 |
通信过程中,每层协议都要加上一个数据首部(用于存储信息),称为封装 Encapsulation,常见头部如下:
- 以太网帧头部 - 14字节(也叫 MAC 头部):
1 |
|
- IP 头部 - 20~60字节(取决于有没有 options):
1 | struct iphdr { |
- UDP 头部 - 20字节(8字节真头部,12字节假头部):
1 | struct udphdr { |
- TCP 头部 - 20~60字节(取决于有没有 options):
1 | struct tcphdr { |
网络工具 netwox 简述
netwox 是由 lauconstantin 开发的一款网络工具集,适用群体为网络管理员和网络黑客,它可以创造任意的 TCP、UDP 和 IP 数据报文,以实现网络欺骗,并且可以在 Linux 和 Windows 系统中运行
1 | ➜ Sniffer sudo netwox 32 |
00:0C:29:BF:5F:3F
:源 MAC 地址,是当前主机的 MAC 地址00:08:09:0A:0B:0C
:目标 MAC 地址0x0000
:以太网类型
启动 netwox 显示它提供的功能:
1 | ➜ Sniffer netwox |
- 这不是本篇博客的重点,以后有机会慢慢看
网络编程基础
Linux 实现网络通信的核心函数 socket
如下:
1 | int socket(int domain, int type, int protocol); |
- domain:即协议域,又称为协议族(family)
- 常用的协议族有:AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE
- 协议族决定了 socket 的地址类型,在通信中必须采用对应的地址:
- AF_INET:用 ipv4 地址(32位的)与端口号(16位的)的组合
- AF_UNIX:用一个绝对路径名作为地址
- type:指定 socket 类型
- 常用的 socket 类型有:SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET
- 不同 socket 类型有不同的功能:
- SOCK_STREAM:提供面向连接的稳定数据传输,即TCP协议
- OOB:在所有数据传送前必须使用 connect() 来建立连接状态
- SOCK_SEQPACKET:提供连续可靠的数据包连接
- SOCK_RDM:提供可靠的数据包连接
- SOCK_PACKET:与网络驱动程序直接通信
- SOCK_DGRAM:使用不连续不可靠的数据包连接(去除以太网报文头部)
- SOCK_RAW:提供原始网络协议存取(让应用程序对以太网报文头部有完全的控制)
- protocol:
- 指定协议
- 常用的协议有,IPPROTO_TCP(TCP传输协议)、IPPTOTO_UDP(UDP传输协议)、IPPROTO_SCTP(STCP传输协议)、IPPROTO_TIPC(TIPC传输协议)
服务端需要 bind
函数来“绑定”一个端口
客户端需要 connect
函数来连接指定的服务端(通过 IP:port
)
实现 Sniffer
Sniffer 需要一个 socket 去监听每个端口,得到那些没有被丢弃的报文,使用 PF_PACKET 的协议簇,应用程序可以直接利用网络驱动程序发送和接收报文,避免了原来的协议栈处理过程
Sniffer 案例一
1 |
|
- 使用 PF_PACKET 协议族嗅探所有发往自己主机的报文
- 通过简单的格式化输出 MAC 头与 IP 头的信息
运行结果如下:
1 | ➜ Sniffer sudo ./test1 |
- 本地进程通信的太网帧
Sniffer 案例二
在上述案例中(非混杂模式),网卡丢弃所有不含有主机 MAC 地址的数据包,只有三个例外:
- 如果一个帧的目的 MAC 地址是一个受限的广播地址
255.255.255.255
那么它将被所有的网卡接收 - 如果一个帧的目的地址是组播地址,那么它将被那些打开组播接收功能的网卡所接收
- 网卡如被设置成混杂模式,那么它将接收所有流经它的数据包
在 Linux 中可以使用如下代码 开启/关闭 混杂模式:
1 | sudo ifconfig ens33 promisc |
- 我的 VMware 上使用 ens33 虚拟网卡
使用如下代码来查看目标网卡是否处于混杂模式:
1 | cat /sys/class/net/ens33/flags |
0x1103
:开启0x1003
:关闭
在C语言中使用如下代码也可以开启混杂模式:
1 | struct ifreq ethreq; |
Sniffer 案例三
接下来要实现数据包的过滤,Linux 内核允许我们把一个名为 LPF 的过滤器直接放到 PF_PACKET 协议处理例程中(在网卡接收中断执行后立即执行)
- 该过滤程序可以根据使用者的定义来运行
- 由一种名为 BPF(Berkely Packet Filter 伯克利数据包过滤器)的伪机器码写成的
使用 tcpdump
可以快速生成 BPF 代码:
1 | ➜ Sniffer sudo tcpdump -d host 192.168.157.136 |
- 第 0-1,6-7 行:确定被抓到的帧是否在传输着 IP,ARP 或者 RARP 协议
- 第 2-5,8-11 行:比较源地址或者目的地址是否与
192.168.157.1
相同 - 将第12格的值与协议的特征值比较,如果比较失败,那么丢弃这个包
在C语言中,建立一个 sock_filter
并为它绑定一个打开的端口,就可以使用该 BPF
使用 tcpdump
可以生成 BPF 对应的C代码:
1 | ➜ Sniffer sudo tcpdump -dd host 192.168.157.136 |
最后得到最终的代码:
1 | ➜ Sniffer sudo ./test2 |
Sniffer 代码
下面给出我写的 Sniffer 代码:
1 |
|
参考:NSFOCUS绿盟科技