網絡流量嗅探


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