网络流量嗅探


2020-08-11  记录

参考《python黑帽子 黑客渗透测试与编程之道》,内容大多都是书里的,忏愧,本人战五渣。主要就记录自己怎么学的吧。。。


 

  一般黑客发起攻击前都得踩点,了解下有哪些主机开着,它们身上运行着哪些程序,它们都在传输着些什么信息等等。

  有一种踩点,就是嗅探,目的是为了解析目标主机传输流量的内容,这种属于被动攻击。说到嗅探,就想到了wireshark,实验课也是用这个来学习协议的呢!虽然我学得不咋样。

  在一个局域网里,对于我的主机来讲(我的主机不是路由节点),我和其它人连着同一个WiFi,就是在同一个WLAN下,就是一个冲突域(对于以太网和无线局域网),一般别人的数据我的主机也是可以接收到的,不过网卡就是个硬件的"过滤器",它会识别MAC地址过滤掉和自己无关的信息。不过我们还是可以选择来接受这些无关的信息,只要将网卡设置为"混杂模式"就可以了(一般情况下,主机都是非混杂模式的)。开启混杂模式,在Linux下需要root权限,Windows下需要管理员权限。

  目前WLAN下连接的设备有3台(以IP地址表示):

  1.   192.168.0.101  -------------本机   SC-202002071824 
  2.        192.168.0.102                                 TL-WR886N 
  3.   192.168.0.100                                 vivo-Y67 

   直接ping设备vivo-Y67,可以ping通:

1 正在 Ping 192.168.0.100 具有 32 字节的数据:
2 来自 192.168.0.100 的回复: 字节=32 时间=4ms TTL=64
3 来自 192.168.0.100 的回复: 字节=32 时间=323ms TTL=64
4 来自 192.168.0.100 的回复: 字节=32 时间=328ms TTL=64
5 来自 192.168.0.100 的回复: 字节=32 时间=647ms TTL=64

  可以ping通,说明目标主机是存活的;不过一般为了安全,主机会禁止ping功能,如果我们去ping这种主机,一般返回的是目标端口不可达的信息,这也说明了目标主机是存活的。

  一般网络主机扫描的话用UDP协议比较好,它是面向非连接的,相比于TCP,它的消耗要小。


 

开胃菜-先试试能不能接收到数据包

测试环境:Windows10,python3.7

Linux下开启混杂模式的命令:

1 # ifconfig eth1 promisc             #设置混杂模式
2 # ifconfig eth1 -promisc            #取消混杂模式

 

promisc adj.混杂的

 

Windows下开启混杂模式:

使用套接字输入/输出控制(IOCTL)来设置标志,向网卡驱动发送IOCTL信号来启用混杂模式。

 

代码清单1

 1 # -*- coding=utf-8 -*-
 2 import socket
 3 import os
 4 
 5 host = '192.168.0.101'
 6 
 7 # 判断是否处于windows下
 8 if os.name == 'nt':
 9     socket_protocol = socket.IPPROTO_IP
10 else:
11     socket_protocol = socket.IPPROTO_ICMP
12 
13 # 创建套接字
14 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
15 
16 sniffer.bind((host, 0))
17 
18 # 设置捕获的数据包中包含IP头
19 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
20 
21 # Windows下开启混杂模式
22 if os.name == 'nt':
23     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
24 
25 print(sniffer.recvfrom(65565))
26 
27 # Windows下关闭混杂模式
28 if os.name == 'nt':
29     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

 

8-11行:为啥Windows下要用socket.IPPROTO_IP参数,Linux下用socket.IPPROTO_ICMP参数呢?

    Windows和Linux下的套接字是有区别的,Windows允许我们嗅探所有协议的所有数据包,而Linux只能嗅探到ICMP数据。

14行:socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

   直接看下面的参数解释:(copy别人博客的)

参数一:地址簇

  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文SOCK_RDM通常仅限于高级用户或管理员运行的程序使用。

  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

    IPPROTO_ICMP = 1
    IPPROTO_IP = 0

  第2个参数选择socket.SOCK_RAW就是要监听各种协议的数据包的。。。

16行:sniffer.bind((host, 0));将套接字绑定在一个地址上,这个地址是IP+端口构成的一个元组。可是端口为啥要绑定到0上呢?

    绑定到0,就是告诉系统自己选一个合适的端口给套接字。

   平常我们写服务端程序时,总是要绑定到确定的IP地址和端口上,为啥?服务器的地址就是得周知的,客户端才知道向谁请求。那么客户端程序怎么没有写bind语句呢?呵呵,不写,都是让系统自动分配一个合适的IP地址和端口号的,我们省略了这一步。

19行:  sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

    这是自己来设置套接字的选项,设置在捕获的数据包中包含IP头部。

23行: sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

    很明显,看英文意思就是开启接受所有数据的开关。这里打开网卡的混杂模式。

 

接下来运行看看能不能收到什么东西。

(b'E\x00\x00\xcaG\x13\x00\x00\x02\x11\xbf\xe8\xc0\xa8\x00\x85\xef\xff\xff\xfa9U\x07l\x00\xb6^\xffM-SEARCH * HTTP/1.1\r\nMX: 1\r\nST: upnp:rootdevice\r\nMAN: "ssdp:discover"\r\nUser-Agent: UPnP/1.0 IQIYIDLNA/iqiyidlna/NewDLNA/1.0\r\nConnection: close\r\nHost: 239.255.255.250:1900\r\n\r\n', ('192.168.0.133', 0))

 

抓到了192.168.0.133这台主机的数据包(我的是192.168.0.101),不过我看不懂这是啥,好像是啥协议。。。反正现在可以接收到数据包了,这只是个测试。

 


 

对原始数据进行探索

  学过计算机网络的都知道,数据从应用层-->表示层-->会话层-->传输层-->网络层-->数据链路层-->物理层下去,一层层地将数据封装,到达目标主机后按相反的顺序再拆封,往上传。(现实中不一定都要经过7层,那个只是标准化模型)。数据的封装是按照协议来的,上面抓的那个数据包的内容也看不出啥有用的内容。我们需要对数据包进行解码才行,就像主机各个层的协议会对数据解码一样。

  现在我们只解码数据包里的IP头数据。先看看IP协议包的构成:

 

 

                 图1.IPV4协议的内容

解码是啥,就是我知道这个数据包的0-3字节表示版本号,4-7字节表示头长度,那么我就每次读取0-3字节时,用一个unsigned char数组变量来接受0-3字节的数据,这个变量表示的就是版本号,以此类推。

看看IP头在C语言中如何表示:

 1 struct ip{
 2     u_char ip_hl:4;        // 头长度        1Bytes     :4是比特位标志,说明字段按比特位计算,长度是4比特。就是1个字节里只用到4比特。  3     u_char ip_v:4;         // 版本号        1Bytes
 4     u_char ip_tos;         // 服务类型       1Byte          
 5     u_short ip_len;        // IP数据包总长度   2Bytes
 6     u_short ip_id;         // 标记         2Bytes
 7     u_short ip_off;        // 片偏移        2Bytes
 8     u_char  ip_ttl;        // 生存时间       1Byte
 9     u_char  ip_p;         // 协议类型        1Byte
10     u_short ip_sum;        // 头部校验       2Byte
11     u_long  ip_src;        // 源IP地址       32bit系统下u_long是4Bytes,64bit系统下u_long是8Bytes 
12     u_long  ip_dst;        // 目的IP地址       32bit系统下是u_long4Bytes,64bit系统下u_long是8Bytes 
13 };

 

对照着协议标准里的内容和真正实现的代码,发现不是所有字节都有用到的。

以C语言的结构体为参照,然后使用python的ctypes模块来创建类似于C的结构体,直接上代码:

 

代码清单2:

 1 # -*- coding=utf-8 -*-
 2 import socket
 3 import os
 4 import struct
 5 from ctypes import *
 6 
 7 # 监听的主机
 8 host = '192.168.0.101'
 9 
10 
11 class IP(Structure):
12     _fields_ = [
13         ('ihl', c_ubyte, 4),
14         ('version', c_ubyte, 4),
15         ('tos', c_ubyte),
16         ('len', c_ushort),
17         ('id', c_ushort),
18         ('offset', c_ushort),
19         ('ttl', c_ubyte),
20         ('protocol_num', c_ubyte),
21         ('sum', c_ushort),
22         ('src', c_ulong),
23         ('dst', c_ulong)
24     ]
25 
26     def __new__(self, socket_buffer=None):
27         return self.from_buffer_copy(socket_buffer)
28 
29     def __init__(self, socket_buffer=None):
30         self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
31         self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
32         self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
33 
34         try:
35             self.protocol = self.protocol_map[self.protocol_num]
36         except:
37             self.protocol = str(self.protocol_num)
38 
39 
40 if os.name == 'nt':
41     socket_protocol = socket.IPPROTO_IP
42 else:
43     socket_protocol = socket.IPPROTO_ICMP
44 
45 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
46 
47 sniffer.bind((host, 0))
48 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
49 
50 # 打开混杂端口
51 if os.name == 'nt':
52     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
53 
54 # 循环侦听
55 try:
56     while True:
57         # 读取数据包
58         raw_buffer = sniffer.recvfrom(65565)[0]
59         # 将缓冲区的前20字节按IP头进行解析
60         ip_header = IP(raw_buffer[0:20])
61         # 输出双方IP地址
62         print("Protocol:{0} {1} -> {2}".format(ip_header.protocol, ip_header.src_address, ip_header.dst_address))
63 # 处理CTRL-C
64 except KeyboardInterrupt:
65     # 关闭混杂模式
66     if os.name == 'nt':
67         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

 

IP类继承自Structure类,这个类就是用来构造类似C语言的结构体的。

使用ctypes模块就可以调用C语言的函数,变量的类型定义等(short, 指针,结构体之类的)。

  IP类中出现了__new__和__init__函数,16行:IP(raw_buffer[0:20]),当我们要创建一个类的实例时,会先调用这个类的__new__()函数来生成一个实例,然后通过这个实例调用__init__()方法来初始化该实例的变量和参数。(想到C++里先new一个对象,然后再调用构造函数了。。。)

流程:

1.判断当前操作系统,来确定要使用的协议类型socket_protocol;

2.创建可以接受原始数据流的套接字;

3.绑定套接字到"192.168.0.101"上,端口由系统分配;

4.设置套接字选项,接受数据中的IP头部;

5.打开网卡的混杂端口;

6.开始侦听,将接受到的数据作为参数传给IP类,由IP类解析数据包,返回IP实例,这个实例里有解码后的信息;

7.打印IP头部信息;

8.如果遇到中断信号CTRL-C,关闭网卡的混杂模式。

 

IP类中的_fields_是一个列表,列表里的元素是元组。这就是IP类的内置变量了(以单下划线开头,单下划线结尾),IP类的每个函数都可以使用它。

传进IP类的数据流的前20个字节会按在__new__()执行时填充到__fields__结构当中,相当于解码了。。。然后__init__()方法会把这些结构进一步转为更具可读性的数据。

 

                 图2.数据类型对照

毕竟是要和C语言的数据类型对照的,ctypes充当着一个映射的作用。

('ihl', c_ubyte, 4),ihl就是变量名,c_ubyte是变量的类型,对应C语言的unsigned char,python的int/long;4是比特位标志。

 u_char ip_hl:4;这个是C语言的格式。

不太懂ctypes到底如何映射这些数据类型,用就完了。。。

30行:self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

   protocol_map变量是一个字典,里面是数字和对应协议类型的对照关系。这个在RFC791(好像是这个)有提到,哪个数字代表哪种协议(自己找吧)。

35行:self.protocol = self.protocol_map[self.protocol_num]

   这个就是根据数据包里的协议号来判断是哪种协议。

31行:self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))

   struct.pack将c_long类型的src(源地址)转为小端的long类型数据,返回源地址的bytes格式。(<号的意思看下表)

    

 

     socket.inet_ntoa()函数则把网络地址,即src,转为以"."分割的字符串,其实就是IP地址的点分十进制形式啦。

 

好玩的来了,运行看看:

1 Protocol:ICMP 192.168.0.101 -> 192.168.0.102
2 Protocol:ICMP 192.168.0.102 -> 192.168.0.101
3 Protocol:UDP 192.168.0.101 -> 230.0.0.1
4 Protocol:UDP 192.168.0.101 -> 230.0.0.1
5 Protocol:TCP 13.107.42.12 -> 192.168.0.101
6 Protocol:TCP 118.212.233.223 -> 192.168.0.101
7 Protocol:TCP 118.212.233.223 -> 192.168.0.101

 

就截取了一部分,看到的是传输层和网络层的协议类型,看不出来应用层是DNS还是HTTP,FTP啥的。Linux下的没有试,按书上的话只能看到ICMP协议的数据包。

不知道怎么分析数据包,这我也看不出啥。。。

 


 接着继续解码ICMP

 代码清单3

 1 # -*- coding=utf-8 -*-
 2 import socket
 3 import os
 4 import struct
 5 from ctypes import *
 6 
 7 # 监听的主机
 8 host = '192.168.0.101'
 9 
10 
11 class IP(Structure):
12     _fields_ = [
13         ('ihl', c_ubyte, 4),
14         ('version', c_ubyte, 4),
15         ('tos', c_ubyte),
16         ('len', c_ushort),
17         ('id', c_ushort),
18         ('offset', c_ushort),
19         ('ttl', c_ubyte),
20         ('protocol_num', c_ubyte),
21         ('sum', c_ushort),
22         ('src', c_ulong),
23         ('dst', c_ulong)
24     ]
25 
26     def __new__(self, socket_buffer=None):
27         return self.from_buffer_copy(socket_buffer)
28 
29     def __init__(self, socket_buffer=None):
30         self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
31         self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
32         self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
33 
34         try:
35             self.protocol = self.protocol_map[self.protocol_num]
36         except:
37             self.protocol = str(self.protocol_num)
38 
39 
40 class ICMP(Structure):
41     _fields_ = [
42         ('type', c_ubyte),
43         ('code', c_ubyte),
44         ('checksum', c_ushort),
45         ('unused', c_ushort),
46         ('next_hop_mtu', c_ushort)
47     ]
48 
49     def __new__(self, socket_buffer):
50         return self.from_buffer_copy(socket_buffer)
51 
52     def __init__(self, socket_buffer):
53         pass
54 
55 
56 if os.name == 'nt':
57     socket_protocol = socket.IPPROTO_IP
58 else:
59     socket_protocol = socket.IPPROTO_ICMP
60 
61 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
62 
63 sniffer.bind((host, 0))
64 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
65 
66 # 打开混杂端口
67 if os.name == 'nt':
68     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
69 
70 # 循环侦听
71 try:
72     while True:
73         # 读取数据包
74         raw_buffer = sniffer.recvfrom(65565)[0]
75         # 将缓冲区的前20字节按IP头进行解析
76         ip_header = IP(raw_buffer[0:20])
77         # 输出双方IP地址
78         if ip_header.protocol == 'ICMP':
79             # 计算ICMP包的起始位置
80             offset = ip_header.ihl * 4
81             buf = raw_buffer[offset:offset + sizeof(ICMP)]
82 
83             # 解析ICMP数据
84             icmp_header = ICMP(buf)
85 
86             print("ICMP:{0}->{1}".format(ip_header.src_address, ip_header.dst_address))
87             print("type:{0} code:{1}".format(icmp_header.type, icmp_header.code))
88 
89 # 处理CTRL-C
90 except KeyboardInterrupt:
91     # 关闭混杂模式
92     if os.name == 'nt':
93         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

 

 相比于代码清单2,多了一个ICMP类,用来解析ICMP的数据。

 

 IP和ICMP协议都是网络层的协议,ICMP属于IP的一部分。看图。

要对ICMP协议部分的数据进行解码,就得知道ICMP部分的数据从哪开始,到哪结束。

从哪开始呢?

ICMP报文位于IP数据包的数据部分,所以得知道IP数据报的首部长度。在IP头部中,有个头部长度,C语言表示为u_char ip_hl:4;它表示IP首部中占32BIT的数目,如果有选项的话,也会包含选项的长度。

那么只要ip_hl * 4(4是4Bytes=32比特)就可以知道IP首部的长度了,也就是ICMP报文的起始位置了。

 到哪结束呢?

 

 类型1字节,代码1字节,校验和2字节,首部其它部分(取决于ICMP的类型)4字节。所以ICMP头部为8字节。

参考代码41-47行

代码81行:buf = raw_buffer[offset:offset + sizeof(ICMP)]

     取得ICMP报文头部的数据,sizeof(ICMP)其实就是_fields_结构的大小(理解成结构体就好了)。

 然后为了简洁点,只打印ICMP相关的数据,测试结果:

ICMP:192.168.0.101->192.168.0.102
type:8 code:0
ICMP:192.168.0.102->192.168.0.101
type:0 code:0
ICMP:192.168.0.101->192.168.0.102
type:8 code:0
ICMP:192.168.0.102->192.168.0.101
type:0 code:0
ICMP:192.168.0.101->192.168.0.102
type:8 code:0
ICMP:192.168.0.102->192.168.0.101
type:0 code:0
ICMP:192.168.0.101->192.168.0.102
type:8 code:0
ICMP:192.168.0.102->192.168.0.101
type:0 code:0

 

从百科copy来的表:

TYPE CODE Description Query Error
0 0 Echo Reply——回显应答(Ping应答) x  
3 0 Network Unreachable——网络不可达   x
3 1 Host Unreachable——主机不可达   x
3 2 Protocol Unreachable——协议不可达   x
3 3 Port Unreachable——端口不可达   x
3 4 Fragmentation needed but no frag. bit set——需要进行分片但设置不分片比特   x
3 5 Source routing failed——源站选路失败   x
3 6 Destination network unknown——目的网络未知   x
3 7 Destination host unknown——目的主机未知   x
3 8 Source host isolated (obsolete)——源主机被隔离(作废不用)   x
3 9 Destination network administratively prohibited——目的网络被强制禁止   x
3 10 Destination host administratively prohibited——目的主机被强制禁止   x
3 11 Network unreachable for TOS——由于服务类型TOS,网络不可达   x
3 12 Host unreachable for TOS——由于服务类型TOS,主机不可达   x
3 13 Communication administratively prohibited by filtering——由于过滤,通信被强制禁止   x
3 14 Host precedence violation——主机越权   x
3 15 Precedence cutoff in effect——优先中止生效   x
4 0 Source quench——源端被关闭(基本流控制)    
5 0 Redirect for network——对网络重定向    
5 1 Redirect for host——对主机重定向    
5 2 Redirect for TOS and network——对服务类型和网络重定向    
5 3 Redirect for TOS and host——对服务类型和主机重定向    
8 0 Echo request——回显请求(Ping请求) x  
9 0 Router advertisement——路由器通告    
10 0 Route solicitation——路由器请求    
11 0 TTL equals 0 during transit——传输期间生存时间为0   x
11 1 TTL equals 0 during reassembly——在数据报组装期间生存时间为0   x
12 0 IP header bad (catchall error)——坏的IP首部(包括各种差错)   x
12 1 Required options missing——缺少必需的选项   x
13 0 Timestamp request (obsolete)——时间戳请求(作废不用) x  
14   Timestamp reply (obsolete)——时间戳应答(作废不用) x  
15 0 Information request (obsolete)——信息请求(作废不用) x  
16 0 Information reply (obsolete)——信息应答(作废不用) x  
17 0 Address mask request——地址掩码请求 x  
18 0 Address mask reply——地址掩码应答  

看code和type字段就知道这个数据包在干嘛了。

 


 

 

扫描网段啦

 之前都只是在抓包,得要自己手动去ping别人,现在要一次性扫描多个主机了。

书上是只打印端口不可达的情况,不过我这边的其它主机都可以ping通呢!所以,我打算把所有类型的ICMP消息都打印下,试试吧。

代码清单4:

呵呵,不知道是不是我写错了,按书上那个发包函数,都收不到ICMP消息呢!网上找了一大堆都是python2写的,还都可以用,真的是我这的问题吗,也没什么太大差别吧?

 

  1 # -*- coding=utf-8 -*-
  2 import socket
  3 import os
  4 import struct
  5 from ctypes import *
  6 import threading
  7 import time
  8 from netaddr import IPNetwork, IPAddress
  9 
 10 # 监听的主机
 11 host = '192.168.0.101'
 12 
 13 # 要扫描的目标子网
 14 subnet = '192.168.0.0/24'
 15 
 16 # 待会要发送的消息
 17 my_message = b"WALL"
 18 
 19 
 20 class IP(Structure):
 21     _fields_ = [
 22         ('ihl', c_ubyte, 4),
 23         ('version', c_ubyte, 4),
 24         ('tos', c_ubyte),
 25         ('len', c_ushort),
 26         ('id', c_ushort),
 27         ('offset', c_ushort),
 28         ('ttl', c_ubyte),
 29         ('protocol_num', c_ubyte),
 30         ('sum', c_ushort),
 31         ('src', c_ulong),
 32         ('dst', c_ulong)
 33     ]
 34 
 35     def __new__(self, socket_buffer=None):
 36         return self.from_buffer_copy(socket_buffer)
 37 
 38     def __init__(self, socket_buffer=None):
 39         self.protocol_map = {1: "ICMP", 2: "IGMP", 6: "TCP", 17: "UDP"}
 40         self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
 41         self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))
 42 
 43         try:
 44             self.protocol = self.protocol_map[self.protocol_num]
 45         except:
 46             self.protocol = str(self.protocol_num)
 47 
 48 
 49 class ICMP(Structure):
 50     _fields_ = [
 51         ('type', c_ubyte),
 52         ('code', c_ubyte),
 53         ('checksum', c_ushort),
 54         ('unused', c_ushort),
 55         ('next_hop_mtu', c_ushort)
 56     ]
 57 
 58     def __new__(self, socket_buffer):
 59         return self.from_buffer_copy(socket_buffer)
 60 
 61     def __init__(self, socket_buffer):
 62         pass
 63 
 64 
 65 #批量发送UDP数据包
 66 def udp_sender(subnet, my_message):
 67     time.sleep(2)
 68     sender = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
 69 
 70     for ip in IPNetwork(subnet):
 71         try:
 72             print("#Scan {0}".format(ip))
 73             sender.sendto(my_message, ("%s" % ip,0))
 74         except:
 75             pass
 76 
 77 
 78 if os.name == 'nt':
 79     socket_protocol = socket.IPPROTO_IP
 80 else:
 81     socket_protocol = socket.IPPROTO_ICMP
 82 
 83 sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
 84 
 85 sniffer.bind((host, 0))
 86 sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
 87 
 88 # 打开混杂端口
 89 if os.name == 'nt':
 90     sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
 91 
 92 # 启动发送数据包的线程
 93 t = threading.Thread(target=udp_sender, args=(subnet, my_message))
 94 t.start()
 95 
 96 # 循环侦听
 97 try:
 98     while True:
 99         # 读取数据包
100         raw_buffer, address = sniffer.recvfrom(65565)
101         # 将缓冲区的前20字节按IP头进行解析
102         ip_header = IP(raw_buffer[0:20])
103         if ip_header.protocol == 'ICMP':
104             print('#From {0}:{1}'.format(address[0], address[1]))
105             # 计算ICMP包的起始位置
106             offset = ip_header.ihl * 4
107             buf = raw_buffer[offset:offset + sizeof(ICMP)]
108 
109             # 解析ICMP数据
110             icmp_header = ICMP(buf)
111 
112             print("ICMP:{0}->{1}".format(ip_header.src_address, ip_header.dst_address))
113             print("type:{0} code:{1}".format(icmp_header.type, icmp_header.code))
114 
115             # 判断发送来的数据包的源地址位于我们扫描的子网内
116             if icmp_header.type == 3 and icmp_header.code == 3:
117                 if IPAddress(ip_header.src_address) in IPNetwork(subnet):
118                     # 确认ICMP中包含我们发的自定义消息
119                     if raw_buffer[len(raw_buffer) - len(my_message):] == my_message:
120                         print('Host up:{0}'.format(ip_header.src_address))
121 # 处理CTRL-C
122 except KeyboardInterrupt:
123     # 关闭混杂模式
124     if os.name == 'nt':
125         sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

 

 这个子网扫描的没成功,ICMP数据包就是没成功发送给目标主机过,type:3,code:1主机不可达,呵呵。

折腾了半天,这个部分没弄好。。。

 留了一个坑。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM