標籤:python packet嗅探 raw-socket 乙太網路幀解析
1. Raw Socket基礎
提供了一種方法來繞過整個網路堆棧遍曆和直接將乙太網路幀輸送到一個應用程式。
有很多種方法來建立raw sockets,例如AF_PACKET,PF_PACKET。這裡使用PF_PACKET,它是linux系統上才有的選項,如果是windows或者是mac的系統的話,可以使用AF_PACKET。
1.1 PF_SOCKET
- 在鏈路層接收和發送包得應用介面。
- 所有接收到的包都包含完整的頭部和資料部分。
- 所有發送的包都會由核心無更改的傳遞到媒介中。
- 支援使用Berkley Packet 過濾。(更詳細資料可參考:http://howtounix.info/man/FreeBSD/man4/bpf.4)
2. 建立Raw Socket2.1 理解Packet Header
為乙太網路幀頭部和IP頭部格式:
乙太網路幀的頭部有14位元組,前6位元組為目的mac地址,後6位元組為源mac地址,之後2位元組為內部協議的類型,比如IP協議的類型為0x0800
。關於各種協議的類型可在/usr/include/linux/if_ether.h
檔案中查看(linux系統)。往裡一層是IP等網路層協議,IP層內部包含TCP、UDP等傳輸層協議,再往裡就是應用程式層協議,如HTTP、ssh等。
圖中最下側是IP的頭部格式,最重要的部分是前20位元組。
2.2 提取位元據到變數中
- 使用struct模組中的unpack()函數。
- 返回的時tuple格式。(tuple的更詳細內容可參考:廖雪峰講的python中tuple部分)
- 第一個字元標明位元組序。
舉個例子, 終端下輸入python,然後進行一下操作:
>>> import struct
>>>
>>>
>>> struct.pack(“B”,1)
‘\x01’
>>> struct.pack(“H”,1)
‘\x01\x00’
>>> struct.pack(“>H”,1)
‘\x00\x01’
>>> struct.pack(“!H”,1)
‘\x00\x01’
>>> struct.pack(“!L”,1)
‘\x00\x00\x00\x01’
>>> exit()
2.3 raw Socket嗅探
主要思路:
使用Socket模組。
讀取接收到的包。
解釋和分析包。
也可以發送應答
linux下開啟終端,進入python環境(raw_socket需要root許可權,輸入命令是要以sudo python
命令進入。),依次按一下方式操作:
[email protected]:~/workspace/python$ sudo python
[sudo] password for jeanphorn:
Python 2.7.3 (default, Apr 20 2012, 22:44:07)
[GCC 4.6.3] on linux2
Type “help”, “copyright”, “credits” or “license” for more information.
>>>
>>>
>>>
>>> import socket
>>> import struct
>>> import binascii
>>>
>>>
>>> rawSocket = socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))
>>>
>>> pkt = rawSocket.recvfrom(2048)
>>> pkt
(“\x08\x00’[email protected]\x8f.\xd5\xc4\x08\x00E\x10\x004W\[email protected]\[email protected]\x06_\xf0\xc0\xa8\x01_\xc0\xa8\x01\x0e\xf0u\x00\x16\xa9^i\x03\xe3\xa0V\xb4\x80\x10\x0f\xfeGr\x00\x00\x01\x01\x08\n\x1e\xd3\x0f\xc7\x00\x00/\xb2”, (‘eth0’, 2048, 0, 1, ‘@l\x8f.\xd5\xc4’))
>>> ethernetHeader = pkt[0][0:14]
>>>ethernetHeader
“\x08\x00’[email protected]\x8f.\xd5\xc4\x08\x00”
>>> eth_hdr = struct.unpack(“!6s6s2s”,ethernetHeader)
>>> eth_hdr = struct.unpack(“!6s6s2s”,ethernetHeader)
>>> eth_hdr
(“\x08\x00’5BY”, ‘@l\x8f.\xd5\xc4’, ‘\x08\x00’)
>>> binascii.hexlify(eth_hdr[0])
‘080027354259’
>>> binascii.hexlify(eth_hdr[1])
‘406c8f2ed5c4’
>>> binascii.hexlify(eth_hdr[2])
‘0800’
>>> ipHeader = pkt[0][14:34]
>>> pkt
(“\x08\x00’[email protected]\x8f.\xd5\xc4\x08\x00E\x10\x004W\[email protected]\[email protected]\x06_\xf0\xc0\xa8\x01_\xc0\xa8\x01\x0e\xf0u\x00\x16\xa9^i\x03\xe3\xa0V\xb4\x80\x10\x0f\xfeGr\x00\x00\x01\x01\x08\n\x1e\xd3\x0f\xc7\x00\x00/\xb2”, (‘eth0’, 2048, 0, 1, ‘@l\x8f.\xd5\xc4’))
>>> ip_hdr = struct.unpack(“!12s4s4s”,ipHeader)
>>> ip_hdr
(‘E\x10\x004W\[email protected]\[email protected]\x06_\xf0’, ‘\xc0\xa8\x01_’, ‘\xc0\xa8\x01\x0e’)
>>> socket.inet_ntoa(ip_hdr[1])
‘192.168.1.95’
>>> socket.inet_ntoa(ip_hdr[2])
‘192.168.1.14’
>>> tcpHeader = pkt[0][34:54]
>>> tcp_hdr = struct.unpack(“!HH16s”,tcpHeader)
>>> tcp_hdr
(61557, 22, ‘\xa9^i\x03\xe3\xa0V\xb4\x80\x10\x0f\xfeGr\x00\x00’)
3. 完整的python代碼
將以上代碼寫在一個packetSniffer.py的檔案中,運行時要加上sudo ./packetSniffer.py
。
#!/usr/bin/env python# _*_ coding=utf-8 _*_import socketimport structimport binasciirawSocket = socket.socket(socket.PF_PACKET,socket.SOCK_RAW,socket.htons(0x0800))pkt = rawSocket.recvfrom(2048)ethernetHeader = pkt[0][0:14] #提取乙太網路幀頭eth_hdr = struct.unpack("!6s6s2s",ethernetHeader) #6位元組目的mac地址,6位元組源mac地址,2位元組協議類型binascii.hexlify(eth_hdr[0])binascii.hexlify(eth_hdr[1])binascii.hexlify(eth_hdr[2])ipHeader = pkt[0][14:34] #提取IP協議頭,不包含option和padding欄位。ip_hdr = struct.unpack("!12s4s4s",ipHeader) # !標示轉換網路位元組序,前12位元組為版本、頭部長度、服務類型、總長度、標誌等其他選項,後面的兩個四位元組依次為源IP地址和目的IP地址。print "source IP address: " + soket.inet_ntoa(ip_hdr[1])print "destination IP address: " + soket.inet_ntoa(ip_hdr[2])tcpHeader = pkt[0][34:54]tcp_hdr = struct.unpack("!HH16s",tcpHeader)print tcp_hdr
python 利用Raw Socket進行乙太網路幀嗅探