那麼,就寫一隻Python Sniffer吧!
上次寫Sniffer時,應該是大學二年級的時候了吧那時候, wireshark 還是叫作 Ethereal 呢
當時是為了做專題需要
用 winpcap 提供的範例,自已開網卡等等等
別再回想了,一想到 MFC,我胃就痛了…
用Python寫Sniffer快多了
麻煩的地方大概是
- 使用RAW Socket,需要 root 權限
- WIN OS RAW Socket只能從IP層開始,大概要安裝其它pcap lib才能解決這問題
原始碼
# -*- encoding: utf-8 -*- import binascii import socket import struct import sys def arp_parser(packet): arp_length = 28 # fixed arp = struct.unpack("!2s2s1s1s2s6s4s6s4s", packet[0:arp_length]) print ("ARP Header :") print (" |_ SHA: {0} -> THA: {1}".format(binascii.hexlify(arp[5]), binascii.hexlify(arp[7]))) print (" |_ SPA: {0} -> TPA: {1}".format(socket.inet_ntoa(arp[6]), socket.inet_ntoa(arp[8]))) print (" |_ HTYPE : {0}".format(binascii.hexlify(arp[0]))) print (" |_ PTYPE : {0}".format(binascii.hexlify(arp[1]))) print (" |_ HLEN : {0}".format(binascii.hexlify(arp[2]))) print (" |_ PLEN : {0}".format(binascii.hexlify(arp[3]))) print (" |_ OPER : {0}".format(binascii.hexlify(arp[4]))) #print (" |_ SHA : {0}".format(binascii.hexlify(arp[5]))) #print (" |_ SPA : {0}".format(socket.inet_ntoa(arp[6]))) #print (" |_ THA : {0}".format(binascii.hexlify(arp[7]))) #print (" |_ TPA : {0}".format(socket.inet_ntoa(arp[8]))) # Padding dump packet[arp_length:] print (" |_ Data : {0}".format(binascii.hexlify(packet[arp_length:]))) def icmp_parser(packet): icmp_length = 4 icmp = struct.unpack("!BBH", packet[0:icmp_length]) print ("ICMP Header :") print (" |_ Type : {0}".format(icmp[0])) print (" |_ Code : {0}".format(icmp[1])) print (" |_ Checksum : {0} ({1})".format(icmp[2], hex(icmp[2]))) # Padding dump packet[icmp_length:] print (" |_ Data : {0}".format(binascii.hexlify(packet[icmp_length:]))) def tcp_parser(packet): tcp_length = 20 tcp = struct.unpack("!HHLLBBHHH", packet[0:tcp_length]) # Real tcp header length tcp_length = (tcp[4] >> 4) * 4 Flags = ((tcp[4] & 0x0f) << 8) + tcp[5] print ("TCP Header :") print (" |_ Src Port: {0} -> Dst Port: {1}".format(tcp[0], tcp[1])) print (" |_ Sequence : {0} ({1})".format(tcp[2], hex(tcp[2]))) print (" |_ Acknowledgment : {0} ({1})".format(tcp[3], hex(tcp[3]))) print (" |_ Length : {0}".format(tcp_length)) print (" |_ Flags : {0}".format(hex(Flags))) print (" |_ Window size : {0}".format(tcp[6])) print (" |_ Checksum : {0} ({1})".format(tcp[7], hex(tcp[7]))) print (" |_ Urgent pointer : {0}".format(tcp[8])) # Padding dump packet[icmp_length:] print (" |_ Data : {0}".format(binascii.hexlify(packet[tcp_length:]))) def udp_parser(packet): udp_length = 8 udp = struct.unpack("!HHHH", packet[0:udp_length]) print ("UDP Header :") print (" |_ Src Port: {0} -> Dst Port: {1}".format(udp[0], udp[1])) print (" |_ Length : {0}".format(udp[2])) print (" |_ Checksum : {0} ({1})".format(udp[3], hex(udp[3]))) # Padding dump packet[icmp_length:] print (" |_ Data : {0}".format(binascii.hexlify(packet[udp_length:]))) def ip_parser(packet): ip_length = 20 ip = struct.unpack("!BBHHHBBH4s4s", packet[0:ip_length]) version = ip[0] >> 4 IHL = (ip[0] & 0xf) * 4 DiffServ = ip[1] >> 2 ECN = ip[1] & 0x03 Flags = ip[4] >> 13 Frag_offset = ip[4] & 0x1fff print ("IP Header :") print (" |_ From: {0} -> To: {1}".format(socket.inet_ntoa(ip[8]), socket.inet_ntoa(ip[9]))) print (" |_ Version : {0}".format(version)) print (" |_ Header Length : {0}".format(IHL)) print (" |_ DiffServ : {0}".format(DiffServ)) print (" |_ ECN : {0}".format(ECN)) print (" |_ Total Length : {0}".format(ip[2])) print (" |_ Identification : {0} ({1})".format(ip[3], hex(ip[3]))) print (" |_ Flags : {0}".format(Flags)) print (" |_ Fragment Offset: {0}".format(Frag_offset)) print (" |_ TTL : {0}".format(ip[5])) print (" |_ Protocol : {0}".format(hex(ip[6]))) print (" |_ Checksum : {0} ({1})".format(ip[7], hex(ip[7]))) # next header if ip[6] == 1: # ICMP = 0x01 icmp_parser(packet[IHL:]) elif ip[6] == 6: # TCP = 0x06 tcp_parser(packet[IHL:]) elif ip[6] == 17: # UDP = 0x11 udp_parser(packet[IHL:]) else: pass def ethernet_parser(packet): eth_length = 14 # fixed eth = struct.unpack("!6s6s2s", packet[0:eth_length]) print ("ETHERNET Header :") print (" |_ From: {1} -> To: {0}".format(binascii.hexlify(eth[0]), binascii.hexlify(eth[1]))) print (" |_ Type: {0}".format(binascii.hexlify(eth[2]))) # parser next header if eth[2] == b'\x08\x06': arp_parser(packet[eth_length:]) elif eth[2] == b'\x08\x00': ip_parser(packet[eth_length:]) else: pass def linux_main(): rawSocket = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(0x0003)) # Bind the interface, likes eth0 # rawSocket.bind(("eno50332184", 0)) while True: packet = rawSocket.recvfrom(2048)[0] ethernet_parser(packet) print ("") rawSocket.close() def windows_main(): # create a raw socket and bind it to the public interface rawSocket = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP) # Bind a interface with public IP HOST = socket.gethostbyname(socket.gethostname()) rawSocket.bind((HOST, 0)) print ("Bind a interface : {0}".format(HOST)) rawSocket.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) # Include IP headers rawSocket.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) # receive all packages while True: packet = rawSocket.recvfrom(2048)[0] ip_parser(packet) print ("") rawSocket.close() if __name__ == '__main__': if sys.platform.lower().startswith("win"): windows_main() else: linux_main()
這隻程式在WinOS, Linus皆可運作
在 Python 2.7, Python 3.5 可正常執行
您好,拜讀了您的大作,獲益良多
回覆刪除紙是想請問,為什麼執行了這個程式,結果只能看到自己送出去的封包呢?
我有重新驗證過,在 Linux 環境是正常的;而在 Windows 環境時,只有 ICMP 是正常的
刪除所以我猜想您應該是在 Windows 環境執行這個程式的
因為 Windows 在 XP SP1 之後,有針對 RAW Socking 有增加一些限制 (防止 DOS)
雖然我沒有測試過,若是不使用 RAW Socking 的話,也許就會正常了
如果你仍然需要 RAW Socking 的話,我還有其它的建議可以試試看
1. 改用 C/C++ 撰寫,使用 Winpcap lib
2. 可以試其它的 Python 套件,如 PyPcap or Scapy,去實現你的需求
https://www.winpcap.org/
https://github.com/pynetwork/pypcap
https://github.com/zlorb/scapy