zoukankan      html  css  js  c++  java
  • Code a network packet sniffer in python for Linux

    Basic Sniffer

    The most basic form of a sniffer would be :

    1 #Packet sniffer in python
    2 #For Linux
    3  
    4 import socket
    5  
    6 #create an INET, raw socket
    7 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
    8  
    9 # receive a packet
    10 while True:
    11   print s.recvfrom(65565)

    Run this with root privileges or sudo on ubuntu :

    sudo python sniffer.py

    The above sniffer works on the principle that a raw socket is capable of receiving all (of its type , like AF_INET) incoming traffic in Linux.

    The output could look like this :

    1 sudo python raw_socket.py
    2 ("E \x00x\xcc\xfc\x00\x000\x06j%J}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeI\xbf\x1aF[\x83P\x18\xff\xff\x88\xf6\x00\x00\x17\x03\x01\x00\x1c\xbbT\xb3\x07}\xb0\xedqE\x1e\xe7;-\x03\x9bU\xb7\xb1r\xd2\x9e]\xa1\xb8\xac\xa4V\x9a\x17\x03\x01\x00*\xed\x1f\xda\xa4##Qe\x9a\xe9\xd6\xadN\xf4\x9b\xc4\xf0C'\x01\xc4\x82\xdb\xb2\x8d(\xa5\xd0\x06\x95\x13WO\x0f\x8e\x1c\xa6f\x1d\xdf\xe1x", ('74.125.71.19', 0))
    3 ('E \x00I\xcc\xfd\x00\x000\x06jSJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ\x0f\x1aF[\x83P\x18\xff\xff:\x11\x00\x00\x17\x03\x01\x00\x1c\xaa];\t\x81yi\xbbC\xb5\x11\x14(Ct\x13\x10wt\xe0\xbam\xa9\x88/\xf8O{', ('74.125.71.19', 0))
    4 ('E \x00(\xcc\xfe\x00\x000\x06jsJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ0\x1aFa\x19P\x10\xff\xff\xe5\xb0\x00\x00', ('74.125.71.19', 0))
    5 ('E \x00(\xcc\xff\x00\x000\x06jrJ}G\x13\xc0\xa8\x01\x06\x01\xbb\xa3\xdc\x0b\xbeJ0\x1aFbtP\x10\xff\xff\xe4U\x00\x00', ('74.125.71.19', 0))

    The above is a dump of the network packets in hex. They can be parsed using the unpack function.

    Parsing the sniffed packet

    Here is the code to parse a TCP packet

    1 #Packet sniffer in python
    2 #For Linux
    3  
    4 import socket
    5 from struct import *
    6  
    7 #create an INET, STREAMing socket
    8 = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
    9  
    10 # receive a packet
    11 while True:
    12   packet = s.recvfrom(65565)
    13    
    14   #packet string from tuple
    15   packet = packet[0]
    16    
    17   #take first 20 characters for the ip header
    18   ip_header = packet[0:20]
    19    
    20   #now unpack them :)
    21   iph = unpack('!BBHHHBBH4s4s' , ip_header)
    22    
    23   version_ihl = iph[0]
    24   version = version_ihl >> 4
    25   ihl = version_ihl & 0xF
    26    
    27   ttl = iph[5]
    28   protocol = iph[6]
    29   s_addr = socket.inet_ntoa(iph[8]);
    30   d_addr = socket.inet_ntoa(iph[9]);
    31    
    32   print 'Version : ' + str(version) + ' IP Header Length : ' + str(ihl) + ' TTL : ' + str(ttl)+ ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr)
    33    
    34   tcp_header = packet[20:40]
    35    
    36   #now unpack them :)
    37   tcph = unpack('!HHLLBBHHH' , tcp_header)
    38    
    39   source_port = tcph[0]
    40   dest_port = tcph[1]
    41   sequence = tcph[2]
    42   acknowledgement = tcph[3]
    43   doff_reserved = tcph[4]
    44   tcph_length = doff_reserved >> 4
    45    
    46   print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) + ' TCP header length : ' + str(tcph_length)
    47    
    48   h_size = ihl * 4 + tcph_length * 4
    49   data_size = len(packet) - h_size
    50    
    51   #get data from the packet
    52   data = packet[data_size:]
    53    
    54   print 'Data : ' + data
    55   print

    The above code breaks down the packet into IP Header + TCP Header + Data.
    The unpack function is used to break down the packet. Documentation

    The output of the code should look like this :

    1 sudo python raw_socket.py
    2 Version : 4 IP Header Length : 5 TTL : 48 Protocol : 6 Source Address : 74.125.71.104 Destination Address : 192.168.1.6
    3 Source Port : 80 Dest Port : 36454 Sequence Number : 3689394456 Acknowledgement : 2825932501 TCP header length : 5
    4 Data : E (%?0=J}Gh?P?f?????pN?P,??
    5  
    6 Version : 4 IP Header Length : 5 TTL : 48 Protocol : 6 Source Address : 74.125.71.132 Destination Address : 192.168.1.6
    7 Source Port : 80 Dest Port : 46534 Sequence Number : 2071060663 Acknowledgement : 2858668979 TCP header length : 5
    8 Data : E (?H0;?J}G??P??{q?c?P
    9  
    10 Version : 4 IP Header Length : 5 TTL : 48 Protocol : 6 Source Address : 74.125.71.132 Destination Address : 192.168.1.6
    11 Source Port : 80 Dest Port : 46533 Sequence Number : 377985304 Acknowledgement : 2869878012 TCP header length : 5
    12 Data : E (??0JqJ}G??P????????PR?
    13  
    14 Version : 4 IP Header Length : 5 TTL : 48 Protocol : 6 Source Address : 74.125.71.17 Destination Address : 192.168.1.6
    15 Source Port : 443 Dest Port : 59643 Sequence Number : 183723837 Acknowledgement : 3530935779 TCP header length : 5
    16 Data : 2=?D?? ???????

    According to RFC 791 an IP header looks like this :

    1 0                   1                   2                   3
    2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |Version|  IHL  |Type of Service|          Total Length         |
    5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    6 |         Identification        |Flags|      Fragment Offset    |
    7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |  Time to Live |    Protocol   |         Header Checksum       |
    9 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    10 |                       Source Address                          |
    11 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    12 |                    Destination Address                        |
    13 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    14 |                    Options                    |    Padding    |
    15 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    If the IHL is 5 then total size is 20 bytes hence options+padding is absent.
    For TCP packets the protocol is 6. Source address is the source IPv4 address in long format.

    Next comes the TCP header :

    1 0                   1                   2                   3
    2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |          Source Port          |       Destination Port        |
    5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    6 |                        Sequence Number                        |
    7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |                    Acknowledgment Number                      |
    9 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    10 |  Data |           |U|A|P|R|S|F|                               |
    11 | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
    12 |       |           |G|K|H|T|N|N|                               |
    13 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    14 |           Checksum            |         Urgent Pointer        |
    15 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    16 |                    Options                    |    Padding    |
    17 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    18 |                             data                              |
    19 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    and the balance after the TCP header is the data portion.

    The C version of the code is here.
    The Php version of the code is here.

    Note :

    1. The above sniffer picks up only TCP packets, because of the declaration :

    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

    For UDP and ICMP the declaration has to be :

    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)

    You might be tempted to think of doing :

    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)

    but this will not work , since IPPROTO_IP is a dummy protocol not a real one.

    2. This sniffer picks up only incoming packets.

    3. This sniffer delivers only IP frames , which means ethernet headers are not available.

    Better Sniffer

    Now let us see how we can overcome the above mentioned drawbacks. The solutions is quite simple.

    This line :

    s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

    needs to be changed to :

    s = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0×0003))

    Now the same socket will receive :

    1. All incoming and outgoing traffic.

    2. All Ethernet frames , which means all kinds of IP packets(TCP , UDP , ICMP) and even other kinds of packets(like ARP) if there are any.

    3. It will also provide the ethernet header as a part of the received packet.

    Here is the source code :

    1 #Packet sniffer in python
    2 #For Linux - Sniffs all incoming and outgoing packets :)
    3  
    4 import socket
    5 from struct import *
    6  
    7 #Convert a string of 6 characters of ethernet address into a dash separated hex string
    8 def eth_addr (a) :
    9   = "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x" % (ord(a[0]) , ord(a[1]) , ord(a[2]), ord(a[3]),ord(a[4]) , ord(a[5]))
    10   return b
    11  
    12 #create an PACKET , RAW SOCKET
    13 #define ETH_P_ALL    0x0003          /* Every packet (be careful!!!) */
    14 = socket.socket( socket.AF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
    15  
    16 # receive a packet
    17 while True:
    18   packet = s.recvfrom(65565)
    19    
    20   #packet string from tuple
    21   packet = packet[0]
    22    
    23   #parse ethernet header
    24   eth_length = 14
    25    
    26   eth_header = packet[:eth_length]
    27   eth = unpack('!6s6sH' , eth_header)
    28   eth_protocol = socket.ntohs(eth[2])
    29   print 'Destination MAC : ' + eth_addr(packet[0:6]) + ' Source MAC : ' +eth_addr(packet[6:12]) + ' Protocol : ' + str(eth_protocol)
    30    
    31   #Parse IP packets
    32   if eth_protocol == 8 :
    33     #Parse IP header
    34     #take first 20 characters for the ip header
    35     ip_header = packet[eth_length:20+eth_length]
    36      
    37     #now unpack them :)
    38     iph = unpack('!BBHHHBBH4s4s' , ip_header)
    39      
    40     version_ihl = iph[0]
    41     version = version_ihl >> 4
    42     ihl = version_ihl & 0xF
    43      
    44     iph_length = ihl * 4
    45      
    46     ttl = iph[5]
    47     protocol = iph[6]
    48     s_addr = socket.inet_ntoa(iph[8]);
    49     d_addr = socket.inet_ntoa(iph[9]);
    50      
    51     print 'Version : ' + str(version) + ' IP Header Length : ' + str(ihl) + ' TTL : ' +str(ttl) + ' Protocol : ' + str(protocol) + ' Source Address : ' + str(s_addr) + ' Destination Address : ' + str(d_addr)
    52      
    53     #TCP protocol
    54     if protocol == 6 :
    55       = iph_length + eth_length
    56       tcp_header = packet[t:t+20]
    57      
    58       #now unpack them :)
    59       tcph = unpack('!HHLLBBHHH' , tcp_header)
    60        
    61       source_port = tcph[0]
    62       dest_port = tcph[1]
    63       sequence = tcph[2]
    64       acknowledgement = tcph[3]
    65       doff_reserved = tcph[4]
    66       tcph_length = doff_reserved >> 4
    67        
    68       print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Sequence Number : ' + str(sequence) + ' Acknowledgement : ' + str(acknowledgement) + ' TCP header length : ' + str(tcph_length)
    69        
    70       h_size = eth_length + iph_length + tcph_length * 4
    71       data_size = len(packet) - h_size
    72        
    73       #get data from the packet
    74       data = packet[data_size:]
    75        
    76       print 'Data : ' + data
    77      
    78     #ICMP Packets
    79     elif protocol == 1 :
    80       = iph_length + eth_length
    81       icmph_length = 4
    82       icmp_header = packet[u:u+4]
    83      
    84       #now unpack them :)
    85       icmph = unpack('!BBH' , icmp_header)
    86        
    87       icmp_type = icmph[0]
    88       code = icmph[1]
    89       checksum = icmph[2]
    90        
    91       print 'Type : ' + str(icmp_type) + ' Code : ' + str(code) + ' Checksum : ' +str(checksum)
    92        
    93       h_size = eth_length + iph_length + icmph_length
    94       data_size = len(packet) - h_size
    95        
    96       #get data from the packet
    97       data = packet[data_size:]
    98        
    99       print 'Data : ' + data
    100      
    101     #UDP packets
    102     elif protocol == 17 :
    103       = iph_length + eth_length
    104       udph_length = 8
    105       udp_header = packet[u:u+8]
    106      
    107       #now unpack them :)
    108       udph = unpack('!HHHH' , udp_header)
    109        
    110       source_port = udph[0]
    111       dest_port = udph[1]
    112       length = udph[2]
    113       checksum = udph[3]
    114        
    115       print 'Source Port : ' + str(source_port) + ' Dest Port : ' + str(dest_port) + ' Length : ' + str(length) + ' Checksum : ' + str(checksum)
    116        
    117       h_size = eth_length + iph_length + udph_length
    118       data_size = len(packet) - h_size
    119        
    120       #get data from the packet
    121       data = packet[data_size:]
    122        
    123       print 'Data : ' + data
    124      
    125     #some other IP packet like IGMP
    126     else :
    127       print 'Protocol other than TCP/UDP/ICMP'
    128        
    129     print

    Run with root privileges.

    The output should be something like this :

    1 Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
    2 Version : 4 IP Header Length : 5 TTL : 57 Protocol : 6 Source Address : 64.131.72.23 Destination Address : 192.168.1.6
    3 Source Port : 80 Dest Port : 58928 Sequence Number : 1392138007 Acknowledgement : 2935013912 TCP header length : 6
    4 Data : ??y?%^?=E ,@9?c@?H?P?0R?W????`?5t?
    5  
    6 Destination MAC : 00-25-5e-1a-3d-f1 Source MAC : 00-1c-c0-f8-79-ee Protocol : 8
    7 Version : 4 IP Header Length : 5 TTL : 64 Protocol : 6 Source Address : 192.168.1.6 Destination Address : 64.131.72.23
    8 Source Port : 58928 Dest Port : 80 Sequence Number : 2935013912 Acknowledgement : 1392138008 TCP header length : 5
    9 Data : %^?=???yE(mU@@?2?@?H?0P????R?W?PJc
    10  
    11 Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
    12 Version : 4 IP Header Length : 5 TTL : 55 Protocol : 17 Source Address : 78.141.179.8 Destination Address : 192.168.1.6
    13 Source Port : 34049 Dest Port : 56295 Length : 28 Checksum : 25749
    14 Data : @7?YN?????d??????r'?y@?f?h`??
    15  
    16 Destination MAC : 00-1c-c0-f8-79-ee Source MAC : 00-25-5e-1a-3d-f1 Protocol : 8
    17 Version : 4 IP Header Length : 5 TTL : 118 Protocol : 17 Source Address : 173.181.21.51 Destination Address : 192.168.1.6
    18 Source Port : 5999 Dest Port : 56295 Length : 26 Checksum : 22170
    19 Data : s)vL??3?o???V?Z???cw?k??pIQ

    It parses the Ethernet header and also the UDP and ICMP headers.

    Ethernet header looks like this :

    1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    2 |       Ethernet destination address (first 32 bits)            |
    3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 | Ethernet dest (last 16 bits)  |Ethernet source (first 16 bits)|
    5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    6 |       Ethernet source address (last 32 bits)                  |
    7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |        Type code              |                               |
    9 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    UDP Header according to RFC 768 :

    1 0      7 8     15 16    23 24    31 
    2 +--------+--------+--------+--------+
    3 |     Source      |   Destination   |
    4 |      Port       |      Port       |
    5 +--------+--------+--------+--------+
    6 |                 |                 |
    7 |     Length      |    Checksum     |
    8 +--------+--------+--------+--------+
    9 |                                    
    10 |          data octets ...           
    11 +---------------- ...                

    ICMP Header according to RFC 792 :

    1 0                   1                   2                   3
    2 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    3 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    4 |     Type      |     Code      |          Checksum             |
    5 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    6 |                             unused                            |
    7 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    8 |      Internet Header + 64 bits of Original Data Datagram      |
    9 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    This kind of a sniffer does not depend on any external libraries like libpcap.

    The C version of this code is here.

    References :

    1. Python Socket documentation.

  • 相关阅读:
    hdu4276 依赖背包
    poj1155 依赖背包
    cf219d 基础换根法
    贪婪大陆——(树状数组)
    数星星(树状数组或者线段树)
    拓扑排序基础题——排序
    Codeforces Round #511 (Div. 1) T2 Little C Loves 3 II
    除虫药水(简单dp)
    烽火传递(单调队列优化dp,然而蒟蒻用一个优先队列做)
    ZOJ----3471Most powerful(简单状压dp)
  • 原文地址:https://www.cnblogs.com/rollenholt/p/2591017.html
Copyright © 2011-2022 走看看