Home | History | Annotate | Download | only in windows
      1 ## This file is part of Scapy
      2 ## See http://www.secdev.org/projects/scapy for more informations
      3 ## Copyright (C) Philippe Biondi <phil (at] secdev.org>
      4 ## Copyright (C) Gabriel Potter <gabriel (at] potter.fr>
      5 ## This program is published under a GPLv2 license
      6 
      7 """
      8 Customizations needed to support Microsoft Windows.
      9 """
     10 from __future__ import absolute_import
     11 from __future__ import print_function
     12 import os, re, sys, socket, time, itertools, platform
     13 import subprocess as sp
     14 from glob import glob
     15 import tempfile
     16 from threading import Thread, Event
     17 
     18 import scapy
     19 from scapy.config import conf, ConfClass
     20 from scapy.error import Scapy_Exception, log_loading, log_runtime, warning
     21 from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader, pretty_list
     22 from scapy.utils6 import construct_source_candidate_set
     23 from scapy.base_classes import Gen, Net, SetGen
     24 from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
     25 
     26 import scapy.modules.six as six
     27 from scapy.modules.six.moves import range, zip, input
     28 from scapy.compat import plain_str
     29 
     30 conf.use_pcap = False
     31 conf.use_dnet = False
     32 conf.use_winpcapy = True
     33 
     34 WINDOWS = (os.name == 'nt')
     35 NEW_RELEASE = None
     36 
     37 #hot-patching socket for missing variables on Windows
     38 import socket
     39 if not hasattr(socket, 'IPPROTO_IPIP'):
     40     socket.IPPROTO_IPIP=4
     41 if not hasattr(socket, 'IPPROTO_AH'):
     42     socket.IPPROTO_AH=51
     43 if not hasattr(socket, 'IPPROTO_ESP'):
     44     socket.IPPROTO_ESP=50
     45 if not hasattr(socket, 'IPPROTO_GRE'):
     46     socket.IPPROTO_GRE=47
     47 
     48 from scapy.arch import pcapdnet
     49 from scapy.arch.pcapdnet import *
     50 
     51 _WlanHelper = NPCAP_PATH + "\\WlanHelper.exe"
     52 
     53 import scapy.consts
     54 
     55 def is_new_release(ignoreVBS=False):
     56     if NEW_RELEASE and conf.prog.powershell is not None:
     57         return True
     58     release = platform.release()
     59     if conf.prog.powershell is None and not ignoreVBS:
     60         return False
     61     try:
     62          if float(release) >= 8:
     63              return True
     64     except ValueError:
     65         if (release=="post2008Server"):
     66             return True
     67     return False
     68 
     69 def _encapsulate_admin(cmd):
     70     """Encapsulate a command with an Administrator flag"""
     71     # To get admin access, we start a new powershell instance with admin
     72     # rights, which will execute the command
     73     return "Start-Process PowerShell -windowstyle hidden -Wait -Verb RunAs -ArgumentList '-command &{%s}'" % cmd
     74 
     75 class _PowershellManager(Thread):
     76     """Instance used to send multiple commands on the same Powershell process.
     77     Will be instantiated on loading and automatically stopped.
     78     """
     79     def __init__(self):
     80         # Start & redirect input
     81         if conf.prog.powershell:
     82             self.process = sp.Popen([conf.prog.powershell,
     83                                      "-NoLogo", "-NonInteractive",  # Do not print headers
     84                                      "-Command", "-"],  # Listen commands from stdin
     85                              stdout=sp.PIPE,
     86                              stdin=sp.PIPE,
     87                              stderr=sp.STDOUT)
     88             self.cmd = False
     89         else:  # Fallback on CMD (powershell-only commands will fail, but scapy use the VBS fallback)
     90             self.process = sp.Popen([conf.prog.cmd],
     91                              stdout=sp.PIPE,
     92                              stdin=sp.PIPE,
     93                              stderr=sp.STDOUT)
     94             self.cmd = True
     95         self.buffer = []
     96         self.running = True
     97         self.query_complete = Event()
     98         Thread.__init__(self)
     99         self.daemon = True
    100         self.start()
    101         if self.cmd:
    102             self.query(["echo @off"])  # Remove header
    103         else:
    104             self.query(["$FormatEnumerationLimit=-1"])  # Do not crop long IP lists
    105 
    106     def run(self):
    107         while self.running:
    108             read_line = self.process.stdout.readline().strip()
    109             if read_line == b"scapy_end":
    110                 self.query_complete.set()
    111             else:
    112                 self.buffer.append(read_line.decode("utf8", "ignore") if six.PY3 else read_line)
    113 
    114     def query(self, command):
    115         self.query_complete.clear()
    116         if not self.running:
    117             self.__init__(self)
    118         # Call powershell query using running process
    119         self.buffer = []
    120         # 'scapy_end' is used as a marker of the end of execution
    121         query = " ".join(command) + ("&" if self.cmd else ";") + " echo scapy_end\n"
    122         self.process.stdin.write(query.encode())
    123         self.process.stdin.flush()
    124         self.query_complete.wait()
    125         return self.buffer[1:]  # Crops first line: the command
    126 
    127     def close(self):
    128         self.running = False
    129         try:
    130             self.process.stdin.write("exit\n")
    131             self.process.terminate()
    132         except:
    133             pass
    134 
    135 def _exec_query_ps(cmd, fields):
    136     """Execute a PowerShell query, using the cmd command,
    137     and select and parse the provided fields.
    138     """
    139     if not conf.prog.powershell:
    140         raise OSError("Scapy could not detect powershell !")
    141     # Build query
    142     query_cmd = cmd + ['|', 'select %s' % ', '.join(fields),  # select fields
    143                        '|', 'fl',  # print as a list
    144                        '|', 'out-string', '-Width', '4096']  # do not crop
    145     l=[]
    146     # Ask the powershell manager to process the query
    147     stdout = POWERSHELL_PROCESS.query(query_cmd)
    148     # Process stdout
    149     for line in stdout:
    150         if not line.strip(): # skip empty lines
    151             continue
    152         sl = line.split(':', 1)
    153         if len(sl) == 1:
    154             l[-1] += sl[0].strip()
    155             continue
    156         else:
    157             l.append(sl[1].strip())
    158         if len(l) == len(fields):
    159             yield l
    160             l=[]
    161 
    162 def _vbs_exec_code(code, split_tag="@"):
    163     if not conf.prog.cscript:
    164         raise OSError("Scapy could not detect cscript !")
    165     tmpfile = tempfile.NamedTemporaryFile(mode="wb", suffix=".vbs", delete=False)
    166     tmpfile.write(raw(code))
    167     tmpfile.close()
    168     ps = sp.Popen([conf.prog.cscript, tmpfile.name],
    169                   stdout=sp.PIPE, stderr=open(os.devnull),
    170                   universal_newlines=True)
    171     for _ in range(3):
    172         # skip 3 first lines
    173         ps.stdout.readline()
    174     for line in ps.stdout:
    175         data = line.replace("\n", "").split(split_tag)
    176         for l in data:
    177             yield l
    178     os.unlink(tmpfile.name)
    179 
    180 def _vbs_get_hardware_iface_guid(devid):
    181     try:
    182         devid = str(int(devid) + 1)
    183         guid = next(iter(_vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\%s\\ServiceName")
    184 """ % devid)))
    185         guid = guid[:-1] if guid.endswith('}\n') else guid
    186         if guid.startswith('{') and guid.endswith('}'):
    187             return guid
    188     except StopIteration:
    189         return None
    190 
    191 # Some names differ between VBS and PS
    192 ## None: field will not be returned under VBS
    193 _VBS_WMI_FIELDS = {
    194     "Win32_NetworkAdapter": {
    195         "InterfaceDescription": "Description",
    196         # Note: when using VBS, the GUID is not the same than with Powershell
    197         # So we use get the device ID instead, then use _vbs_get_hardware_iface_guid
    198         # To get its real GUID
    199         "GUID": "DeviceID"
    200     },
    201     "*": {
    202         "Status": "State"
    203     }
    204 }
    205 
    206 _VBS_WMI_REPLACE = {
    207     "Win32_NetworkAdapterConfiguration": {
    208         "line.IPAddress": "\"{\" & Join( line.IPAddress, \", \" ) & \"}\"",
    209     }
    210 }
    211 
    212 _VBS_WMI_OUTPUT = {
    213     "Win32_NetworkAdapter": {
    214         "DeviceID": _vbs_get_hardware_iface_guid,
    215     }
    216 }
    217 
    218 def _exec_query_vbs(cmd, fields):
    219     """Execute a query using VBS. Currently Get-WmiObject, Get-Service
    220     queries are supported.
    221 
    222     """
    223     if not(len(cmd) == 2 and cmd[0] in ["Get-WmiObject", "Get-Service"]):
    224         return
    225     action = cmd[0]
    226     fields = [_VBS_WMI_FIELDS.get(cmd[1], _VBS_WMI_FIELDS.get("*", {})).get(fld, fld) for fld in fields]
    227     parsed_command = "WScript.Echo " + " & \" @ \" & ".join("line.%s" % fld for fld in fields
    228                            if fld is not None)
    229     # The IPAddress is an array: convert it to a string
    230     for key,val in _VBS_WMI_REPLACE.get(cmd[1], {}).items():
    231         parsed_command = parsed_command.replace(key, val)
    232     if action == "Get-WmiObject":
    233         values = _vbs_exec_code("""Set wmi = GetObject("winmgmts:")
    234 Set lines = wmi.InstancesOf("%s")
    235 On Error Resume Next
    236 Err.clear
    237 For Each line in lines
    238   %s
    239 Next
    240 """ % (cmd[1], parsed_command), "@")
    241     elif action == "Get-Service":
    242         values = _vbs_exec_code("""serviceName = "%s"
    243 Set wmi = GetObject("winmgmts://./root/cimv2")
    244 Set line = wmi.Get("Win32_Service.Name='" & serviceName & "'")
    245 %s
    246 """ % (cmd[1], parsed_command), "@")
    247 
    248     while True:
    249         yield [None if fld is None else
    250                _VBS_WMI_OUTPUT.get(cmd[1], {}).get(fld, lambda x: x)(
    251                    next(values).strip()
    252                )
    253                for fld in fields]
    254 
    255 def exec_query(cmd, fields):
    256     """Execute a system query using PowerShell if it is available, and
    257     using VBS/cscript as a fallback.
    258 
    259     """
    260     if conf.prog.powershell is None:
    261         return _exec_query_vbs(cmd, fields)
    262     return _exec_query_ps(cmd, fields)
    263 
    264 def _where(filename, dirs=None, env="PATH"):
    265     """Find file in current dir, in deep_lookup cache or in system path"""
    266     if dirs is None:
    267         dirs = []
    268     if not isinstance(dirs, list):
    269         dirs = [dirs]
    270     if glob(filename):
    271         return filename
    272     paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs
    273     for path in paths:
    274         for match in glob(os.path.join(path, filename)):
    275             if match:
    276                 return os.path.normpath(match)
    277     raise IOError("File not found: %s" % filename)
    278 
    279 def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
    280     """Find executable in current dir, system path or given ProgramFiles subdir"""
    281     fns = [filename] if filename.endswith(".exe") else [filename+".exe", filename]
    282     for fn in fns:
    283         try:
    284             if installsubdir is None:
    285                 path = _where(fn)
    286             else:
    287                 path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)])
    288         except IOError:
    289             path = None
    290         else:
    291             break        
    292     return path
    293 
    294 
    295 class WinProgPath(ConfClass):
    296     _default = "<System default>"
    297     def __init__(self):
    298         self._reload()
    299 
    300     def _reload(self):
    301         # We try some magic to find the appropriate executables
    302         self.pdfreader = win_find_exe("AcroRd32") 
    303         self.psreader = win_find_exe("gsview32")
    304         self.dot = win_find_exe("dot")
    305         self.tcpdump = win_find_exe("windump")
    306         self.tshark = win_find_exe("tshark")
    307         self.tcpreplay = win_find_exe("tcpreplay")
    308         self.display = self._default
    309         self.hexedit = win_find_exe("hexer")
    310         self.sox = win_find_exe("sox")
    311         self.wireshark = win_find_exe("wireshark", "wireshark")
    312         self.powershell = win_find_exe(
    313             "powershell",
    314             installsubdir="System32\\WindowsPowerShell\\v1.0",
    315             env="SystemRoot"
    316         )
    317         self.cscript = win_find_exe("cscript", installsubdir="System32",
    318                                env="SystemRoot")
    319         self.cmd = win_find_exe("cmd", installsubdir="System32",
    320                                env="SystemRoot")
    321         if self.wireshark:
    322             manu_path = load_manuf(os.path.sep.join(self.wireshark.split(os.path.sep)[:-1])+os.path.sep+"manuf")
    323             scapy.data.MANUFDB = conf.manufdb = manu_path
    324         
    325         self.os_access = (self.powershell is not None) or (self.cscript is not None)
    326 
    327 conf.prog = WinProgPath()
    328 if not conf.prog.os_access:
    329     warning("Scapy did not detect powershell and cscript ! Routes, interfaces and much more won't work !", onlyOnce=True)
    330 
    331 if conf.prog.tcpdump and conf.use_npcap and conf.prog.os_access:
    332     def test_windump_npcap():
    333         """Return wether windump version is correct or not"""
    334         try:
    335             p_test_windump = sp.Popen([conf.prog.tcpdump, "-help"], stdout=sp.PIPE, stderr=sp.STDOUT)
    336             stdout, err = p_test_windump.communicate()
    337             _output = stdout.lower()
    338             return b"npcap" in _output and not b"winpcap" in _output
    339         except:
    340             return False
    341     windump_ok = test_windump_npcap()
    342     if not windump_ok:
    343         warning("The installed Windump version does not work with Npcap ! Refer to 'Winpcap/Npcap conflicts' in scapy's doc", onlyOnce=True)
    344     del windump_ok
    345 
    346 # Auto-detect release
    347 NEW_RELEASE = is_new_release()
    348 
    349 class PcapNameNotFoundError(Scapy_Exception):
    350     pass    
    351 
    352 def is_interface_valid(iface):
    353     if "guid" in iface and iface["guid"]:
    354         # Fix '-' instead of ':'
    355         if "mac" in iface:
    356             iface["mac"] = iface["mac"].replace("-", ":")
    357         return True
    358     return False
    359 
    360 def get_windows_if_list():
    361     """Returns windows interfaces."""
    362     if not conf.prog.os_access:
    363         return []
    364     if is_new_release():
    365         # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
    366         # Careful: this is weird, but Get-NetAdaptater works like: (Name isn't the interface name)
    367         # Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
    368         # ----                      --------------------                    ------- ------       ----------             ---------
    369         # Ethernet                  Killer E2200 Gigabit Ethernet Contro...      13 Up           D0-50-99-56-DD-F9         1 Gbps
    370         query = exec_query(['Get-NetAdapter'],
    371                            ['InterfaceDescription', 'InterfaceIndex', 'Name',
    372                             'InterfaceGuid', 'MacAddress', 'InterfaceAlias']) # It is normal that it is in this order
    373     else:
    374         query = exec_query(['Get-WmiObject', 'Win32_NetworkAdapter'],
    375                            ['Name', 'InterfaceIndex', 'InterfaceDescription',
    376                             'GUID', 'MacAddress', 'NetConnectionID'])
    377     return [
    378         iface for iface in
    379         (dict(zip(['name', 'win_index', 'description', 'guid', 'mac', 'netid'], line))
    380          for line in query)
    381         if is_interface_valid(iface)
    382     ]
    383 
    384 def get_ips(v6=False):
    385     """Returns all available IPs matching to interfaces, using the windows system.
    386     Should only be used as a WinPcapy fallback."""
    387     res = {}
    388     for descr, ipaddr in exec_query(['Get-WmiObject',
    389                                      'Win32_NetworkAdapterConfiguration'],
    390                                     ['Description', 'IPAddress']):
    391         if ipaddr.strip():
    392             res[descr] = ipaddr.split(",", 1)[v6].strip('{}').strip()
    393     return res
    394 
    395 def get_ip_from_name(ifname, v6=False):
    396     """Backward compatibility: indirectly calls get_ips
    397     Deprecated."""
    398     return get_ips(v6=v6).get(ifname, "")
    399         
    400 class NetworkInterface(object):
    401     """A network interface of your local host"""
    402     
    403     def __init__(self, data=None):
    404         self.name = None
    405         self.ip = None
    406         self.mac = None
    407         self.pcap_name = None
    408         self.description = None
    409         self.data = data
    410         self.invalid = False
    411         self.raw80211 = None
    412         if data is not None:
    413             self.update(data)
    414 
    415     def update(self, data):
    416         """Update info about network interface according to given dnet dictionary"""
    417         if 'netid' in data and data['netid'] == scapy.consts.LOOPBACK_NAME:
    418             # Force LOOPBACK_NAME: Some Windows systems overwrite 'name'
    419             self.name = scapy.consts.LOOPBACK_NAME
    420         else:
    421             self.name = data['name']
    422         self.description = data['description']
    423         self.win_index = data['win_index']
    424         self.guid = data['guid']
    425         if 'invalid' in data:
    426             self.invalid = data['invalid']
    427         # Other attributes are optional
    428         self._update_pcapdata()
    429 
    430         try:
    431             # Npcap loopback interface
    432             if self.name == scapy.consts.LOOPBACK_NAME and conf.use_npcap:
    433                 # https://nmap.org/npcap/guide/npcap-devguide.html
    434                 self.mac = "00:00:00:00:00:00"
    435                 self.ip = "127.0.0.1"
    436                 conf.cache_ipaddrs[self.pcap_name] = socket.inet_aton(self.ip)
    437                 return
    438             else:
    439                 self.mac = data['mac']
    440         except KeyError:
    441             pass
    442 
    443         try:
    444             self.ip = socket.inet_ntoa(get_if_raw_addr(self))
    445         except (TypeError, NameError):
    446             pass
    447 
    448         try:
    449             # Windows native loopback interface
    450             if not self.ip and self.name == scapy.consts.LOOPBACK_NAME:
    451                 self.ip = "127.0.0.1"
    452                 conf.cache_ipaddrs[self.pcap_name] = socket.inet_aton(self.ip)
    453         except (KeyError, AttributeError, NameError) as e:
    454             print(e)
    455 
    456     def _update_pcapdata(self):
    457         if self.is_invalid():
    458             return
    459         for i in get_if_list():
    460             if i.endswith(self.data['guid']):
    461                 self.pcap_name = i
    462                 return
    463 
    464         raise PcapNameNotFoundError
    465 
    466     def is_invalid(self):
    467         return self.invalid
    468 
    469     def _check_npcap_requirement(self):
    470         if not conf.use_npcap:
    471             raise OSError("This operation requires Npcap.")
    472         if self.raw80211 is None:
    473             # This checks if npcap has Dot11 enabled and if the interface is compatible,
    474             # by looking for the npcap/Parameters/Dot11Adapters key in the registry.
    475             try:
    476                 dot11adapters = next(iter(_vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\npcap\\Parameters\\Dot11Adapters")""")))
    477             except StopIteration:
    478                 pass
    479             else:
    480                 self.raw80211 = ("\\Device\\" + self.guid).lower() in dot11adapters.lower()
    481         if not self.raw80211:
    482             raise Scapy_Exception("This interface does not support raw 802.11")
    483 
    484     def mode(self):
    485         """Get the interface operation mode.
    486         Only available with Npcap."""
    487         self._check_npcap_requirement()
    488         return sp.Popen([_WlanHelper, self.guid[1:-1], "mode"], stdout=sp.PIPE).communicate()[0].strip()
    489 
    490     def availablemodes(self):
    491         """Get all available interface modes.
    492         Only available with Npcap."""
    493         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    494         self._check_npcap_requirement()
    495         return sp.Popen([_WlanHelper, self.guid[1:-1], "modes"], stdout=sp.PIPE).communicate()[0].strip().split(",")
    496 
    497     def setmode(self, mode):
    498         """Set the interface mode. It can be:
    499         - 0 or managed: Managed Mode (aka "Extensible Station Mode")
    500         - 1 or monitor: Monitor Mode (aka "Network Monitor Mode")
    501         - 2 or master: Master Mode (aka "Extensible Access Point") (supported from Windows 7 and later)
    502         - 3 or wfd_device: The Wi-Fi Direct Device operation mode (supported from Windows 8 and later)
    503         - 4 or wfd_owner: The Wi-Fi Direct Group Owner operation mode (supported from Windows 8 and later)
    504         - 5 or wfd_client: The Wi-Fi Direct Client operation mode (supported from Windows 8 and later)
    505         Only available with Npcap."""
    506         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    507         self._check_npcap_requirement()
    508         _modes = {
    509             0: "managed",
    510             1: "monitor",
    511             2: "master",
    512             3: "wfd_device",
    513             4: "wfd_owner",
    514             5: "wfd_client"
    515         }
    516         m = _modes.get(mode, "unknown") if isinstance(mode, int) else mode
    517         return sp.call(_WlanHelper + " " + self.guid[1:-1] + " mode " + m)
    518 
    519     def channel(self):
    520         """Get the channel of the interface.
    521         Only available with Npcap."""
    522         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    523         self._check_npcap_requirement()
    524         return sp.Popen([_WlanHelper, self.guid[1:-1], "channel"],
    525                         stdout=sp.PIPE).communicate()[0].strip()
    526 
    527     def setchannel(self, channel):
    528         """Set the channel of the interface (1-14):
    529         Only available with Npcap."""
    530         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    531         self._check_npcap_requirement()
    532         return sp.call(_WlanHelper + " " + self.guid[1:-1] + " channel " + str(channel))
    533 
    534     def frequence(self):
    535         """Get the frequence of the interface.
    536         Only available with Npcap."""
    537         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    538         self._check_npcap_requirement()
    539         return sp.Popen([_WlanHelper, self.guid[1:-1], "freq"], stdout=sp.PIPE).communicate()[0].strip()
    540 
    541     def setfrequence(self, freq):
    542         """Set the channel of the interface (1-14):
    543         Only available with Npcap."""
    544         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    545         self._check_npcap_requirement()
    546         return sp.call(_WlanHelper + " " + self.guid[1:-1] + " freq " + str(freq))
    547 
    548     def availablemodulations(self):
    549         """Get all available 802.11 interface modulations.
    550         Only available with Npcap."""
    551         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    552         self._check_npcap_requirement()
    553         return sp.Popen([_WlanHelper, self.guid[1:-1], "modus"], stdout=sp.PIPE).communicate()[0].strip().split(",")
    554 
    555     def modulation(self):
    556         """Get the 802.11 modulation of the interface.
    557         Only available with Npcap."""
    558         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    559         self._check_npcap_requirement()
    560         return sp.Popen([_WlanHelper, self.guid[1:-1], "modu"], stdout=sp.PIPE).communicate()[0].strip()
    561 
    562     def setmodulation(self, modu):
    563         """Set the interface modulation. It can be:
    564            - 0: dsss
    565            - 1: fhss
    566            - 2: irbaseband
    567            - 3: ofdm
    568            - 4: hrdss
    569            - 5: erp
    570            - 6: ht
    571            - 7: vht
    572            - 8: ihv
    573            - 9: mimo-ofdm
    574            - 10: mimo-ofdm
    575         Only available with Npcap."""
    576         # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11
    577         self._check_npcap_requirement()
    578         _modus = {
    579             0: "dsss",
    580             1: "fhss",
    581             2: "irbaseband",
    582             3: "ofdm",
    583             4: "hrdss",
    584             5: "erp",
    585             6: "ht",
    586             7: "vht",
    587             8: "ihv",
    588             9: "mimo-ofdm",
    589             10: "mimo-ofdm",
    590         }
    591         m = _modus.get(modu, "unknown") if isinstance(modu, int) else modu
    592         return sp.call(_WlanHelper + " " + self.guid[1:-1] + " mode " + m)
    593 
    594     def __repr__(self):
    595         return "<%s %s %s>" % (self.__class__.__name__, self.name, self.guid)
    596 
    597 def pcap_service_name():
    598     """Return the pcap adapter service's name"""
    599     return "npcap" if conf.use_npcap else "npf"
    600 
    601 def pcap_service_status():
    602     """Returns a tuple (name, description, started) of the windows pcap adapter"""
    603     for i in exec_query(['Get-Service', pcap_service_name()], ['Name', 'DisplayName', 'Status']):
    604         name = i[0]
    605         description = i[1]
    606         started = (i[2].lower().strip() == 'running')
    607         if name == pcap_service_name():
    608             return (name, description, started)
    609     return (None, None, None)
    610 
    611 def pcap_service_control(action, askadmin=True):
    612     """Util to run pcap control command"""
    613     if not conf.prog.powershell:
    614         return False
    615     command = action + ' ' + pcap_service_name()
    616     stdout = POWERSHELL_PROCESS.query([_encapsulate_admin(command) if askadmin else command])
    617     return "error" not in "".join(stdout).lower()
    618 
    619 def pcap_service_start(askadmin=True):
    620     """Starts the pcap adapter. Will ask for admin. Returns True if success"""
    621     return pcap_service_control('Start-Service', askadmin=askadmin)
    622 
    623 def pcap_service_stop(askadmin=True):
    624     """Stops the pcap adapter. Will ask for admin. Returns True if success"""
    625     return pcap_service_control('Stop-Service', askadmin=askadmin) 
    626     
    627 from scapy.modules.six.moves import UserDict
    628 
    629 class NetworkInterfaceDict(UserDict):
    630     """Store information about network interfaces and convert between names""" 
    631     def load_from_powershell(self):
    632         if not conf.prog.os_access:
    633             return
    634         ifaces_ips = None
    635         for i in get_windows_if_list():
    636             try:
    637                 interface = NetworkInterface(i)
    638                 self.data[interface.guid] = interface
    639                 # If no IP address was detected using winpcap and if
    640                 # the interface is not the loopback one, look for
    641                 # internal windows interfaces
    642                 if not interface.ip:
    643                     if not ifaces_ips:  # ifaces_ips is used as a cache
    644                         ifaces_ips = get_ips()
    645                     # If it exists, retrieve the interface's IP from the cache
    646                     interface.ip = ifaces_ips.get(interface.name, "")
    647             except (KeyError, PcapNameNotFoundError):
    648                 pass
    649         
    650         if not self.data and conf.use_winpcapy:
    651             _detect = pcap_service_status()
    652             def _ask_user():
    653                 if not conf.interactive:
    654                     return False
    655                 while True:
    656                     _confir = input("Do you want to start it ? (yes/no) [y]: ").lower().strip()
    657                     if _confir in ["yes", "y", ""]:
    658                         return True
    659                     elif _confir in ["no", "n"]:
    660                         return False
    661                 return False
    662             _error_msg = "No match between your pcap and windows network interfaces found. "
    663             if _detect[0] and not _detect[2] and not (hasattr(self, "restarted_adapter") and self.restarted_adapter):
    664                 warning("Scapy has detected that your pcap service is not running !")
    665                 if not conf.interactive or _ask_user():
    666                     succeed = pcap_service_start(askadmin=conf.interactive)
    667                     self.restarted_adapter = True
    668                     if succeed:
    669                         log_loading.info("Pcap service started !")
    670                         self.load_from_powershell()
    671                         return
    672                 _error_msg = "Could not start the pcap service ! "
    673             warning(_error_msg +
    674                     "You probably won't be able to send packets. "
    675                     "Deactivating unneeded interfaces and restarting Scapy might help. "
    676                     "Check your winpcap and powershell installation, and access rights.", onlyOnce=True)
    677         else:
    678             # Loading state: remove invalid interfaces
    679             self.remove_invalid_ifaces()
    680             # Replace LOOPBACK_INTERFACE
    681             try:
    682                 scapy.consts.LOOPBACK_INTERFACE = self.dev_from_name(
    683                     scapy.consts.LOOPBACK_NAME,
    684                 )
    685             except:
    686                 pass
    687 
    688     def dev_from_name(self, name):
    689         """Return the first pcap device name for a given Windows
    690         device name.
    691         """
    692         for iface in six.itervalues(self):
    693             if iface.name == name:
    694                 return iface
    695         raise ValueError("Unknown network interface %r" % name)
    696 
    697     def dev_from_pcapname(self, pcap_name):
    698         """Return Windows device name for given pcap device name."""
    699         for iface in six.itervalues(self):
    700             if iface.pcap_name == pcap_name:
    701                 return iface
    702         raise ValueError("Unknown pypcap network interface %r" % pcap_name)
    703 
    704     def dev_from_index(self, if_index):
    705         """Return interface name from interface index"""
    706         for devname, iface in six.iteritems(self):
    707             if iface.win_index == str(if_index):
    708                 return iface
    709         if str(if_index) == "1":
    710             # Test if the loopback interface is set up
    711             if isinstance(scapy.consts.LOOPBACK_INTERFACE, NetworkInterface):
    712                 return scapy.consts.LOOPBACK_INTERFACE
    713         raise ValueError("Unknown network interface index %r" % if_index)
    714 
    715     def remove_invalid_ifaces(self):
    716         """Remove all invalid interfaces"""
    717         for devname in list(self.keys()):
    718             iface = self.data[devname]
    719             if iface.is_invalid():
    720                 self.data.pop(devname)
    721 
    722     def reload(self):
    723         """Reload interface list"""
    724         self.restarted_adapter = False
    725         self.data.clear()
    726         self.load_from_powershell()
    727 
    728     def show(self, resolve_mac=True, print_result=True):
    729         """Print list of available network interfaces in human readable form"""
    730         res = []
    731         for iface_name in sorted(self.data):
    732             dev = self.data[iface_name]
    733             mac = dev.mac
    734             if resolve_mac and conf.manufdb:
    735                 mac = conf.manufdb._resolve_MAC(mac)
    736             res.append((str(dev.win_index).ljust(5), str(dev.name).ljust(35), str(dev.ip).ljust(15), mac))
    737 
    738         res = pretty_list(res, [("INDEX", "IFACE", "IP", "MAC")])
    739         if print_result:
    740             print(res)
    741         else:
    742             return res
    743 
    744     def __repr__(self):
    745         return self.show(print_result=False)
    746 
    747 # Init POWERSHELL_PROCESS
    748 POWERSHELL_PROCESS = _PowershellManager()
    749 
    750 IFACES = NetworkInterfaceDict()
    751 IFACES.load_from_powershell()
    752 
    753 def pcapname(dev):
    754     """Return pypcap device name for given interface or libdnet/Scapy
    755     device name.
    756 
    757     """
    758     if isinstance(dev, NetworkInterface):
    759         if dev.is_invalid():
    760             return None
    761         return dev.pcap_name
    762     try:
    763         return IFACES.dev_from_name(dev).pcap_name
    764     except ValueError:
    765         if conf.use_pcap:
    766             # pcap.pcap() will choose a sensible default for sniffing if
    767             # iface=None
    768             return None
    769         raise
    770 
    771 def dev_from_pcapname(pcap_name):
    772     """Return libdnet/Scapy device name for given pypcap device name"""
    773     return IFACES.dev_from_pcapname(pcap_name)
    774 
    775 def dev_from_index(if_index):
    776     """Return Windows adapter name for given Windows interface index"""
    777     return IFACES.dev_from_index(if_index)
    778     
    779 def show_interfaces(resolve_mac=True):
    780     """Print list of available network interfaces"""
    781     return IFACES.show(resolve_mac)
    782 
    783 _orig_open_pcap = pcapdnet.open_pcap
    784 pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcapname(iface),*args,**kargs)
    785 
    786 get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: (
    787     ARPHDR_ETHER, mac2str(IFACES.dev_from_pcapname(pcapname(iface)).mac)
    788 )
    789 
    790 def _read_routes_xp():
    791     # The InterfaceIndex in Win32_IP4RouteTable does not match the
    792     # InterfaceIndex in Win32_NetworkAdapter under some platforms
    793     # (namely Windows XP): let's try an IP association
    794     routes = []
    795     partial_routes = []
    796     # map local IP addresses to interfaces
    797     local_addresses = {iface.ip: iface for iface in six.itervalues(IFACES)}
    798     iface_indexes = {}
    799     for line in exec_query(['Get-WmiObject', 'Win32_IP4RouteTable'],
    800                            ['Name', 'Mask', 'NextHop', 'InterfaceIndex', 'Metric1']):
    801         if line[2] in local_addresses:
    802             iface = local_addresses[line[2]]
    803             # This gives us an association InterfaceIndex <-> interface
    804             iface_indexes[line[3]] = iface
    805             routes.append((atol(line[0]), atol(line[1]), "0.0.0.0", iface,
    806                            iface.ip, int(line[4])))
    807         else:
    808             partial_routes.append((atol(line[0]), atol(line[1]), line[2],
    809                                    line[3], int(line[4])))
    810     for dst, mask, gw, ifidx, metric in partial_routes:
    811         if ifidx in iface_indexes:
    812             iface = iface_indexes[ifidx]
    813             routes.append((dst, mask, gw, iface, iface.ip, metric))
    814     return routes
    815 
    816 def _read_routes_7():
    817     routes=[]
    818     for line in exec_query(['Get-WmiObject', 'Win32_IP4RouteTable'],
    819                            ['Name', 'Mask', 'NextHop', 'InterfaceIndex', 'Metric1']):
    820         try:
    821             iface = dev_from_index(line[3])
    822             ip = "127.0.0.1" if line[3] == "1" else iface.ip # Force loopback on iface 1
    823             routes.append((atol(line[0]), atol(line[1]), line[2], iface, ip, int(line[4])))
    824         except ValueError:
    825             continue
    826     return routes
    827         
    828 def read_routes():
    829     routes = []
    830     if not conf.prog.os_access:
    831         return routes
    832     release = platform.release()
    833     try:
    834         if is_new_release():
    835             routes = _read_routes_post2008()
    836         elif release == "XP":
    837             routes = _read_routes_xp()
    838         else:
    839             routes = _read_routes_7()
    840     except Exception as e:    
    841         warning("Error building scapy IPv4 routing table : %s", e, onlyOnce=True)
    842     else:
    843         if not routes:
    844             warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually", onlyOnce=True)
    845     return routes
    846 
    847 def _get_metrics(ipv6=False):
    848     """Returns a dict containing all IPv4 or IPv6 interfaces' metric,
    849     ordered by their interface index.
    850     """
    851     query_cmd = "netsh interface " + ("ipv6" if ipv6 else "ipv4") + " show interfaces level=verbose"
    852     stdout = POWERSHELL_PROCESS.query([query_cmd])
    853     res = {}
    854     _buffer = []
    855     _pattern = re.compile(".*:\s+(\d+)")
    856     for _line in stdout:
    857         if not _line.strip() and len(_buffer) > 0:
    858             if_index = re.search(_pattern, _buffer[3]).group(1)
    859             if_metric = int(re.search(_pattern, _buffer[5]).group(1))
    860             res[if_index] = if_metric
    861             _buffer = []
    862         else:
    863             _buffer.append(_line)
    864     return res
    865 
    866 def _read_routes_post2008():
    867     routes = []
    868     if4_metrics = None
    869     # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
    870     # Get-NetRoute -AddressFamily IPV4 | select ifIndex, DestinationPrefix, NextHop, RouteMetric, InterfaceMetric | fl
    871     for line in exec_query(['Get-NetRoute', '-AddressFamily IPV4'], ['ifIndex', 'DestinationPrefix', 'NextHop', 'RouteMetric', 'InterfaceMetric']):
    872         try:
    873             iface = dev_from_index(line[0])
    874             if iface.ip == "0.0.0.0":
    875                 continue
    876         except:
    877             continue
    878         # try:
    879         #     intf = pcapdnet.dnet.intf().get_dst(pcapdnet.dnet.addr(type=2, addrtxt=dest))
    880         # except OSError:
    881         #     log_loading.warning("Building Scapy's routing table: Couldn't get outgoing interface for destination %s", dest)
    882         #     continue
    883         dest, mask = line[1].split('/')
    884         ip = "127.0.0.1" if line[0] == "1" else iface.ip # Force loopback on iface 1
    885         if not line[4].strip():  # InterfaceMetric is not available. Load it from netsh
    886             if not if4_metrics:
    887                  if4_metrics = _get_metrics()
    888             metric = int(line[3]) + if4_metrics.get(iface.win_index, 0)  # RouteMetric + InterfaceMetric
    889         else:
    890             metric = int(line[3]) + int(line[4])  # RouteMetric + InterfaceMetric
    891         routes.append((atol(dest), itom(int(mask)),
    892                        line[2], iface, ip, metric))
    893     return routes
    894 
    895 ############
    896 ### IPv6 ###
    897 ############
    898 
    899 def in6_getifaddr():
    900     """
    901     Returns all IPv6 addresses found on the computer
    902     """
    903     ifaddrs = []
    904     for ifaddr in in6_getifaddr_raw():
    905         try:
    906             ifaddrs.append((ifaddr[0], ifaddr[1], dev_from_pcapname(ifaddr[2])))
    907         except ValueError:
    908             pass
    909     # Appends Npcap loopback if available
    910     if conf.use_npcap and scapy.consts.LOOPBACK_INTERFACE:
    911         ifaddrs.append(("::1", 0, scapy.consts.LOOPBACK_INTERFACE))
    912     return ifaddrs
    913 
    914 def _append_route6(routes, dpref, dp, nh, iface, lifaddr, metric):
    915     cset = [] # candidate set (possible source addresses)
    916     if iface.name == scapy.consts.LOOPBACK_NAME:
    917         if dpref == '::':
    918             return
    919         cset = ['::1']
    920     else:
    921         devaddrs = (x for x in lifaddr if x[2] == iface)
    922         cset = construct_source_candidate_set(dpref, dp, devaddrs)
    923     if not cset:
    924         return
    925     # APPEND (DESTINATION, NETMASK, NEXT HOP, IFACE, CANDIDATS, METRIC)
    926     routes.append((dpref, dp, nh, iface, cset, metric))
    927 
    928 def _read_routes6_post2008():
    929     routes6 = []
    930     # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
    931     # Get-NetRoute -AddressFamily IPV6 | select ifIndex, DestinationPrefix, NextHop | fl
    932     lifaddr = in6_getifaddr()
    933     for line in exec_query(['Get-NetRoute', '-AddressFamily IPV6'], ['ifIndex', 'DestinationPrefix', 'NextHop', 'RouteMetric', 'InterfaceMetric']):
    934         try:
    935             if_index = line[0]
    936             iface = dev_from_index(if_index)
    937         except:
    938             continue
    939 
    940         dpref, dp = line[1].split('/')
    941         dp = int(dp)
    942         nh = line[2]
    943         metric = int(line[3])+int(line[4])
    944 
    945         _append_route6(routes6, dpref, dp, nh, iface, lifaddr, metric)
    946     return routes6
    947 
    948 def _read_routes6_7():
    949     # Not supported in powershell, we have to use netsh
    950     routes = []
    951     query_cmd = "netsh interface ipv6 show route level=verbose"
    952     stdout = POWERSHELL_PROCESS.query([query_cmd])
    953     lifaddr = in6_getifaddr()
    954     if6_metrics = _get_metrics(ipv6=True)
    955     # Define regexes
    956     r_int = [".*:\s+(\d+)"]
    957     r_all = ["(.*)"]
    958     r_ipv6 = [".*:\s+([A-z|0-9|:]+(\/\d+)?)"]
    959     # Build regex list for each object
    960     regex_list = r_ipv6*2 + r_int + r_all*3 + r_int + r_all*3
    961     current_object =  []
    962     index = 0
    963     for l in stdout:
    964         if not l.strip():
    965             if not current_object:
    966                 continue
    967             
    968             if len(current_object) == len(regex_list):
    969                 try:
    970                     if_index = current_object[2]
    971                     iface = dev_from_index(if_index)
    972                 except:
    973                     current_object = []
    974                     index = 0
    975                     continue
    976                 _ip = current_object[0].split("/")
    977                 dpref = _ip[0]
    978                 dp = int(_ip[1])
    979                 _match = re.search(r_ipv6[0], current_object[3])
    980                 nh = "::"
    981                 if _match: # Detect if Next Hop is specified (if not, it will be the IFName)
    982                     _nhg1 = _match.group(1)
    983                     nh = _nhg1 if re.match(".*:.*:.*", _nhg1) else "::"
    984                 metric = int(current_object[6]) + if6_metrics.get(if_index, 0)
    985                 _append_route6(routes, dpref, dp, nh, iface, lifaddr, metric)
    986 
    987             # Reset current object
    988             current_object = []
    989             index = 0
    990         else:
    991             pattern = re.compile(regex_list[index])
    992             match = re.search(pattern, l)
    993             if match:
    994                 current_object.append(match.group(1))
    995                 index = index + 1
    996     return routes
    997 
    998 def read_routes6():
    999     routes6 = []
   1000     if not conf.prog.os_access:
   1001         return routes6
   1002     try:
   1003         if is_new_release():
   1004             routes6 = _read_routes6_post2008()
   1005         else:
   1006             routes6 = _read_routes6_7()
   1007     except Exception as e:    
   1008         warning("Error building scapy IPv6 routing table : %s", e, onlyOnce=True)
   1009     return routes6
   1010 
   1011 def get_working_if():
   1012     try:
   1013         # return the interface associated with the route with smallest
   1014         # mask (route by default if it exists)
   1015         return min(conf.route.routes, key=lambda x: x[1])[3]
   1016     except ValueError:
   1017         # no route
   1018         return scapy.consts.LOOPBACK_INTERFACE
   1019 
   1020 def _get_valid_guid():
   1021     if scapy.consts.LOOPBACK_INTERFACE:
   1022         return scapy.consts.LOOPBACK_INTERFACE.guid
   1023     else:
   1024         for i in six.itervalues(IFACES):
   1025             if not i.is_invalid():
   1026                 return i.guid
   1027 
   1028 def route_add_loopback(routes=None, ipv6=False, iflist=None):
   1029     """Add a route to 127.0.0.1 and ::1 to simplify unit tests on Windows"""
   1030     if not WINDOWS:
   1031         warning("Not available")
   1032         return
   1033     warning("This will completly mess up the routes. Testing purpose only !")
   1034     # Add only if some adpaters already exist
   1035     if ipv6:
   1036         if not conf.route6.routes:
   1037             return
   1038     else:
   1039         if not conf.route.routes:
   1040             return
   1041     data = {
   1042         'name': scapy.consts.LOOPBACK_NAME,
   1043         'description': "Loopback",
   1044         'win_index': -1,
   1045         'guid': _get_valid_guid(),
   1046         'invalid': False,
   1047         'mac': '00:00:00:00:00:00',
   1048     }
   1049     data['pcap_name'] = six.text_type("\\Device\\NPF_" + data['guid'])
   1050     adapter = NetworkInterface(data)
   1051     adapter.ip = "127.0.0.1"
   1052     if iflist:
   1053         iflist.append(adapter.pcap_name)
   1054         return
   1055     # Remove all LOOPBACK_NAME routes
   1056     for route in list(conf.route.routes):
   1057         iface = route[3]
   1058         if iface.name == scapy.consts.LOOPBACK_NAME:
   1059             conf.route.routes.remove(route)
   1060     # Remove LOOPBACK_NAME interface
   1061     for devname, iface in list(IFACES.items()):
   1062         if iface.name == scapy.consts.LOOPBACK_NAME:
   1063             IFACES.pop(devname)
   1064     # Inject interface
   1065     IFACES["{0XX00000-X000-0X0X-X00X-00XXXX000XXX}"] = adapter
   1066     scapy.consts.LOOPBACK_INTERFACE = adapter
   1067     if isinstance(conf.iface, NetworkInterface):
   1068         if conf.iface.name == LOOPBACK_NAME:
   1069             conf.iface = adapter
   1070     if isinstance(conf.iface6, NetworkInterface):
   1071         if conf.iface6.name == LOOPBACK_NAME:
   1072             conf.iface6 = adapter
   1073     # Build the packed network addresses
   1074     loop_net = struct.unpack("!I", socket.inet_aton("127.0.0.0"))[0]
   1075     loop_mask = struct.unpack("!I", socket.inet_aton("255.0.0.0"))[0]
   1076     # Build the fake routes
   1077     loopback_route = (loop_net, loop_mask, "0.0.0.0", adapter, "127.0.0.1", 1)
   1078     loopback_route6 = ('::1', 128, '::', adapter, ["::1"], 1)
   1079     loopback_route6_custom = ("fe80::", 128, "::", adapter, ["::1"], 1)
   1080     if routes == None:
   1081         # Injection
   1082         conf.route6.routes.append(loopback_route6)
   1083         conf.route6.routes.append(loopback_route6_custom)
   1084         conf.route.routes.append(loopback_route)
   1085         # Flush the caches
   1086         conf.route6.invalidate_cache()
   1087         conf.route.invalidate_cache()
   1088     else:
   1089         if ipv6:
   1090             routes.append(loopback_route6)
   1091             routes.append(loopback_route6_custom)
   1092         else:
   1093             routes.append(loopback_route)
   1094 
   1095 
   1096 if not conf.use_winpcapy:
   1097 
   1098     class NotAvailableSocket(SuperSocket):
   1099         desc = "wpcap.dll missing"
   1100         def __init__(self, *args, **kargs):
   1101             raise RuntimeError("Sniffing and sending packets is not available: "
   1102                                "winpcap is not installed")
   1103 
   1104     conf.L2socket = NotAvailableSocket
   1105     conf.L2listen = NotAvailableSocket
   1106     conf.L3socket = NotAvailableSocket
   1107