第三章 网络:原始套接字和流量嗅探
Windows上和Linux上的包嗅探
在示例中,我们首先导入socket与os模块,根据os.name判断主机操作系统,该变量属性当前只注册了三个值,具体如下:
posix | nt | java |
---|---|---|
Linux | Windows | Java虚拟机 |
根据操作系统创建原始套接字,这里因为Linux系统只能嗅探到ICMP的数据包,所以这里创建的是基于ICMP包的原始套接字IPPROTO_ICMP
,但windows可以嗅探到所有IP数据包IPPROTO_IP
,所以这里需要使用os.name
对主机操作系统进行判断,然后使用原始套接字构造IP头socket(socket.AF_INET,socket.SOCK_RAW, socket.IPPROTO_IP)
,使用bind()
方法对网卡进行监听,这里ICMP包是不具备端口号的,所以端口号为0,然后使用setsockopt()方法设置在捕获的数据包中包含IP头,如果操作系统为windows的话,这里需要使用ioctl()
方法开启网卡混杂模式,然后使用recvfrom()
方法读取单个数据库,用if语句对操作系统再次判断,如为windows则关闭网卡混杂模式。
import socket
import os
# 监听的网卡 0.0.0.0表示所有网卡
host = "172.16.1.7"
# 创建一个原始套接字(RAW Socket),然后绑定到公开接口上
if os.name == "nt": # Mac的os.name==posix
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
# Windows和Linux的区别是Windows允许我们嗅探所有协议的所有数据包,但Linux只能嗅探到ICMP数据。
# 使用原始套接字用于构造IP头
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0)) # 对网卡进行监听,由于ICMP包是不具备端口号的,所以端口填什么都可以,你可以try一try
# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# 在Windows平台上,需要设置IOCTL以启动混杂模式,以允许我们嗅探网卡上经过的所有数据包(即使数据的目的地址不是本机)
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# 读取单个数据包
print(sniffer.recvfrom(65535))
# 在Windows平台上关闭混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
下面是执行代码得到的结果:
解码IP层
在理解代码之前,我们首先得要理解IP头结构:
详情可以参考https://www.cnblogs.com/jacklikedogs/articles/3848263.html
理解完IP头结构之后,我们先直接看看代码:
import socket
import os
import struct
from ctypes import *
# 监听的主机IP
host = "172.16.1.7"
# IP头定义
class IP(Structure):
_fields_ = [
('ihl', c_ubyte, 4), # 头长度ip header length
('version', c_ubyte, 4), # 版本
('tos', c_ubyte), # 服务类型
('len', c_ushort), # ip数据包总长度
('id', c_ushort),