网络安全编程——Python编写基于UDP的主机发现工具(解码IP header)
网络嗅探是网络安全分析的基础手段。本文基于Python原始socket实现网络嗅探器完成IP报文头的结构解析详细说明代码逻辑与实现原理并通过实际抓包效果验证功能。文章涵盖原理讲解、代码实现、效果演示等内容帮助理解网络数据包的底层结构与嗅探技术的实现方式适用于网络安全学习与实践参考。文章目录前情提要原理讲解原始socket嗅探器代码解释效果演示解码IP头结构Python实现典型IPv4头结构代码解释效果演示总结前情提要从本篇开始我们要着手开始编写一个流量嗅探器嗅探工具的主要目标是发现目标网络里存活的主机。攻击者希望能找出网络里的所有潜在目标以便有针对性地开展侦察和渗透原理讲解UDP请求的过程主机存活当我们向主机发送一个UDP数据包时如果主机上的UDP端口没有开启一般会返回一个ICMP包来提示目标端口不可访问。主机不存在如果主机根本不存在的话我们应该不会收到任何信息前提是选中的UDP端口没有被使用所以未来避免这种情况我们应该尽可能多的探测多个端口UDP的优势在整个子网里滥发UDP数据包并等待对方回复ICMP消息的开销很小其主要的工作就是解码并分析各种网络协议的数据头原始socket嗅探器这里我们先编写一个最简单的socket嗅探器只能读取一个数据包importosimportsocket# 定义了需要监听的主机 IP 地址host192.168.1.10defmain():# 定义所需的参数socket_protocolifos.nament:socket_protocolsocket.IPPROTO_IPelse:socket_protocolsocket.IPPROTO_ICMP# 创建socket对象并绑定端口sniffersocket.socket(socket.AF_INET,socket.SOCK_RAW,socket_protocol)sniffer.bind((host,0))# 抓包时包含IP头;# socket.IP_HDRINCL1 时代表包含IP头sniffer.setsockopt(socket.IPPROTO_IP,socket.IP_HDRINCL,1)# 打开混杂模式监听所有数据包(但是该程序只能读取一个)ifos.nament:sniffer.ioctl(socket.SIO_RCVALL,socket.RCVALL_ON)# 读取收到的数据包print(fReceive data:{sniffer.recvfrom(65535)})# 如果是Windows记得关闭混杂模式ifos.nament:sniffer.ioctl(socket.SIO_RCVALL,socket.RCVALL_OFF)if__name____main__:main()代码解释HOST 192.168.44.142: 定义了需要监听的主机 IP 地址。判断操作系统: 脚本首先检查了os.name是否为nt代表 Windows 系统。如果是 Windows则协议设置为socket.IPPROTO_IP这意味着它可以嗅探所有的 IP 数据包。如果是 Linux/Unix则设置为socket.IPPROTO_ICMP。因为在 Linux 上捕获所有 IP 数据包通常需要更复杂的设置这里作为基础脚本后退了一步只去捕获 ICMP例如 Ping 请求数据包。1创建并绑定原始套接字 (Raw Socket)sniffer socket.socket(...)这里创建了一个原始套接字SOCK_RAW普通的套接字如 TCP/UDP会由操作系统自动处理网络头信息而原始套接字允许程序直接访问底层的网络数据包。sniffer.bind((HOST, 0))将该套接字绑定到之前设定的公开网卡 IP 上。端口指定为0因为原始套接字关注的是网络层协议而不是应用层端口。2 配置捕获 IP 头部信息sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)IP_HDRINCL(IP Header Include) 标志位设为1是在告诉操作系统“在返回给我的数据中请包含完整的 IP 数据包头部信息源IP、目标IP、协议类型等”而不仅仅是数据内容本身。(3)开启网卡的混杂模式 (Promiscuous Mode) - 仅限 Windowssniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)默认情况下网卡只会接收发往自己 MAC 地址的数据包。开启“混杂模式”后网卡会接收局域网内流经它的所有数据包不管是不是发给它的。这一步是通过底层的 I/O 控制 (IOCTL) 命令实现的。4读取数据包recvfrom(65565)调用recvfrom方法接收数据。65565是缓冲区大小足够容纳目前最大可能的 IP 数据包。注意运行此类涉及原始套接字和混杂模式的代码通常需要管理员权限 (Win)或Root 权限 (Linux)。效果演示这里我用Windows进行测试未开启“管理员权限”执行用管理员执行这里我们打开另一个cmd窗口访问baidu.com成功抓到第一关握手包作用正在向一个 HTTPS 网站发起连接请求TCP SYN ↓ 访问某个加密网站 ↓ 发起 TCP三次握手 的第一个包这里我们成功编写了最简单的一个socket抓包嗅探器但是不觉得少了很多东西吗解码IP头得到 源 / 目的 地址端口解码ICMP包判断目标主机是否存活所以接下来我们需要做的就是学会如何解码 IP头 以及 ICMP包并将它们打印出来;解码IP头结构Python实现虽然当前我们的嗅探器可以捕获到TCP、UDP、ICMP等任何高层协议的IP头但里面的信息是以二进制形式封装的是不是很难读懂所以我们需要讲这些数据转化为人类看得懂的参数这里需要使用到struct库来帮助我们解析典型IPv4头结构但在此之前我们需要了解IP头的基本构成这里我们的目的提取协议类型、源IP地址和目的IP地址等信息importipaddressimportstructclassIP:def__init__(self,buffNone):# 使用 struct 模块解析前 20 个字节的 IP 头# 代表小端序B代表1字节无符号整数H代表2字节4s代表4字节字符串headerstruct.unpack(BBHHHBBH4s4s,buff[0:20])# 通过位运算提取版本号 (Version) 和头部长度 (IHL)self.verheader[0]4self.h_lenheader[0]0xFself.server_typeheader[1]self.lenheader[2]self.idheader[3]self.offsetheader[4]self.ttlheader[5]self.protocol_numheader[6]self.sumheader[7]self.srcheader[8]self.dstheader[9]# 将二进制的源/目的 IP 转换为人类可读格式 (如 192.168.10.1)self.src_addressipaddress.ip_address(self.src)self.dst_addressipaddress.ip_address(self.dst)# # 协议号映射表self.protocol_map{1:ICMP,6:TCP,17:UDP}try:self.protocolself.protocol_map(self.protocol_num)exceptKeyError:self.protocolstr(self.protocol_num)# 接下来的代码就是之前的原始socket嗅探器的功能了defsniff(host):ifos.nament:socket_protocolsocket.IPPROTO_IPelse:socket_protocolsocket.IPPROTO_ICMP# 创建socket对象并绑定端口sniffersocket.socket(socket.AF_INET,socket.SOCK_RAW,socket_protocol)sniffer.bind((host,0))# 抓包时包含IP头;# socket.IP_HDRINCL1 时代表包含IP头sniffer.setsockopt(socket.IPPROTO_IP,socket.IP_HDRINCL,1)# 打开混杂模式监听所有数据包(但是该程序只能读取一个)ifos.nament:sniffer.ioctl(socket.SIO_RCVALL,socket.RCVALL_ON)# 读取收到的数据包print(f[*] 正在{host}上嗅探流量.... )try:whileTrue:# 读取一个数据包raw_buffersniffer.recvfrom(65535)[0]# 解析IP头ip_headerIP(raw_buffer[0:20])print(fProtocol:{ip_header.protocol}|{ip_header.src_address}--{ip_header.dst_address})exceptKeyboardInterruptase:print(f[*] 用户中止嗅探....)# 关闭嗅探模式如果Windowsifos.nament:sniffer.ioctl(socket.SIO_RCVALL,socket.RCVALL_OFF)sys.exit()if__name____main__:# 这里必须填你运行代码的本机的真实局域网 IPhost_ip192.168.1.10sniff(host_ip)代码解释SOCK_RAW原始套接字作用 原始套接字允许我们直接读取和伪造 IP 报文头包含源 IP、目的 IP、TTL 存活时间等struct.unpack解码器的核心作用根据上图知道 IP 报文头固定是 20 个字节且规定了第几个字节代表什么比如前 4 个 bit 代表 IPv4 还是 IPv6socket.IP_HDRINCL一般情况下操作系统会自动把 IP 报文头部剥离掉标志位设为1是在告诉操作系统“在返回给我的数据中请包含完整的 IP 数据包头部信息1byte(字节) 8bit代码变量对应 IP 头字段字节位置self.ver / self.h_len版本 / IHL0self.server_type服务类型1self.len总长度2-3self.id标识4-5self.offset标志 段偏移6-7self.ttlTTL8self.protocol_num协议9self.sum头校验和10-11self.src源 IP12-15self.dst目的 IP16-19效果演示这里我们执行代码TCP 流量你电脑正在和公网服务器建立 / 维持加密网页HTTPS连接UDP 流量你电脑正在进行 DNS 查询、音视频 / 游戏等无连接通信双向通信每一组请求都对应了服务器的响应符合网络通信的基本原理当然如果想看ICMP包的话直接ping jd.com即可总结因为我们没有深入地解码数据包的内容所以这里只能猜测整个数据流的含义。但整体来说还是实现了基本功能下一篇我们就要实现ICMP包解析等功能了期待下次再见