Browse Source

v0.90, Python 3.x fähig
commandlineargs extended
renamed sisniff.py -> sisniff

Peter Siegrist 4 years ago
parent
commit
042e3f467e
2 changed files with 87 additions and 42 deletions
  1. 23 12
      README.md
  2. 64 30
      sisniff.py

+ 23 - 12
README.md

@@ -1,7 +1,8 @@
 sisniff
 -------
 
-A commandline network sniffer showing the applications belonging to each packet.
+Like tcpdump, sisniff captures and displays all connections from and to the local machine. 
+Additionally it will show you the <b>applications belonging to each packet</b>.<br> 
 
 It supports TCP, UDP and ICMP packets.<br>
 The Sniffer accepts some filter like tcpdump.<br>
@@ -10,7 +11,7 @@ The Sniffer accepts some filter like tcpdump.<br>
 For HTTP connections, there is an argument to show part of its payload.<br>
 
 ----
-Under some cirumstances the program/PID cannot be evaluated. This would be reported as follow:
+Under some cirumstances the program/PID cannot be evaluated. This mavericks would be reported as follow:
 <pre>
  "?/?" = No entry in /proc/net/[TCP/UDP/ICMP]
  "-/-" = Found Inode but no PID
@@ -21,29 +22,39 @@ Under some cirumstances the program/PID cannot be evaluated. This would be repor
 <p><i>
 !! sisniff uses scapy's sniff() function, so scapy package is needed:<br>
 !! debian: apt-get install scapy<br>
+!! pip: pip/pip3 install scapy
 !! other systems: http://www.secdev.org/projects/scapy<br><p>
 </i>
 
-<pre>
---------------------
+<pre><br> 
+
+This program needs Python 3.x or Python 2.x. 
 
-# ./sisniff.py -h
-usage: sisniff.py [-h] -i {eth0,lo,tun0,wlan0} [-n] [-p program] [-pH] [-pHl]
-                  [filter]
 
-sisniff V0.80
+--------------------
+
+# sisniff -h
+usage: sisniff [-h] -i {eth0,lo,wlan0} [-n] [-p program|not-program] [-pH] [-pHl] [filter]
+ 
+sisniff V0.90
 
 positional arguments:
   filter                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 (required)
+  -i {eth0,lo,wlan0}    Interface (required)
   -n                    Do not resolve IP-Addresses
-  -p program            Filter by program name
+  -p program|not-program
+                        Filter by program name ([not-] negates)
   -pH                   Show HTTP Payload
   -pHl                  Show HTTP Payload, long output
-
 --------------------
 </pre>
+
+- Interfaces showed in the help are gathered from the running system.
+- <pre>program</pre> means the name in the 'Program' column, e.g. <pre>thunderbird-bin</pre>
+- <pre>not-program</pre> excludes the program from beeing showed, e.g. <pre>not-thunderbird-bin</pre>
+- <pre>filter</pre> is in same syntax as tcpdump uses. Must be written in double-quotes "..."
+
+

+ 64 - 30
sisniff.py

@@ -1,6 +1,17 @@
-#!/usr/bin/python2
+#!/usr/bin/env python
 
-# (c) 2017 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch>
+# (c) 2017-2019 by Siegrist(SystemLoesungen) <PSS@ZweierNet.ch>
+#
+# All Rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
 
 from scapy.all import *
 import pwd
@@ -11,10 +22,20 @@ import sys
 import string
 import fcntl
 import struct
-import commands
 import argparse
+if sys.version_info.major == 2:
+	import commands as subprocess
+elif sys.version_info.major == 3:
+	import subprocess
 
-VERSION = "0.81"
+def _to_str(inp):
+	if sys.version_info.major == 2:
+		return inp
+	else:
+		return "".join( chr(x) for x in inp)
+	
+
+VERSION = "0.90"
 
 PROC_TCP4 = "/proc/net/tcp"
 PROC_UDP4 = "/proc/net/udp"
@@ -63,7 +84,7 @@ def get_conn_info(proto,hosts,ports):
     
     
     if pid == "NoPid":
-        #print ">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array)
+        #print(">>>>>>>>>>>NoPID:" + str(hosts) +" "+ str(ports) + "//" + str(line_array))
         return ['-', '-', uid]
 
     try:                                            # try read the process name.
@@ -71,7 +92,7 @@ def get_conn_info(proto,hosts,ports):
     except:
         exe = None
     
-    #print str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid
+    #print(str(lhost) +" "+ str(lport) +" "+ inode +" "+ pid)
     return [pid, exe, uid]
 
     
@@ -115,7 +136,7 @@ def _proc4load(proto,hosts,ports):
                         
                 return 0
         except:
-            print "open proc_udp4 error"
+            print("open proc_udp4 error")
             return 0
     elif proto == IPPROTO_TCP:
         try:
@@ -133,7 +154,7 @@ def _proc4load(proto,hosts,ports):
                         
                 return 0
         except:
-            print "open proc_tcp error"
+            print("open proc_tcp error")
             return 0
     
     elif proto == IPPROTO_ICMP:
@@ -150,7 +171,7 @@ def _proc4load(proto,hosts,ports):
                         
                 return 0
         except:
-            print "open proc_icmp4 error"
+            print("open proc_icmp4 error")
             return 0
     
     return 0
@@ -171,7 +192,6 @@ def _ip(s):
 
 def _ip6(s):
     ip = [s[6:8],s[4:6],s[2:4],s[0:2],s[12:14],s[14:16],s[10:12],s[8:10],s[22:24],s[20:22],s[18:20],s[16:18],s[30:32],s[28:30],s[26:28],s[24:26]]
-    #print '66666:', ':'.join(ip), s
     return ':'.join(ip)
 
 def _ip_hexrev(ip):
@@ -253,7 +273,6 @@ def doPackets(packet):
                 if len(conn_cache) >= cc_maxlen:
                     conn_cache.pop(0)
                 conn_cache.append([c_hash,program,pid])
-                #print conn_cache
             else:
                 program = sexe
                 pid = spid
@@ -273,11 +292,16 @@ def doPackets(packet):
     except:
         pass
     else:
-        if program != filter_prog:
-            return
+        if filter_prog.startswith('not-'):
+            if program == filter_prog[4:]:
+                return
+        else:
+            if program != filter_prog:
+                return
         
     
     o_payload = ""
+    
     if packet.haslayer(UDP):
         o_proto = "UDP"
         try:
@@ -289,7 +313,7 @@ def doPackets(packet):
         except:
             o_sport = str(packet[0][2].sport)
         flags = ""
-        #o_payload = packet[0].sprintf('%10s,UDP.payload%')
+        #o_payload = _to_str(packet[0].sprintf('%10s,UDP.payload%'))
     elif packet.haslayer(TCP):
         o_proto = "TCP"
         try:
@@ -304,8 +328,10 @@ def doPackets(packet):
         if payloadH == True:
             if packet.haslayer(Raw):
                 #tpld = packet[0].sprintf('%TCP.payload%')
-                tpld = packet[0][TCP].load
-                if re.match("^GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*", tpld[0:8]):
+                tpld = _to_str(packet[0][TCP].load)
+                tpldhead = tpld[0:8]
+                #print("tpld:" + tpldhead)
+                if re.match(r'GET|POST|HTTP|HEAD|PUT|PATCH|DELETE|TRACE|OPTIONS|CONNECT.*', tpldhead):
                     if payloadHl == True:
                         o_payload = str(tpld)
                     else:
@@ -328,11 +354,19 @@ def doPackets(packet):
                 o_sport = str(packet[0][2].dport)
         flags = "["+packet[0].sprintf('%ICMP.type%') + "/" + packet[0].sprintf('%ICMP.code%')+"]"
     else:
-        o_proto = "UNKNOWN"
+    	layerukn = packet[0][IP].getlayer(1)
+    	if layerukn is None:
+    		o_proto = "UNKNOWN"
+    	else:
+    		#print("Layer:", xxl1.name)
+    		o_proto = layerukn.name
+    	
+        #o_proto = "UNKNOWN"
         
     if o_dir == 1:
         if numeric == False:
-            if res_cache.has_key(packet[0][1].dst):
+            #if res_cache.has_key(packet[0][1].dst):
+            if packet[0][1].dst in res_cache:
                 rem_name = res_cache[packet[0][1].dst]
             else:
                 rem_name = _resolve_ip(packet[0][1].dst)
@@ -343,7 +377,8 @@ def doPackets(packet):
         return "\033[1m"+str(program)+"\033[0m" +"/"+ str(pid) + " - " + o_proto + ": " + packet[0][1].src + ":" + o_sport + "\033[1m\033[31m  ->>>  \033[0m" + rem_name + ":" + o_dport + " " + flags + " Len:" + str(packet[0][1].len) + " : " + o_payload
     else:
         if numeric == False:
-            if res_cache.has_key(packet[0][1].src):
+            #if res_cache.has_key(packet[0][1].src):
+            if packet[0][1].src in res_cache:
                 rem_name = res_cache[packet[0][1].src]
             else:
                 rem_name = _resolve_ip(packet[0][1].src)
@@ -361,19 +396,18 @@ 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()
 
 # get the interfaces
-ifaces = commands.getoutput("ls /sys/class/net")
+ifaces = subprocess.getoutput("ls /sys/class/net")
 iface_list = ifaces.split('\n')
 
-print
+print("")
 # commandline params
 parser = argparse.ArgumentParser(description='sisniff V'+VERSION)
 parser.add_argument('-i', help="Interface (required)", choices=iface_list, required=True)
 parser.add_argument('-n', help="Do not resolve IP-Addresses", action="store_true")
-parser.add_argument('-p', help="Filter by program name", type=str, metavar='program')
+parser.add_argument('-p', help='Filter by program name ([not-] negates)', type=str, metavar='program|not-program')
 parser.add_argument('-pH', help="Show HTTP Payload", action="store_true")
 parser.add_argument('-pHl', help="Show HTTP Payload, long output", action="store_true")
 parser.add_argument('filter', nargs='?', help="Filter (BPF syntax) on top of IP (in dbl-quotes \"...\")", type=str)
@@ -388,7 +422,7 @@ if args.pHl:
     payloadHl = True
 if args.filter:
     fillter = " and (" + args.filter + ")"
-    print "> Applying Filter: \"ip" + fillter + "\"" 
+    print("> Applying Filter: \"ip" + fillter + "\"") 
 if args.p:
     filter_prog = args.p
     
@@ -398,7 +432,7 @@ MYADDRS = _remove_empty([os.popen('ip addr show '+iface).read().split("inet ")[1
 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)
+print("> My IP-Addresses: " + str(MYADDRS))
 
 # confirmed connections cache (ringboffer)
 conn_cache = []
@@ -407,11 +441,11 @@ 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 "-------------------------------------------------------------------------------"
+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, store=0)