├── .gitignore ├── Other ├── ethernet_frame.png ├── ip_header_diagram.png ├── notes.txt └── tcp_ip_packet_diagram.jpg ├── README.md ├── general.py ├── networking ├── ethernet.py ├── http.py ├── icmp.py ├── ipv4.py ├── pcap.py ├── tcp.py └── udp.py └── sniffer.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /Other/ethernet_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Python-Packet-Sniffer/50f4e838a900f677685fe4f25c9d8e71a823700a/Other/ethernet_frame.png -------------------------------------------------------------------------------- /Other/ip_header_diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Python-Packet-Sniffer/50f4e838a900f677685fe4f25c9d8e71a823700a/Other/ip_header_diagram.png -------------------------------------------------------------------------------- /Other/notes.txt: -------------------------------------------------------------------------------- 1 | ----- Overview ----- 2 | 3 | Make sure to run as sudo 4 | 5 | 6 | ----- Server ----- 7 | 8 | 1. Socket object is created 9 | 2. Bind socket to IP address and port 10 | 3. Server can now listen/wait for connections 11 | 4. Clients will attempt to connect, server can then accept connections 12 | 13 | Once connection is made, server and client enter request/response (send/receive) loop 14 | When connection is closed, server returns to listening state 15 | 16 | 17 | ----- Resources ----- 18 | 19 | http://www.binarytides.com/python-packet-sniffer-code-linux/ 20 | https://docs.python.org/3.4/library/struct.html#format-characters 21 | 22 | 23 | ----- Tasks ----- 24 | 25 | Parse responses when encoding is unknown. 26 | 27 | Data Size: 597 28 | Encoding: Unknown 29 | 30 | b'HTTP/1.1 200 OK\r\nCache-Control: private, max-age=0, no-cache\r\nContent-Type: image/gif\r\nDate: Wed, 23 Dec 2015 06:15:00 GMT\r\nExpires: Mon, 26 Jul 1997 05:00:00 GMT\r\nP3P: CP="NOI DSP COR CURa ADMa DEVa PSAa PSDa OUR BUS COM INT DEM STA LOC"\r\nPragma: no-cache\r\nServer: Apache\r\nSet-Cookie: _PM_UNIQID=4589a04060e6980cb3e65eb54dd9dfd9; expires=Thu, 22-Dec-2016 06:15:00 GMT; path=/; domain=tentaculos.net\r\nSet-Cookie: _PM_CONV_140505413525=0; expires=Thu, 22-Dec-2016 06:15:00 GMT; path=/; domain=tentaculos.net\r\nContent-Length: 43\r\nConnection: keep-alive\r\n\r\nGIF89a\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00!\xf9\x04\x00\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;' 31 | -------------------------------------------------------------------------------- /Other/tcp_ip_packet_diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buckyroberts/Python-Packet-Sniffer/50f4e838a900f677685fe4f25c9d8e71a823700a/Other/tcp_ip_packet_diagram.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## To Do 2 | 3 | - Interpret HTTP data (tcp.dest_port will be 80) 4 | -------------------------------------------------------------------------------- /general.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | 4 | # Returns MAC as string from bytes (ie AA:BB:CC:DD:EE:FF) 5 | def get_mac_addr(mac_raw): 6 | byte_str = map('{:02x}'.format, mac_raw) 7 | mac_addr = ':'.join(byte_str).upper() 8 | return mac_addr 9 | 10 | 11 | # Formats multi-line data 12 | def format_multi_line(prefix, string, size=80): 13 | size -= len(prefix) 14 | if isinstance(string, bytes): 15 | string = ''.join(r'\x{:02x}'.format(byte) for byte in string) 16 | if size % 2: 17 | size -= 1 18 | return '\n'.join([prefix + line for line in textwrap.wrap(string, size)]) 19 | -------------------------------------------------------------------------------- /networking/ethernet.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import struct 3 | from general import * 4 | 5 | 6 | class Ethernet: 7 | 8 | def __init__(self, raw_data): 9 | 10 | dest, src, prototype = struct.unpack('! 6s 6s H', raw_data[:14]) 11 | 12 | self.dest_mac = get_mac_addr(dest) 13 | self.src_mac = get_mac_addr(src) 14 | self.proto = socket.htons(prototype) 15 | self.data = raw_data[14:] 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /networking/http.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class HTTP: 4 | 5 | def __init__(self, raw_data): 6 | try: 7 | self.data = raw_data.decode('utf-8') 8 | except: 9 | self.data = raw_data 10 | -------------------------------------------------------------------------------- /networking/icmp.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | class ICMP: 5 | 6 | def __init__(self, raw_data): 7 | self.type, self.code, self.checksum = struct.unpack('! B B H', raw_data[:4]) 8 | self.data = raw_data[4:] 9 | -------------------------------------------------------------------------------- /networking/ipv4.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | class IPv4: 5 | 6 | def __init__(self, raw_data): 7 | version_header_length = raw_data[0] 8 | self.version = version_header_length >> 4 9 | self.header_length = (version_header_length & 15) * 4 10 | self.ttl, self.proto, src, target = struct.unpack('! 8x B B 2x 4s 4s', raw_data[:20]) 11 | self.src = self.ipv4(src) 12 | self.target = self.ipv4(target) 13 | self.data = raw_data[self.header_length:] 14 | 15 | # Returns properly formatted IPv4 address 16 | def ipv4(self, addr): 17 | return '.'.join(map(str, addr)) 18 | -------------------------------------------------------------------------------- /networking/pcap.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import time 3 | 4 | 5 | class Pcap: 6 | 7 | def __init__(self, filename, link_type=1): 8 | self.pcap_file = open(filename, 'wb') 9 | self.pcap_file.write(struct.pack('@ I H H i I I I', 0xa1b2c3d4, 2, 4, 0, 0, 65535, link_type)) 10 | 11 | def write(self, data): 12 | ts_sec, ts_usec = map(int, str(time.time()).split('.')) 13 | length = len(data) 14 | self.pcap_file.write(struct.pack('@ I I I I', ts_sec, ts_usec, length, length)) 15 | self.pcap_file.write(data) 16 | 17 | def close(self): 18 | self.pcap_file.close() 19 | -------------------------------------------------------------------------------- /networking/tcp.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | class TCP: 5 | 6 | def __init__(self, raw_data): 7 | (self.src_port, self.dest_port, self.sequence, self.acknowledgment, offset_reserved_flags) = struct.unpack( 8 | '! H H L L H', raw_data[:14]) 9 | offset = (offset_reserved_flags >> 12) * 4 10 | self.flag_urg = (offset_reserved_flags & 32) >> 5 11 | self.flag_ack = (offset_reserved_flags & 16) >> 4 12 | self.flag_psh = (offset_reserved_flags & 8) >> 3 13 | self.flag_rst = (offset_reserved_flags & 4) >> 2 14 | self.flag_syn = (offset_reserved_flags & 2) >> 1 15 | self.flag_fin = offset_reserved_flags & 1 16 | self.data = raw_data[offset:] 17 | -------------------------------------------------------------------------------- /networking/udp.py: -------------------------------------------------------------------------------- 1 | import struct 2 | 3 | 4 | class UDP: 5 | 6 | def __init__(self, raw_data): 7 | self.src_port, self.dest_port, self.size = struct.unpack('! H H 2x H', raw_data[:8]) 8 | self.data = raw_data[8:] 9 | -------------------------------------------------------------------------------- /sniffer.py: -------------------------------------------------------------------------------- 1 | import socket 2 | from general import * 3 | from networking.ethernet import Ethernet 4 | from networking.ipv4 import IPv4 5 | from networking.icmp import ICMP 6 | from networking.tcp import TCP 7 | from networking.udp import UDP 8 | from networking.pcap import Pcap 9 | from networking.http import HTTP 10 | 11 | TAB_1 = '\t - ' 12 | TAB_2 = '\t\t - ' 13 | TAB_3 = '\t\t\t - ' 14 | TAB_4 = '\t\t\t\t - ' 15 | 16 | DATA_TAB_1 = '\t ' 17 | DATA_TAB_2 = '\t\t ' 18 | DATA_TAB_3 = '\t\t\t ' 19 | DATA_TAB_4 = '\t\t\t\t ' 20 | 21 | 22 | def main(): 23 | pcap = Pcap('capture.pcap') 24 | conn = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.ntohs(3)) 25 | 26 | while True: 27 | raw_data, addr = conn.recvfrom(65535) 28 | pcap.write(raw_data) 29 | eth = Ethernet(raw_data) 30 | 31 | print('\nEthernet Frame:') 32 | print(TAB_1 + 'Destination: {}, Source: {}, Protocol: {}'.format(eth.dest_mac, eth.src_mac, eth.proto)) 33 | 34 | # IPv4 35 | if eth.proto == 8: 36 | ipv4 = IPv4(eth.data) 37 | print(TAB_1 + 'IPv4 Packet:') 38 | print(TAB_2 + 'Version: {}, Header Length: {}, TTL: {},'.format(ipv4.version, ipv4.header_length, ipv4.ttl)) 39 | print(TAB_2 + 'Protocol: {}, Source: {}, Target: {}'.format(ipv4.proto, ipv4.src, ipv4.target)) 40 | 41 | # ICMP 42 | if ipv4.proto == 1: 43 | icmp = ICMP(ipv4.data) 44 | print(TAB_1 + 'ICMP Packet:') 45 | print(TAB_2 + 'Type: {}, Code: {}, Checksum: {},'.format(icmp.type, icmp.code, icmp.checksum)) 46 | print(TAB_2 + 'ICMP Data:') 47 | print(format_multi_line(DATA_TAB_3, icmp.data)) 48 | 49 | # TCP 50 | elif ipv4.proto == 6: 51 | tcp = TCP(ipv4.data) 52 | print(TAB_1 + 'TCP Segment:') 53 | print(TAB_2 + 'Source Port: {}, Destination Port: {}'.format(tcp.src_port, tcp.dest_port)) 54 | print(TAB_2 + 'Sequence: {}, Acknowledgment: {}'.format(tcp.sequence, tcp.acknowledgment)) 55 | print(TAB_2 + 'Flags:') 56 | print(TAB_3 + 'URG: {}, ACK: {}, PSH: {}'.format(tcp.flag_urg, tcp.flag_ack, tcp.flag_psh)) 57 | print(TAB_3 + 'RST: {}, SYN: {}, FIN:{}'.format(tcp.flag_rst, tcp.flag_syn, tcp.flag_fin)) 58 | 59 | if len(tcp.data) > 0: 60 | 61 | # HTTP 62 | if tcp.src_port == 80 or tcp.dest_port == 80: 63 | print(TAB_2 + 'HTTP Data:') 64 | try: 65 | http = HTTP(tcp.data) 66 | http_info = str(http.data).split('\n') 67 | for line in http_info: 68 | print(DATA_TAB_3 + str(line)) 69 | except: 70 | print(format_multi_line(DATA_TAB_3, tcp.data)) 71 | else: 72 | print(TAB_2 + 'TCP Data:') 73 | print(format_multi_line(DATA_TAB_3, tcp.data)) 74 | 75 | # UDP 76 | elif ipv4.proto == 17: 77 | udp = UDP(ipv4.data) 78 | print(TAB_1 + 'UDP Segment:') 79 | print(TAB_2 + 'Source Port: {}, Destination Port: {}, Length: {}'.format(udp.src_port, udp.dest_port, udp.size)) 80 | 81 | # Other IPv4 82 | else: 83 | print(TAB_1 + 'Other IPv4 Data:') 84 | print(format_multi_line(DATA_TAB_2, ipv4.data)) 85 | 86 | else: 87 | print('Ethernet Data:') 88 | print(format_multi_line(DATA_TAB_1, eth.data)) 89 | 90 | pcap.close() 91 | 92 | 93 | main() 94 | --------------------------------------------------------------------------------