Browse Source

v0.75/si
- ICMP support
- some changes regarding speed

Peter Siegrist 5 years ago
parent
commit
000332a14a
2 changed files with 152 additions and 58 deletions
  1. 29 1
      README.md
  2. 123 57
      sisniff.py

+ 29 - 1
README.md

@@ -1 +1,29 @@
-.
+== sisniff
+
+A commandline network sniffer showing the applications belonging to each packet.
+
+It supports TCP, UDP and ICMP packets.
+The Sniffer accepts some filter like tcpdump.
+
+For HTTP connections, there is an argument to show part of its payload.
+
+!! sisniff uses scapy's sniff() function, so scapy package is needed:
+!! debian: apt-get install scapy
+!! other systems: http://www.secdev.org/projects/scapy
+
+
+
+--------------------
+# ./sisniff.py -h
+usage: sisniff.py [-h] -i {eth0,lo,tun0,wlan0} [-n] [-pH] [filter]
+
+positional arguments:
+  filter                Pcap filter (BPF syntax) on top of IP (in dbl-quotes "...")
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -i {eth0,lo,tun0,wlan0}
+                        Interface (mandatory)
+  -n                    Do not resolve IP-Addresses
+  -pH                   Show HTTP Payload
+--------------------

+ 123 - 57
sisniff.py

@@ -14,17 +14,26 @@ import struct
 import commands
 import argparse
 
-VERSION = "0.71"
+VERSION = "0.75"
 
 PROC_TCP4 = "/proc/net/tcp"
 PROC_UDP4 = "/proc/net/udp"
+PROC_ICMP4 = "/proc/net/icmp"
 PROC_TCP6 = "/proc/net/tcp6"
 PROC_UDP6 = "/proc/net/udp6"
 PROC_PACKET = "/proc/net/packet"
 
+# Services
 TSERV = dict((TCP_SERVICES[k], k) for k in TCP_SERVICES.keys())
 USERV = dict((UDP_SERVICES[k], k) for k in UDP_SERVICES.keys())
 
+# IP Protocol Numbers (dec)
+IPPROTO_ICMP = 1
+IPPROTO_TCP = 6
+IPROTOP_IGP = 9
+IPPROTO_UDP = 17
+
+nostate = set(['04','05','06''07','08','09','0C','0D'])
 tcp_payload_hdrs = ['GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT']
 numeric = False
 payloadH = False
@@ -32,9 +41,10 @@ fillter = ""
 
 def get_conn_info(proto,hosts,ports):
     ''' returns: pid, exe, uid '''
-
+    uid = 0
+    
     line_array = _proc4load(proto,hosts,ports)
-     
+    
     if line_array == 0:
         return ['?','?','?']
     '''    
@@ -43,16 +53,18 @@ def get_conn_info(proto,hosts,ports):
     except:
         uid = line_array[7]
     '''
-    uid = 0
+        
+    inode = str(line_array[9])
+    if inode == "0":
+        return ['.','.','.']
     
-    inode = line_array[9]                 # Need the inode to get process pid.
-    if inode == 0:
-        return ['?','-','?']
+    pid = _get_pid_of_inode(inode)          # try get a pid
     
-    pid = _get_pid_of_inode(inode)
-    if pid == "NoPid":
-        return ["NoPid", "NoExe", uid]
     
+    if pid == "NoPid":
+        #print ">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array)
+        return ['-', '-', uid]
+
     try:                                            # try read the process name.
         exe = os.readlink('/proc/'+pid+'/exe').split('/')[-1]
     except:
@@ -65,7 +77,7 @@ def get_conn_info(proto,hosts,ports):
 def _proc4load(proto,hosts,ports):
     ''' Read the table of tcp/udp connections
     tcp/udp: "sl,  local_address, rem_address, st, tx_queue rx_queue, tr tm->when, retrnsmt,   uid , timeout, inode ,..."
-    ---- TCP states
+    ---- TCP states from https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/net/tcp_states.h?id=HEAD
     enum {
 	TCP_ESTABLISHED = 1,
 	TCP_SYN_SENT,
@@ -84,48 +96,74 @@ def _proc4load(proto,hosts,ports):
     };
     ----------
     '''
-    content = []
-    if proto == 6:
+    
+    #xhosts = _ip_hexrev(hosts)
+    xports = _dec2hex(ports)
+    
+    if proto == IPPROTO_UDP:
+        try:
+            with open(PROC_UDP4,'r') as f:
+                next(f)
+                for line in f:
+                    line_arrayu = _remove_empty(line.split(' '))
+                    l_xhost,l_xport = line_arrayu[1].split(':')
+                    if l_xhost not in xMYADDRS:
+                        continue
+                    if l_xport == xports:
+                        return line_arrayu
+                        
+                return 0
+        except:
+            print "open proc_udp4 error"
+            return 0
+    elif proto == IPPROTO_TCP:
         try:
             with open(PROC_TCP4,'r') as f:
-                content = f.readlines()
-                f.close()
-                content.pop(0)
+                next(f)
+                for line in f:
+                    line_arrayt = _remove_empty(line.split(' '))
+                    if line_arrayt[3] in nostate:        # not some TCP state
+                        continue
+                    l_xhost,l_xport = line_arrayt[1].split(':')
+                    if l_xhost not in xMYADDRS:
+                        continue
+                    if l_xport == xports:
+                        return line_arrayt
+                        
+                return 0
         except:
-            print "open proc_tcp4 error"
+            print "open proc_tcp error"
             return 0
-    if proto == 17:
+    
+    elif proto == IPPROTO_ICMP:
         try:
-            with open(PROC_UDP4,'r') as f:
-                content = f.readlines()
-                f.close()
-                content.pop(0)
+            with open(PROC_ICMP4,'r') as f:
+                next(f)
+                for line in f:
+                    line_arrayi = _remove_empty(line.split(' '))
+                    l_xhost,l_xport = line_arrayi[1].split(':')
+                    if l_xhost not in xMYADDRS:
+                        continue
+                    if l_xport == xports:
+                        return line_arrayi
+                        
+                return 0
         except:
-            print "open proc_udp4 error"
+            print "open proc_icmp4 error"
             return 0
     
-    for line in content:    # src
-        line_array = _remove_empty(line.split(' '))
-        if line_array[3] in ['04','05','06''07','08','09','0C','0D']:        # not some state
-            continue
-        l_host,l_port = _convert_ipv4_port(line_array[1])
-        # alt if l_host == '127.0.0.1' or l_host not in MYADDRS:
-        if l_host not in MYADDRS:
-            continue
-        
-        if str(l_port) == str(ports):
-            #print l_host+" "+str(l_port)+" // "+host+" "+str(port)
-            return line_array
-    #print "no entry in procfile found!"
     return 0
 
 def _convert_ipv4_port(array):
     host,port = array.split(':')
     return _ip(host),_hex2dec(port)
-
+        
 def _hex2dec(s):
     return str(int(s,16))
 
+def _dec2hex(p):
+    return hex(int(p)).split('x')[-1].upper()
+    
 def _ip(s):
     ip = [(_hex2dec(s[6:8])),(_hex2dec(s[4:6])),(_hex2dec(s[2:4])),(_hex2dec(s[0:2]))]
     return '.'.join(ip)
@@ -135,13 +173,17 @@ def _ip6(s):
     #print '66666:', ':'.join(ip), s
     return ':'.join(ip)
 
+def _ip_hexrev(ip):
+    return ''.join([hex(int(x)+256)[3:] for x in ip.split('.')][::-1]).upper()
+
 def _remove_empty(array):
-    return [x for x in array if x !='']
+    return [x for x in array if x != '']
 
 def _get_pid_of_inode(inode):
-    for item in glob.glob('/proc/[0-9]*/fd/[0-9]*'):
+    s_term = r'^socket\:\['+ inode +r'\]$'
+    for item in glob.iglob('/proc/[0-9]*/fd/[0-9]*'):
         try:
-            if re.search(inode,os.readlink(item)):
+            if re.match(s_term,os.readlink(item)):
                 return item.split('/')[2]
         except:
             pass
@@ -180,36 +222,41 @@ def doPackets(packet):
     # only local addresses
     if packet[0][1].src in MYADDRS:
         conn_addr = packet[0][1].src
-        if packet.haslayer(TCP) or packet.haslayer(UDP):
-            conn_port = packet[0][2].sport
+        if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):
+            try:
+                conn_port = packet[0][2].sport
+            except:
+                conn_port = 99999
         o_dir = 1
     else:
         conn_addr = packet[0][1].dst
-        if packet.haslayer(TCP) or packet.haslayer(UDP):
-            conn_port = packet[0][2].dport
+        if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):
+            try:
+                conn_port = packet[0][2].dport
+            except:
+                conn_port = 99999
         o_dir = 0
     
-    if packet.haslayer(TCP) or packet.haslayer(UDP):
+    if packet.haslayer(TCP) or packet.haslayer(UDP) or packet.haslayer(ICMP):        # grrr, no info in /proc/net/icmp so far. or packet.haslayer(ICMP):
         # logemol casch 
-        c_hash = conn_addr+'.'+str(conn_port)
+        c_hash = conn_addr+'=:='+str(conn_port)
         if not any(x[0] == c_hash for x in conn_cache):
             # get the connection info from packet
             spid,sexe,suid = get_conn_info(packet[0][1].proto, conn_addr, conn_port)
-        
             if re.match("^[0-9]+$", spid):
                 program = sexe
                 pid = spid
                 uid = suid
                 
                 # update cache
-                if len(conn_cache) > cc_maxlen:
+                if len(conn_cache) >= cc_maxlen:
                     conn_cache.pop(0)
                 conn_cache.append([c_hash,program,pid])
                 #print conn_cache
             else:
-                program = "-"
-                pid = "-"
-                uid = "-"
+                program = sexe
+                pid = spid
+                uid = suid
         else:
             # me honds fom casch
             indx = [x[0] for x in conn_cache].index(c_hash)
@@ -234,7 +281,7 @@ def doPackets(packet):
             o_sport = str(packet[0][2].sport)
         flags = ""
         #o_payload = packet[0].sprintf('%10s,UDP.payload%')
-    if packet.haslayer(TCP):
+    elif packet.haslayer(TCP):
         o_proto = "TCP"
         try:
             o_dport = "\033[1m"+TSERV[packet[0][2].dport]+"\033[0m"
@@ -252,7 +299,24 @@ def doPackets(packet):
                     request_line, gaga = tpld.split('\r\n', 1)
                     o_payload = str(request_line)
                     #o_payload = tpld[0:20]
-    
+    elif packet.haslayer(ICMP):
+        o_proto = "ICMP"
+        if conn_port == 99999:
+            o_dport = "-"
+            o_sport = "-"
+        else:
+            try:
+                o_dport = "\033[1m"+USERV[packet[0][2].sport]+"\033[0m"
+            except:
+                o_dport = str(packet[0][2].sport)
+            try:
+                o_sport = "\033[1m"+USERV[packet[0][2].dport]+"\033[0m"
+            except:
+                o_sport = str(packet[0][2].dport)
+        flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]"
+    else:
+        o_proto = "UNKNOWN"
+        
     if o_dir == 1:
         if numeric == False:
             if res_cache.has_key(packet[0][1].dst):
@@ -283,8 +347,8 @@ if not check_root():
     print("This program needs root privileges !\nThats because of reading the /proc filesystem and using libpcap functions.\nSo I give up\n")
     conf.sniff_promisc=0
     conf.sniff_promisc=0
-    print conf
-    #sys.exit()
+    #print conf
+    sys.exit()
 
 # get the interfaces
 ifaces = commands.getoutput("ls /sys/class/net")
@@ -311,6 +375,7 @@ if args.filter:
 MYADDRS = _remove_empty(commands.getoutput("hostname -I").split(' '))
 MYADDRS.append('0.0.0.0')
 MYADDRS.append('127.0.0.1')
+xMYADDRS = [_ip_hexrev(x) for x in MYADDRS]
 print "> My IP-Addresses: " + str(MYADDRS)
 
 # confirmed connections cache (ringboffer)
@@ -320,12 +385,13 @@ cc_maxlen = 20
 # resolver cache
 res_cache = {}
 n_try = 3
-
+print
+print "Prog/PID mavericks: ?/? = No entry in /proc/net/xxx; -/- = No PID for Inode found; ./. = Inode=0;"
 print
 print "Program/PID: Local addr:port <<->> Remote addr:port [Flags] Len:length : [Payload]"
 print "-------------------------------------------------------------------------------"
 
 # sniff, filtering for IP traffic
-sniff(filter="ip"+fillter,iface=iface,prn=doPackets)
+sniff(filter="ip"+fillter,iface=iface,prn=doPackets, store=0)
 
 ## -- oond denn isch schloss