Windows和Linu的区别是Windows允许我们嗅探所有协议的所有数据包,但Linux只能嗅探到ICMP数据。
因为使用混杂模式,在Windows上要使用管理员权限,在Linux上需要root权限。
在这个例子中,只需要设置原始套接字嗅探器,读取一个数据包,然后退出即可。代码如下:
host里面的主机,可以在Windows上用ipconfig查出来,如果写教材上的IP,会报错。
比如:return getattr(self._sock,name)(*args) socket.error: [Errno 10049] 或者 [Errno 10013]
10013这个,用管理员模式开启就行了。
10049这个,把代码中的IP地址改成自己的IP地址或者127.0.0.1即可
import socket
import os
#coding=utf-8
# host to listen on
host = "192.168.***.***"
# create a raw socket and bind it to the public interface
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# we want the IP headers included in the capture
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# if we're on Windows we need to send an IOCTL
# to setup promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# read in a single packet
print sniffer.recvfrom(65565)
# if we're on Windows turn off promiscuous mode
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
测试:
1.用管理员打开CMD,运行代码:
此处用的是127.0.0.1的IP (那些代码是在另一个CMD输入之后才出现的,刚开始什么也没有,此处我已经输入完毕)
打开第二个CMD,输入:
再看第一个CMD,会有数据回馈。
可以试着改以下IP地址试试,就是用本机IP地址,数据有点不同。
感觉这个书上不说清楚,查半天错误。。真滴脑阔痛
解码IP层
解码数据包中的IP头的部分,提取诸如协议类型(TCP,UDP和ICMP),源地址和目的地址等有用信息。
创建sniffer_ip_header_decode.py 如下:
import socket
import os
import struct
from ctypes import *
host = "192.168.233.131"
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4),
("version", c_ubyte, 4),
("tos", c_ubyte),
("len", c_ushort),
("id", c_ushort),
("offset", c_ushort),
("ttl", c_ubyte),
("protocol_num", c_ubyte),
("sum", c_ushort),
("src", c_ulong),
("dst", c_ulong)
]
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}
self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
# type of protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while True:
# read data
raw_buffer = sniffer.recvfrom(65565)[0]
ip_header = IP(raw_buffer[0:20])
print "Protocl: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
except KeyboardInterrupt:
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
主机地址修改一下,然后分别在Windows和Linux上运行:
window:
然后打开浏览器,随便输入一个网站,将在原来的CMD里看到输出:
在Linux上:
Linux上只输出了ICMP的头部解析
解析ICMP
在以上的代码上添加一些代码为sniffer_with_icmp.py,用于解析ICMP报文。输出类型,可通过此知道连接状态。
import socket
import os
import struct
from ctypes import *
host = "192.168.233.131"
class ICMP(Structure):
_fields_ = [
("type", c_ubyte),
("code", c_ubyte),
("checksum", c_ushort),
("unused", c_ushort),
("next_hop_mtu", c_ushort)
]
def __new__(self, socket_buffer):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer):
pass
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4),
("version", c_ubyte, 4),
("tos", c_ubyte),
("len", c_ushort),
("id", c_ushort),
("offset", c_ushort),
("ttl", c_ubyte),
("protocol_num", c_ubyte),
("sum", c_ushort),
("src", c_ulong),
("dst", c_ulong)
]
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}
self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
# type of protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while True:
# read in a single packet
raw_buffer = sniffer.recvfrom(65565)[0]
# create an IP header from the first 20 bytes of the buffer
ip_header = IP(raw_buffer[0:20])
print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# if it's ICMP we want it
if ip_header.protocol == "ICMP":
# calculate where our ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + sizeof(ICMP)]
# create our ICMP structure
icmp_header = ICMP(buf)
print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)
except KeyboardInterrupt:
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
在Linux上测试:
添加netaddr
实现发送UDP数据和获取扫描结果
需要添加netaddr模块,实现对整个子网扫描。在以上代码的基础上修改,保存为scanner.py
首先确认安装了netaddr:
import socket
import threading
import time
from netaddr import IPAddress, IPNetwork
import os
import struct
from ctypes import *
host = "192.168.233.131"
subnet = "192.168.233.0/24"
# to tell the recieced data from icmp
magic_message = "Welcome to the jungle!"
# send udp
def udp_sender(subnet,magic_message):
time.sleep(5)
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for ip in IPNetwork(subnet):
try:
sender.sendto(magic_message,("%s" % ip, 65212))
except:
pass
class ICMP(Structure):
_fields_ = [
("type", c_ubyte),
("code", c_ubyte),
("checksum", c_ushort),
("unused", c_ushort),
("next_hop_mtu", c_ushort)
]
def __new__(self, socket_buffer):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer):
pass
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4),
("version", c_ubyte, 4),
("tos", c_ubyte),
("len", c_ushort),
("id", c_ushort),
("offset", c_ushort),
("ttl", c_ubyte),
("protocol_num", c_ubyte),
("sum", c_ushort),
("src", c_ulong),
("dst", c_ulong)
]
def __new__(self, socket_buffer=None):
return self.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
self.protocol_map = {1:"ICMP", 6:"TCP", 17:"UDP"}
self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
# type of protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
# begin to send data
t = threading.Thread(target=udp_sender,args=(subnet,magic_message))
t.start()
try:
while True:
# read in a single packet
raw_buffer = sniffer.recvfrom(65565)[0]
# create an IP header from the first 20 bytes of the buffer
ip_header = IP(raw_buffer[0:20])
# print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# if it's ICMP we want it
if ip_header.protocol == "ICMP":
# calculate where our ICMP packet starts
offset = ip_header.ihl * 4
buf = raw_buffer[offset:offset + sizeof(ICMP)]
# create our ICMP structure
icmp_header = ICMP(buf)
# print "ICMP -> Type: %d Code: %d" % (icmp_header.type, icmp_header.code)
if icmp_header.code == 3 and icmp_header.type == 3:
# check to make sure we are receiving the response
# that lands in our subnet
if IPAddress(ip_header.src_address) in IPNetwork(subnet):
# test for our magic message
if raw_buffer[len(raw_buffer)-len(magic_message):] == magic_message:
print "Host Up: %s" % ip_header.src_address
except KeyboardInterrupt:
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
修改主机IP和子网地址,测试如下:
这里我只返回了一条信息。。。也不知道为啥。。先放着。。