scapy/arch/windows/__init__.py
author Dirk Loss <d.loss@infodas.de>
Wed Jan 13 12:16:42 2010 +0100 (2010-01-13)
changeset 1216 f263168cfb0e
parent 1194 bfd7bbdcda5b
child 1236 33f69be3ddcd
permissions -rwxr-xr-x
Fixed console redirection bug in sniff()

This was left-over from the old console handling
code in Scapy-win. Now this caused a NameError:
"global name 'console' is not defined"
     1 ## This file is part of Scapy
     2 ## See http://www.secdev.org/projects/scapy for more informations
     3 ## Copyright (C) Philippe Biondi <phil@secdev.org>
     4 ## This program is published under a GPLv2 license
     5 
     6 import os,re,sys,socket,time
     7 from glob import glob
     8 from scapy.config import conf,ConfClass
     9 from scapy.error import Scapy_Exception,log_loading,log_runtime
    10 from scapy.utils import atol, inet_aton, inet_ntoa, PcapReader
    11 from scapy.base_classes import Gen, Net, SetGen
    12 import scapy.plist as plist
    13 from scapy.sendrecv import debug, srp1
    14 from scapy.layers.l2 import Ether, ARP
    15 from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
    16 
    17 conf.use_pcap = 1
    18 conf.use_dnet = 1
    19 from scapy.arch import pcapdnet
    20 from scapy.arch.pcapdnet import *
    21 
    22 LOOPBACK_NAME="lo0"
    23 WINDOWS = True
    24 
    25 
    26 def _where(filename, dirs=[], env="PATH"):
    27     """Find file in current dir or system path"""
    28     if not isinstance(dirs, list):
    29         dirs = [dirs]
    30     if glob(filename):
    31         return filename
    32     paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs
    33     for path in paths:
    34         for match in glob(os.path.join(path, filename)):
    35             if match:
    36                 return os.path.normpath(match)
    37     raise IOError("File not found: %s" % filename)
    38 
    39 def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
    40     """Find executable in current dir, system path or given ProgramFiles subdir"""
    41     for fn in [filename, filename+".exe"]:
    42         try:
    43             if installsubdir is None:
    44                 path = _where(fn)
    45             else:
    46                 path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)])
    47         except IOError:
    48             path = filename
    49         else:
    50             break        
    51     return path
    52 
    53 
    54 class WinProgPath(ConfClass):
    55     _default = "<System default>"
    56     # We try some magic to find the appropriate executables
    57     pdfreader = win_find_exe("AcroRd32") 
    58     psreader = win_find_exe("gsview32.exe", "Ghostgum/gsview")
    59     dot = win_find_exe("dot", "ATT/Graphviz/bin")
    60     tcpdump = win_find_exe("windump")
    61     tcpreplay = win_find_exe("tcpreplay")
    62     display = _default
    63     hexedit = win_find_exe("hexer")
    64     wireshark = win_find_exe("wireshark", "wireshark")
    65 
    66 conf.prog = WinProgPath()
    67 
    68 
    69 
    70 import _winreg
    71 
    72 
    73     
    74 class PcapNameNotFoundError(Scapy_Exception):
    75     pass    
    76 
    77 class NetworkInterface(object):
    78     """A network interface of your local host"""
    79     
    80     def __init__(self, dnetdict=None):
    81         self.name = None
    82         self.ip = None
    83         self.mac = None
    84         self.pcap_name = None
    85         self.win_name = None
    86         self.uuid = None
    87         self.dnetdict = dnetdict
    88         if dnetdict is not None:
    89             self.update(dnetdict)
    90         
    91     def update(self, dnetdict):
    92         """Update info about network interface according to given dnet dictionary"""
    93         self.name = dnetdict["name"]
    94         # Other attributes are optional
    95         try:
    96             self.ip = socket.inet_ntoa(dnetdict["addr"].ip)
    97         except (KeyError, AttributeError, NameError):
    98             pass
    99         try:
   100             self.mac = dnetdict["link_addr"]
   101         except KeyError:
   102             pass
   103         self._update_pcapdata()
   104     
   105     def _update_pcapdata(self):
   106         """Supplement more info from pypcap and the Windows registry"""
   107         
   108         # XXX: We try eth0 - eth29 by bruteforce and match by IP address, 
   109         # because only the IP is available in both pypcap and dnet.
   110         # This may not work with unorthodox network configurations and is
   111         # slow because we have to walk through the Windows registry.
   112         for n in range(30):
   113             guess = "eth%s" % n
   114             win_name = pcapdnet.pcap.ex_name(guess)
   115             if win_name.endswith("}"):
   116                 try:
   117                     uuid = win_name[win_name.index("{"):win_name.index("}")+1]
   118                     keyname = r"SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\Interfaces\%s" % uuid
   119                     try:
   120                         key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, keyname)
   121                     except WindowsError:
   122                         log_loading.debug("Couldn't open 'HKEY_LOCAL_MACHINE\\%s' (for guessed pcap iface name '%s')." % (keyname, guess))
   123                         continue
   124                     try:    
   125                         fixed_ip = _winreg.QueryValueEx(key, "IPAddress")[0][0].encode("utf-8")
   126                     except (WindowsError, UnicodeDecodeError, IndexError):
   127                         fixed_ip = None
   128                     try:
   129                         dhcp_ip = _winreg.QueryValueEx(key, "DhcpIPAddress")[0].encode("utf-8")
   130                     except (WindowsError, UnicodeDecodeError, IndexError):
   131                         dhcp_ip = None
   132                     # "0.0.0.0" or None means the value is not set (at least not correctly).
   133                     # If both fixed_ip and dhcp_ip are set, fixed_ip takes precedence 
   134                     if fixed_ip is not None and fixed_ip != "0.0.0.0":
   135                         ip = fixed_ip
   136                     elif dhcp_ip is not None and dhcp_ip != "0.0.0.0":
   137                         ip = dhcp_ip
   138                     else:
   139                         continue
   140                 except IOError:
   141                     continue
   142                 else:
   143                     if ip == self.ip:
   144                         self.pcap_name = guess
   145                         self.win_name = win_name
   146                         self.uuid = uuid
   147                         break
   148         else:
   149             raise PcapNameNotFoundError
   150     
   151     def __repr__(self):
   152         return "<%s: %s %s %s pcap_name=%s win_name=%s>" % (self.__class__.__name__,
   153                      self.name, self.ip, self.mac, self.pcap_name, self.win_name)
   154 
   155 from UserDict import IterableUserDict
   156 
   157 class NetworkInterfaceDict(IterableUserDict):
   158     """Store information about network interfaces and convert between names""" 
   159     
   160     def load_from_dnet(self):
   161         """Populate interface table via dnet"""
   162         for i in pcapdnet.dnet.intf():
   163             try:
   164                 # XXX: Only Ethernet for the moment: localhost is not supported by dnet and pcap
   165                 # We only take interfaces that have an IP address, because the IP
   166                 # is used for the mapping between dnet and pcap interface names
   167                 # and this significantly improves Scapy's startup performance
   168                 if i["name"].startswith("eth") and "addr" in i:
   169                     self.data[i["name"]] = NetworkInterface(i)
   170             except (KeyError, PcapNameNotFoundError):
   171                 pass
   172         if len(self.data) == 0:
   173             log_loading.warning("No match between your pcap and dnet network interfaces found. "
   174                                 "You probably won't be able to send packets. "
   175                                 "Deactivating unneeded interfaces and restarting Scapy might help.")
   176     
   177     def pcap_name(self, devname):
   178         """Return pypcap device name for given libdnet/Scapy device name
   179         
   180         This mapping is necessary because pypcap numbers the devices differently."""
   181         
   182         try:
   183             pcap_name = self.data[devname].pcap_name
   184         except KeyError:
   185             raise ValueError("Unknown network interface %r" % devname)
   186         else:
   187             return pcap_name
   188             
   189     def devname(self, pcap_name):
   190         """Return libdnet/Scapy device name for given pypcap device name
   191         
   192         This mapping is necessary because pypcap numbers the devices differently."""
   193         
   194         for devname, iface in self.items():
   195             if iface.pcap_name == pcap_name:
   196                 return iface.name
   197         raise ValueError("Unknown pypcap network interface %r" % pcap_name)
   198     
   199     def show(self, resolve_mac=True):
   200         """Print list of available network interfaces in human readable form"""
   201         print "%s  %s  %s" % ("IFACE".ljust(5), "IP".ljust(15), "MAC")
   202         for iface_name in sorted(self.data.keys()):
   203             dev = self.data[iface_name]
   204             mac = str(dev.mac)
   205             if resolve_mac:
   206                 mac = conf.manufdb._resolve_MAC(mac)
   207             print "%s  %s  %s" % (str(dev.name).ljust(5), str(dev.ip).ljust(15), mac)     
   208             
   209 ifaces = NetworkInterfaceDict()
   210 ifaces.load_from_dnet()
   211 
   212 def pcap_name(devname):
   213     """Return pypcap device name for given libdnet/Scapy device name"""  
   214     try:
   215         pcap_name = ifaces.pcap_name(devname)
   216     except ValueError:
   217         # pcap.pcap() will choose a sensible default for sniffing if iface=None
   218         pcap_name = None
   219     return pcap_name            
   220 
   221 def devname(pcap_name):
   222     """Return libdnet/Scapy device name for given pypcap device name"""
   223     return ifaces.devname(pcap_name)
   224     
   225 def show_interfaces(resolve_mac=True):
   226     """Print list of available network interfaces"""
   227     return ifaces.show(resolve_mac)
   228 
   229 _orig_open_pcap = pcapdnet.open_pcap
   230 pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcap_name(iface),*args,**kargs)
   231 
   232 def read_routes():
   233     ok = 0
   234     routes = []
   235     ip = '(\d+\.\d+\.\d+\.\d+)'
   236     # On Vista and Windows 7 the gateway can be IP or 'On-link'.
   237     # But the exact 'On-link' string depends on the locale, so we allow any text.
   238     gw_pattern = '(.+)'
   239     metric_pattern = "(\d+)"
   240     delim = "\s+"        # The columns are separated by whitespace
   241     netstat_line = delim.join([ip, ip, gw_pattern, ip, metric_pattern])
   242     pattern = re.compile(netstat_line)
   243     f=os.popen("netstat -rn")
   244     for l in f.readlines():
   245         match = re.search(pattern,l)
   246         if match:
   247             dest   = match.group(1)
   248             mask   = match.group(2)
   249             gw     = match.group(3)
   250             netif  = match.group(4)
   251             metric = match.group(5)
   252             try:
   253                 intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest))
   254             except OSError:
   255                 log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s" % dest)
   256                 continue               
   257             if not intf.has_key("addr"):
   258                 break
   259             addr = str(intf["addr"])
   260             addr = addr.split("/")[0]
   261             
   262             dest = atol(dest)
   263             mask = atol(mask)
   264             # If the gateway is no IP we assume it's on-link
   265             gw_ipmatch = re.search('\d+\.\d+\.\d+\.\d+', gw)
   266             if gw_ipmatch:
   267                 gw = gw_ipmatch.group(0)
   268             else:
   269                 gw = netif
   270             routes.append((dest,mask,gw, str(intf["name"]), addr))
   271     f.close()
   272     return routes
   273 
   274 def read_routes6():
   275     return []
   276 
   277 def getmacbyip(ip, chainCC=0):
   278     """Return MAC address corresponding to a given IP address"""
   279     if isinstance(ip,Net):
   280         ip = iter(ip).next()
   281     tmp = map(ord, inet_aton(ip))
   282     if (tmp[0] & 0xf0) == 0xe0: # mcast @
   283         return "01:00:5e:%.2x:%.2x:%.2x" % (tmp[1]&0x7f,tmp[2],tmp[3])
   284     iff,a,gw = conf.route.route(ip)
   285     if ( (iff == LOOPBACK_NAME) or (ip == conf.route.get_if_bcast(iff)) ):
   286         return "ff:ff:ff:ff:ff:ff"
   287     # Windows uses local IP instead of 0.0.0.0 to represent locally reachable addresses
   288     ifip = str(pcapdnet.dnet.intf().get(iff)['addr'])
   289     if gw != ifip.split('/')[0]:
   290         ip = gw
   291 
   292     mac = conf.netcache.arp_cache.get(ip)
   293     if mac:
   294         return mac
   295 
   296     res = srp1(Ether(dst=ETHER_BROADCAST)/ARP(op="who-has", pdst=ip),
   297                type=ETH_P_ARP,
   298                iface = iff,
   299                timeout=2,
   300                verbose=0,
   301                chainCC=chainCC,
   302                nofilter=1)
   303     if res is not None:
   304         mac = res.payload.hwsrc
   305         conf.netcache.arp_cache[ip] = mac
   306         return mac
   307     return None
   308 
   309 import scapy.layers.l2
   310 scapy.layers.l2.getmacbyip = getmacbyip
   311 
   312 try:
   313     import readline
   314     console = readline.GetOutputFile()
   315 except (ImportError, AttributeError):
   316     log_loading.info("Could not get readline console. Will not interpret ANSI color codes.") 
   317 else:
   318     conf.readfunc = readline.rl.readline
   319     orig_stdout = sys.stdout
   320     sys.stdout = console
   321 
   322 
   323 
   324 
   325 
   326 def sndrcv(pks, pkt, timeout = 2, inter = 0, verbose=None, chainCC=0, retry=0, multi=0):
   327     if not isinstance(pkt, Gen):
   328         pkt = SetGen(pkt)
   329         
   330     if verbose is None:
   331         verbose = conf.verb
   332     debug.recv = plist.PacketList([],"Unanswered")
   333     debug.sent = plist.PacketList([],"Sent")
   334     debug.match = plist.SndRcvList([])
   335     nbrecv=0
   336     ans = []
   337     # do it here to fix random fields, so that parent and child have the same
   338     all_stimuli = tobesent = [p for p in pkt]
   339     notans = len(tobesent)
   340 
   341     hsent={}
   342     for i in tobesent:
   343         h = i.hashret()
   344         if h in hsent:
   345             hsent[h].append(i)
   346         else:
   347             hsent[h] = [i]
   348     if retry < 0:
   349         retry = -retry
   350         autostop=retry
   351     else:
   352         autostop=0
   353 
   354 
   355     while retry >= 0:
   356         found=0
   357     
   358         if timeout < 0:
   359             timeout = None
   360 
   361         pid=1
   362         try:
   363             if WINDOWS or pid == 0:
   364                 try:
   365                     try:
   366                         i = 0
   367                         if verbose:
   368                             print "Begin emission:"
   369                         for p in tobesent:
   370                             pks.send(p)
   371                             i += 1
   372                             time.sleep(inter)
   373                         if verbose:
   374                             print "Finished to send %i packets." % i
   375                     except SystemExit:
   376                         pass
   377                     except KeyboardInterrupt:
   378                         pass
   379                     except:
   380                         log_runtime.exception("--- Error sending packets")
   381                         log_runtime.info("--- Error sending packets")
   382                 finally:
   383                     try:
   384                         sent_times = [p.sent_time for p in all_stimuli if p.sent_time]
   385                     except:
   386                         pass
   387             if WINDOWS or pid > 0:
   388                 # Timeout starts after last packet is sent (as in Unix version) 
   389                 if timeout:
   390                     stoptime = time.time()+timeout
   391                 else:
   392                     stoptime = 0
   393                 remaintime = None
   394                 inmask = [pks.ins.fd]
   395                 try:
   396                     try:
   397                         while 1:
   398                             if stoptime:
   399                                 remaintime = stoptime-time.time()
   400                                 if remaintime <= 0:
   401                                     break
   402                             r = pks.recv(MTU)
   403                             if r is None:
   404                                 continue
   405                             ok = 0
   406                             h = r.hashret()
   407                             if h in hsent:
   408                                 hlst = hsent[h]
   409                                 for i in range(len(hlst)):
   410                                     if r.answers(hlst[i]):
   411                                         ans.append((hlst[i],r))
   412                                         if verbose > 1:
   413                                             os.write(1, "*")
   414                                         ok = 1                                
   415                                         if not multi:
   416                                             del(hlst[i])
   417                                             notans -= 1;
   418                                         else:
   419                                             if not hasattr(hlst[i], '_answered'):
   420                                                 notans -= 1;
   421                                             hlst[i]._answered = 1;
   422                                         break
   423                             if notans == 0 and not multi:
   424                                 break
   425                             if not ok:
   426                                 if verbose > 1:
   427                                     os.write(1, ".")
   428                                 nbrecv += 1
   429                                 if conf.debug_match:
   430                                     debug.recv.append(r)
   431                     except KeyboardInterrupt:
   432                         if chainCC:
   433                             raise
   434                 finally:
   435                     if WINDOWS:
   436                         for p,t in zip(all_stimuli, sent_times):
   437                             p.sent_time = t
   438         finally:
   439             pass
   440 
   441         remain = reduce(list.__add__, hsent.values(), [])
   442         if multi:
   443             remain = filter(lambda p: not hasattr(p, '_answered'), remain);
   444             
   445         if autostop and len(remain) > 0 and len(remain) != len(tobesent):
   446             retry = autostop
   447             
   448         tobesent = remain
   449         if len(tobesent) == 0:
   450             break
   451         retry -= 1
   452         
   453     if conf.debug_match:
   454         debug.sent=plist.PacketList(remain[:],"Sent")
   455         debug.match=plist.SndRcvList(ans[:])
   456 
   457     #clean the ans list to delete the field _answered
   458     if (multi):
   459         for s,r in ans:
   460             if hasattr(s, '_answered'):
   461                 del(s._answered)
   462     
   463     if verbose:
   464         print "\nReceived %i packets, got %i answers, remaining %i packets" % (nbrecv+len(ans), len(ans), notans)
   465     return plist.SndRcvList(ans),plist.PacketList(remain,"Unanswered")
   466 
   467 
   468 import scapy.sendrecv
   469 scapy.sendrecv.sndrcv = sndrcv
   470 
   471 def sniff(count=0, store=1, offline=None, prn = None, lfilter=None, L2socket=None, timeout=None, *arg, **karg):
   472     """Sniff packets
   473 sniff([count=0,] [prn=None,] [store=1,] [offline=None,] [lfilter=None,] + L2ListenSocket args) -> list of packets
   474 
   475   count: number of packets to capture. 0 means infinity
   476   store: wether to store sniffed packets or discard them
   477     prn: function to apply to each packet. If something is returned,
   478          it is displayed. Ex:
   479          ex: prn = lambda x: x.summary()
   480 lfilter: python function applied to each packet to determine
   481          if further action may be done
   482          ex: lfilter = lambda x: x.haslayer(Padding)
   483 offline: pcap file to read packets from, instead of sniffing them
   484 timeout: stop sniffing after a given time (default: None)
   485 L2socket: use the provided L2socket
   486     """
   487     c = 0
   488 
   489     if offline is None:
   490         if L2socket is None:
   491             L2socket = conf.L2listen
   492         s = L2socket(type=ETH_P_ALL, *arg, **karg)
   493     else:
   494         s = PcapReader(offline)
   495 
   496     lst = []
   497     if timeout is not None:
   498         stoptime = time.time()+timeout
   499     remain = None
   500     while 1:
   501         try:
   502             if timeout is not None:
   503                 remain = stoptime-time.time()
   504                 if remain <= 0:
   505                     break
   506 
   507             try:
   508                 p = s.recv(MTU)
   509             except PcapTimeoutElapsed:
   510                 continue
   511             if p is None:
   512                 break
   513             if lfilter and not lfilter(p):
   514                 continue
   515             if store:
   516                 lst.append(p)
   517             c += 1
   518             if prn:
   519                 r = prn(p)
   520                 if r is not None:
   521                     print r
   522             if count > 0 and c >= count:
   523                 break
   524         except KeyboardInterrupt:
   525             break
   526     s.close()
   527     return plist.PacketList(lst,"Sniffed")
   528 
   529 import scapy.sendrecv
   530 scapy.sendrecv.sniff = sniff
   531 
   532 def get_if_list():
   533     return sorted(ifaces.keys())
   534         
   535 def get_working_if():
   536     try:
   537         return devname(pcap.lookupdev())
   538     except Exception:
   539         return 'lo0'