代碼排版搞不定,強迫症請移步微信訂閱号。
Python黑帽編程 4.1 Sniffer(嗅探器)之數據捕獲(上)
網絡嗅探,是監聽流經本機網卡數據包的一種技術,嗅探器就是利用這種技術進行數據捕獲和分析的軟件。
編寫嗅探器,捕獲數據是前置功能,數據分析要建立在捕獲的基礎上。本節就數據捕獲的基本原理和編程實現做詳細的闡述。
4.1.1 以太網網卡的工作模式
以太網網卡是我們日常生活中見得最多的網卡,我們的電腦通過網線或者wifi接入網絡,使用的都是以太網網卡。
圖3
通過命令
ifconfig
eth0 promisc
可以将eth0設置為混雜模式。
圖4
圖四中圈紅的部分,表示當前網卡處于混雜模式。
通過ifconfig
eth0 -promisc
可以取消網卡的混雜模式。
圖5
ifconfig同樣适用于無線網卡。
4.1.3 無線網卡的監聽模式
對于無線網卡,我們可以使用iwconfig的mode參數來配置混雜模式,mode的選項值如下:
1)Ad-hoc:不帶AP的點對點無線網絡
2)Managed:通過多個AP組成的網絡,無線設備可以在這個網絡中漫遊
3)Master:設置該無線網卡為一個AP
4)Repeater:設置為無線網絡中繼設備,可以轉發網絡包
5)Secondary:設置為備份的AP/Repeater
6)Monitor:監聽模式
7)Auto:由無線網卡自動選擇工作模式
使用如下命令可以設置無線網卡為監聽模式:
ifconfig wlan0 down
iwconfig wlan0 mode monitor
ifconfig wlan0 up
在Kali中我們通過iwconfig來設置混雜模式,可能會遇到點困難,無線網卡設置成混雜模式後,過幾秒又變成manage模式了。這是由于Network Manage服務造成,我們可以關閉該服務。
監聽模式和上文的混雜模式有什麼區别呢?混雜模式是在wifi連接到指定網絡中,監聽子網中的數據傳輸;監聽模式下wifi會斷網,進而監聽某一個信道内所有傳輸流量,因此可以用來掃描wifi熱點,破解wifi密碼等工作。
下面我們來看一下如何編程實現Sniffer。
4.1.4 可以在WINDOWS上運行的SNIFFER
Raw socket是一種較為底層的socket編程接口,可以用來獲取IP層以上的數據,所以可以用來編寫Sniffer。一個完整的sniffer代碼組成,大緻分為創建socket對象,接收數據,分析數據三個部分。其中開啟網卡的混雜模式,需要配置socket對象的屬性。在開啟混雜模式方面,Linux上要比windows上複雜一點,我們先從簡單的情況開始。
首先我們定義出程序的基本框架。
在上面的代碼中,我們首先定義了一個類——PromiscuousSocket,這個類負責創建一個綁定到當前主機名綁定的網卡上的raw socket對象,并設置啟動混雜模式。PromiscuousSocket類有三個方法,分别為類的構造函數,另外兩個函數是用于with關鍵字的塊作用域的起止函數,不了解的同學請翻閱Python的編程基礎資料看一下。sniffer函數會創建PromiscuousSocket類的實例,并使用它接收和分析數據。printPacket方法用來顯示捕獲的數據内容。
接下來我們來完善核心的PromiscuousSocket類,在__init__方法中,我們創建socket對象,并綁定到對象的s字段上。
def __init__(self):
#創建socket
HOST = socket.gethostbyname(socket.gethostname())
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, 0))
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
self.s = s
這段代碼首先創建一個socket對象,第一個字段family我們選擇ipv4;第二個字段type,選擇raw socket。(這裡關于socket編程的基礎内容,如果你不是很理解,可以先看一看本教程的2.8節。)
setsockopt函數是用來對socket對象進行補充選項的設置,三個參數的分别為level、選項名稱和值。
level支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
可用的socket層選項名字如下:
協議層選項名字
SOL_SOCKET SO_REUSEADDR
SOL_SOCKET SO_KKEPALIVE
SOL_SOCKET SO_LINGER
SOL_SOCKET SO_BROADCAST
SOL_SOCKET SO_OOBINLINE
SOL_SOCKET SO_SNDBUF
SOL_SOCKET SO_RCVBUF
SOL_SOCKET SO_TYPE
SOL_SOCKET SO_ERROR
代碼中我們使用了SOL_SOCKET 的SO_REUSEADDR
選項,該選項可以讓多個socket對象綁定到相同的地址和端口上。
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
設置了該選項之後,我們調用bind方法,來綁定socket。
s.bind((HOST, 0))
接下來我們再次通過setsockopt函數來設置數據保護IP頭部。
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
最後,通過ioctl函數類設置混雜模式,注意傳入的兩個參數,第一個指定設置的類型為接收所有數據,第二個參數要個第一個對應,使用RCVALL_ON來開啟。
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
我們再來看完善的__enter__函數。
def __enter__(self):
return self.s
代碼很簡單,返回創建的socket對象。
__exit__方法中,我們調用ioctl方法通過RCVALL_OFF來關閉混雜模式。代碼如下:
def __exit__(self, *args, **kwargs):
self.s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
完善的sniff方法如下:
def sniffer(count, bufferSize=65565, showPort=False, showRawData=False):
with PromiscuousSocket() as s:
for i in range(count):
# receive a package
package = s.recvfrom(bufferSize)
printPacket(package, showPort, showRawData)
sniff方法利用PromiscuousSocket的一個實例,接收數據包,然後調用printPacket方法打印基本信息。
def printPacket(package, showPort, showRawData):
dataIndex = 0
headerIndex = 1
ipAddressIndex = 0
portIndex = 1
print('IP:', package[headerIndex][ipAddressIndex], end=' ')
if(showPort):
print('Port:', package[headerIndex][portIndex], end=' ')
print('') #newline
if(showRawData):
print('Data:', package[dataIndex])
printPacket方法接收數據包對象,打印對應信息。這裡不用過多解釋,傳入的package對象作為二維數組被解析,通過調試可以知道數據包裡面的内容,從而進一步調整程序。
4.1.5 解決LINUX上混雜模式問題
至此,一個簡單 的嗅探程序就完成了,在windows上可以運行無誤了。不過在linux上會遇到問題,在設置混雜模式的代碼:
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
Python并沒有将SIO_RCVALL和RCVALL_ON及RCVALL_OFF暴露出來。但是系統底層的C結構體是有這樣的定義的,這裡我們通過fcntl模塊的fcntl對象的ioctl方法來配置選項。這裡面涉及一個Python編程中python對象和C 類型轉換的知識點,我這裡就不展開了,不太知道的同學請自行查找資料解決。
這裡我們先将要用到的數值封裝到類FLAGS中。
class FLAGS(object):
# linux/if_ether.h
ETH_P_ALL = 0x0003 # 所有協議
ETH_P_IP = 0x0800 # 隻處理IP層
# linux/if.h,混雜模式
IFF_PROMISC = 0x100
# linux/sockios.h
SIOCGIFFLAGS = 0x8913 # 獲取标記值
SIOCSIFFLAGS = 0x8914 # 設置标記值
然後創建一個ifreq類,如下:
class ifreq(ctypes.Structure):
_fields_ = [("ifr_ifrn", ctypes.c_char * 16),
("ifr_flags", ctypes.c_short)]
該類繼承自ctypes.Structure類,使用它我們可以通過字符串中轉c結構體字段的值。
下面我們看如何使用FLAGS和ifreq類。
在PromiscuousSocket類初始化socket的代碼部分,我們增加下面的代碼。
if os.name == 'posix':
import fcntl # posix-only
s = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(FLAGS.ETH_P_ALL))
ifr = ifreq()
ifr.ifr_ifrn = b'eth0' #此處注意,這裡寫死了網卡名稱,需要根據實際情況修改或者傳入
fcntl.ioctl(s, FLAGS.SIOCGIFFLAGS, ifr) # 獲取标記字段的名稱
ifr.ifr_flags |= FLAGS.IFF_PROMISC # 添加混雜模式的值
fcntl.ioctl(s, FLAGS.SIOCSIFFLAGS, ifr) # 更新
self.ifr = ifr
上面的代碼中,注意幾個地方。htons方法用來将16bit的正數的字節順序轉換為網絡傳輸的順序(所謂的大端,小端,不了解的請google之)。我們創建了一個ifreq類的實例 ifr,接下來設置綁定的網卡的名字,這裡程序寫死了,需要根據實際情況調整。通過
fcntl.ioctl(s, FLAGS.SIOCGIFFLAGS, ifr) # 獲取标記字段的名稱
将當前socket已經有的Flag獲取到,然後加上設置混雜模式的數值,在通過
fcntl.ioctl(s, FLAGS.SIOCSIFFLAGS, ifr) # 更新
更新給socket對象,從而使該socket具有獲取所有數據的能力。
在__exit__方法中,取消混雜模式的代碼我們也要修改一下:
def __exit__(self, *args, **kwargs):
if os.name == 'posix':
import fcntl
self.ifr.ifr_flags ^= FLAGS.IFF_PROMISC
fcntl.ioctl(self.s, FLAGS.SIOCSIFFLAGS, self.ifr)
else:
self.s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
這段代碼就不必再解釋了,根據上面的說明應該看得明白。
4.1.6 小結
到此為止,我們基于raw socket實現的嗅探器就完成了,實現我們捕獲數據的目的。此種方法,需要大家對操作系統本身對網絡協議棧的描述,有較為深入的理解。下一節,我們讓這個過程變得輕松一點,使用一些流行的網絡庫來實現Sniffer。
哦,今天是程序員節,給大家送點福利吧,本文的完整的源碼,當然是人人有份了;另外送一本Python網絡編程相關的書,隻有一本哦。活動參與的方法如下:
1)如果你是在微信中閱讀本文,請選擇打賞或者轉發截圖回複,我會講源碼發給你。
2)在打賞的人中,我會随機抽取一個人,送書一本(隻限10月14日當晚有效哦)。
3)如果你不是在微信中閱讀本文,請移步微信,打開玄魂工作室訂閱号(xuanhun521),回複“python”,打開鍊接,在黑帽欄目下面找到本篇文章(《PYTHON黑帽編程 4.1 SNIFFER(嗅探器)之數據捕獲(上)》),回到1)。
第4.2節《4.1 Sniffer(嗅探器)之數據捕獲(下)》已經在微信訂閱号搶先發布,心急的同學進入訂閱号(二維碼在下方),從菜單“專欄”—>”Python黑帽編程”進入即可。
查看完整系列教程,請關注我的微信訂閱号(xuanhun521,下方二維碼),回複“python”。問題讨論請加qq群:Hacking (1群):303242737 Hacking (2群):147098303。
玄魂工作室-精彩不斷
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!