what:
raw socket,即原始套接字,可以接收本机网卡上的数据帧或者数据包,对于监听网络的流量和分析是很有作用的。
where:
一共可以有4种方式创建这种socket:
1.socket(PF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
该套接字可以接收协议类型为(tcp udp icmp等)发往本机的ip数据包、不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包)、不能收到从本机发送出去的数据包。
发送时需要自己组织tcp udp icmp等头部、可以setsockopt来自己包装ip头部。
适用于ping程序
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
socket(AF_PACKET, SOCK_RAW, htons(x))
创建这种套接字可以监听网卡上的所有数据帧。
ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x806 只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
3.socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧(不包括以太网头部)
socket(AF_PACKET, SOCK_DGRAM, htons(x))
功能与第二种功能类似,但是不包括以太网头部
他可以收取非IP协议的数据包
4.socket(PF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))
一般用于抓包程序
why:
原始套接字与标准套接字的区别在于:
原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送数据必须使用原始套接字。
how:
python实现简易的sniffer
import socket
# the public network interface
HOST = socket.gethostbyname(socket.gethostname())
# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))
# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# receive a package
print(s.recvfrom(65565))
# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
解析ip数据包:
import socket
#抓捕
def sniffIpData():
host_ip = socket.gethostbyname(socket.gethostname()) #获取IP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) #创建套接字、可接受协议类型为UDP、TCP、ICMP、IP
sniffer.bind((host_ip, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) #设置套接字options、包装ip头部
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) #receive all package
recv_data, addr = sniffer.recvfrom(1500)
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) # disabled promiscuous mode
sniffer.close()
return recv_data
#解析
def decodeIpData(package):
ip_data = {}
#RFC791
ip_data['version'] = package[0] >> 4
ip_data['headLength'] = package[0] & 0x0f #& 按位与操作
ip_data['DSField'] = package[1]
ip_data['totalLength'] = (package[2] << 8) + package[3]
ip_data['identification'] = (package[4] << 8) + package[5]
ip_data['flag'] = package[6] >> 5
ip_data['moreFragment'] = ip_data['flag'] & 1
ip_data['dontFragment'] = (ip_data['flag'] >> 1) & 1
ip_data['fragmentOffset'] = ((package[6] & 0x1f) << 8) + package[7]
ip_data['TTL'] = package[8]
ip_data['protocol'] = package[9]
ip_data['headerCheckSum'] = (package[10] << 8) + package[11]
#以IP地址形式存储
ip_data['sourceAddress'] = "%d.%d.%d.%d" % (package[12], package[13], package[14], package[15])
ip_data['destinationAddress'] = "%d.%d.%d.%d" % (package[16], package[17], package[18], package[19])
ip_data['options'] = []
#根据headerLength求出options
if ip_data['headLength'] > 5: #一般来说此处的值为0101,表示头长度为20字节、若超出则大于5(0101)
temp = 5
while temp < ip_data['headLength']:
ip_data['options'].append(package[temp * 4] + 0)
ip_data['options'].append(package[temp * 4] + 1)
ip_data['options'].append(package[temp * 4] + 2)
ip_data['options'].append(package[temp * 4] + 3)
temp += 1
#根据totalLength求出data
ip_data['data'] = []
temp = ip_data['headLength'] * 4
while temp < ip_data['totalLength']:
ip_data['data'].append(package[temp])
temp += 1
return ip_data
package = sniffIpData()
data_decode = decodeIpData(package)
# for i, k in data_decode:
# print("%d:%d" % (i, k))
for key in data_decode.items():
print(key)
至此、简易版本的已经实现: