python 发送和发送ICMP数据包
ICMP协议在实际传输中数据包:20字节IP首部 + 8字节ICMP首部+ 1472字节<数据大小>38字节。对于ICMP首部细分为8位类型+8位代码+16位校验和+16位标识符+16位序列号
其中类型的取值如下,我们比较关注的是请求(取值为8)和应答(取值为0)
类型代码取值描述
00Echo Reply——回显应答(ping应答)
30Network Unreachable——网络不可达
31Host Unreachable——主机不可达
32Protocol Unreachable——协议不可达
33Port Unreachable——端口不可达
34Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特
35Source routing failed——源站选路失败
36Destination network unknown——目的网络未知
37Destination host unknown——目的主机未知
38Source host isolated(obsolete)——源主机被隔离(作废不用)
39Destination network administratively prohibited——目的网络被强制禁止
310Destination host administratively prohibited——目的主机被强制禁止
311Network unreachable for TOS——由于服务器型TOS,网络不可达
312Host unreachable for TOS——由于服务器型TOS,主机不可达
313Communication administratively pro hibited by filtering——由于过滤,通信被强制禁止
314Host precedence violation——主机越权
315Precedence cutoff effect——优先中止生效
40Source quench——源端被关闭(基本流控制)
50Redirect for network——对网络重定向
51Redirect for host—— 对主机重定向
52Redirect for TOS and network——对服务类型和网络重定向
53Redirect for TOS and host—— 对服务类型和主机重定向
80Echo request——回显请求(ping请求)
90Router advertisement——路由器通告
100Router solicitation—— 路由器请求
110TTL equals 0 during transit——传输期间生存时间为0
111TTL equals 0 during reassembly——再数据报组装期间生存时间为0
120IP header bad(catchall error)——坏的IP首部(包括各种差错)
121Required options missing——缺少必需的选项
130Timestamp request(obsolete)——时间戳请求(作废不用)
14Timestamp reply(obsolete)——时间戳应答(作废不用)
150Information request(obsolete)——信息请求(作废不用)
160Information reply(obsolete)——信息应答(作废不用)
170Address mask request——地址掩码请求
180Address mask reply——地址掩码应答
SOCK_RAW 是指原始套接字
接收
import socket import sys while True: r = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.getprotobyname('ICMP')) r.settimeout(5) if sys.platform == 'win32': # windows对raw socket有限制,需要bind一个地址 # https://docs.microsoft.com/en-us/windows/win32/winsock/tcp-ip-raw-sockets-2 r.bind(("192.168.2.12", 0)) # 设置混杂模式 r.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) try: packet, address = r.recvfrom(1024) ipHeaderPacket = packet[0:20] sourceIp = "%d.%d.%d.%d" % (ipHeaderPacket[16], ipHeaderPacket[17], ipHeaderPacket[18], ipHeaderPacket[19]) icmpHeaderPacket = packet[20:28] icmpType = icmpHeaderPacket[0] icmpCode = icmpHeaderPacket[1] print(f"{sourceIp}, {icmpType}, {icmpCode}") except Exception as e: print(e)
获取到请求数据包时,前20个字节是ip包,第12-15字节是当前接收机器ip,第16-19字节是发送ping机器的ip
icmp 包是ip包后面8字节,其中第一个字节是icmp的类型,第二字节是取值
发送
# coding=utf-8 import os import socket import struct import array class Pinger(object): def __init__(self, timeout=3): self.timeout = timeout self.__id = os.getpid() self.__data = struct.pack('h', 1) # h代表2个字节与头部8个字节组成偶数可进行最短校验 @property def __icmpSocket(self): # 返回一个可以利用的icmp原对象,当做属性使用 icmp = socket.getprotobyname("icmp") # 指定服务 sock = socket.socket(socket.AF_INET, socket.SOCK_RAW, icmp) # socket.SOCK_RAW原生包 return sock def __doCksum(self, packet): # 校验和运算 words = array.array('h', packet) # 将包分割成2个字节为一组的网络序列 sum = 0 for word in words: sum += (word & 0xffff) # 每2个字节相加 sum = (sum >> 16) + (sum & 0xffff) # 因为sum有可能溢出16位所以将最高位和低位sum相加重复二遍 sum += (sum >> 16) # 为什么这里的sum不需要再 & 0xffff 因为这里的sum已经是16位的不会溢出,可以手动测试超过65535的十进制数字就溢出了 return (~sum) & 0xffff # 最后取反返回完成校验 @property def __icmpPacket(self): # icmp包的构造 header = struct.pack('bbHHh', 8, 0, 0, self.__id, 0) packet = header + self.__data cksum = self.__doCksum(packet) header = struct.pack('bbHHh', 8, 0, cksum, self.__id, 0) # 将校验带入原有包,这里才组成头部,数据部分只是用来做校验所以返回的时候需要返回头部和数据相加 return header + self.__data def sendPing(self, target_host): try: socket.gethostbyname(target_host) sock = self.__icmpSocket sock.settimeout(self.timeout) packet = self.__icmpPacket sock.sendto(packet, (target_host, 1)) # 发送icmp包 result = sock.recvfrom(1024) ac_ip = result[1][0] print('[+] %s active' % (ac_ip)) except Exception as e: print(e) sock.close() sock.close() s = Pinger() s.sendPing('192.168.2.12')
下面是参考文档