1# SPDX-License-Identifier: GPL-2.0-only 2# This file is part of Scapy 3# See https://scapy.net/ for more information 4# Copyright (C) Gabriel Potter <gabriel[]potter[]fr> 5 6""" 7Customizations needed to support Microsoft Windows. 8""" 9 10from glob import glob 11import os 12import platform as platform_lib 13import socket 14import struct 15import subprocess as sp 16import warnings 17 18import winreg 19 20from scapy.arch.windows.structures import ( 21 _windows_title, 22 GetAdaptersAddresses, 23 GetIpForwardTable, 24 GetIpForwardTable2, 25 get_service_status, 26) 27from scapy.consts import WINDOWS, WINDOWS_XP 28from scapy.config import conf, ProgPath 29from scapy.error import ( 30 Scapy_Exception, 31 log_interactive, 32 log_loading, 33 log_runtime, 34 warning, 35) 36from scapy.interfaces import NetworkInterface, InterfaceProvider, \ 37 dev_from_index, resolve_iface, network_name 38from scapy.pton_ntop import inet_ntop 39from scapy.utils import atol, itom, str2mac 40from scapy.utils6 import construct_source_candidate_set, in6_getscope 41from scapy.compat import plain_str 42from scapy.supersocket import SuperSocket 43 44# re-export 45from scapy.arch.common import get_if_raw_addr # noqa: F401 46 47# Typing imports 48from typing import ( 49 Any, 50 Dict, 51 Iterator, 52 List, 53 Optional, 54 Tuple, 55 Type, 56 Union, 57 cast, 58 overload, 59) 60from scapy.compat import Literal 61 62conf.use_pcap = True 63 64# These import must appear after setting conf.use_* variables 65from scapy.arch import libpcap # noqa: E402 66from scapy.arch.libpcap import ( # noqa: E402 67 NPCAP_PATH, 68 PCAP_IF_UP, 69) 70 71# Detection happens after libpcap import (NPcap detection) 72NPCAP_LOOPBACK_NAME = r"\Device\NPF_Loopback" 73NPCAP_LOOPBACK_NAME_LEGACY = "Npcap Loopback Adapter" # before npcap 0.9983 74if conf.use_npcap: 75 conf.loopback_name = NPCAP_LOOPBACK_NAME 76else: 77 try: 78 if float(platform_lib.release()) >= 8.1: 79 conf.loopback_name = "Microsoft KM-TEST Loopback Adapter" 80 else: 81 conf.loopback_name = "Microsoft Loopback Adapter" 82 except ValueError: 83 conf.loopback_name = "Microsoft Loopback Adapter" 84 85# hot-patching socket for missing variables on Windows 86if not hasattr(socket, 'IPPROTO_IPIP'): 87 socket.IPPROTO_IPIP = 4 # type: ignore 88if not hasattr(socket, 'IP_RECVTTL'): 89 socket.IP_RECVTTL = 12 # type: ignore 90if not hasattr(socket, 'IPV6_HDRINCL'): 91 socket.IPV6_HDRINCL = 36 # type: ignore 92# https://github.com/python/cpython/issues/73701 93if not hasattr(socket, 'IPPROTO_IPV6'): 94 socket.IPPROTO_IPV6 = 41 95if not hasattr(socket, 'SOL_IPV6'): 96 socket.SOL_IPV6 = socket.IPPROTO_IPV6 # type: ignore 97if not hasattr(socket, 'IPPROTO_GRE'): 98 socket.IPPROTO_GRE = 47 # type: ignore 99if not hasattr(socket, 'IPPROTO_AH'): 100 socket.IPPROTO_AH = 51 101if not hasattr(socket, 'IPPROTO_ESP'): 102 socket.IPPROTO_ESP = 50 103 104_WlanHelper = NPCAP_PATH + "\\WlanHelper.exe" 105 106 107def _encapsulate_admin(cmd): 108 # type: (str) -> str 109 """Encapsulate a command with an Administrator flag""" 110 # To get admin access, we start a new powershell instance with admin 111 # rights, which will execute the command. This needs to be done from a 112 # powershell as we run it from a cmd. 113 # ! Behold ! 114 return ("powershell /command \"Start-Process cmd " 115 "-windowstyle hidden -Wait -PassThru -Verb RunAs " 116 "-ArgumentList '/c %s'\"" % cmd) 117 118 119def _get_npcap_config(param_key): 120 # type: (str) -> Optional[str] 121 """ 122 Get a Npcap parameter matching key in the registry. 123 124 List: 125 AdminOnly, DefaultFilterSettings, DltNull, Dot11Adapters, Dot11Support 126 LoopbackAdapter, LoopbackSupport, NdisImPlatformBindingOptions, VlanSupport 127 WinPcapCompatible 128 """ 129 hkey = winreg.HKEY_LOCAL_MACHINE 130 node = r"SYSTEM\CurrentControlSet\Services\npcap\Parameters" 131 try: 132 key = winreg.OpenKey(hkey, node) 133 dot11_adapters, _ = winreg.QueryValueEx(key, param_key) 134 winreg.CloseKey(key) 135 except WindowsError: 136 return None 137 return cast(str, dot11_adapters) 138 139 140def _where(filename, dirs=None, env="PATH"): 141 # type: (str, Optional[Any], str) -> str 142 """Find file in current dir, in deep_lookup cache or in system path""" 143 if dirs is None: 144 dirs = [] 145 if not isinstance(dirs, list): 146 dirs = [dirs] 147 if glob(filename): 148 return filename 149 paths = [os.curdir] + os.environ[env].split(os.path.pathsep) + dirs 150 try: 151 return next(os.path.normpath(match) 152 for path in paths 153 for match in glob(os.path.join(path, filename)) 154 if match) 155 except (StopIteration, RuntimeError): 156 raise IOError("File not found: %s" % filename) 157 158 159def win_find_exe(filename, installsubdir=None, env="ProgramFiles"): 160 # type: (str, Optional[Any], str) -> str 161 """Find executable in current dir, system path or in the 162 given ProgramFiles subdir, and retuen its absolute path. 163 """ 164 fns = [filename] if filename.endswith(".exe") else [filename + ".exe", filename] # noqa: E501 165 for fn in fns: 166 try: 167 if installsubdir is None: 168 path = _where(fn) 169 else: 170 path = _where(fn, dirs=[os.path.join(os.environ[env], installsubdir)]) # noqa: E501 171 except IOError: 172 path = None 173 else: 174 break 175 return path or "" 176 177 178class WinProgPath(ProgPath): 179 def __init__(self): 180 # type: () -> None 181 self._reload() 182 183 def _reload(self): 184 # type: () -> None 185 self.pdfreader = "" 186 self.psreader = "" 187 self.svgreader = "" 188 # We try some magic to find the appropriate executables 189 self.dot = win_find_exe("dot") 190 self.tcpdump = win_find_exe("windump") 191 self.tshark = win_find_exe("tshark") 192 self.tcpreplay = win_find_exe("tcpreplay") 193 self.display = self._default 194 self.hexedit = win_find_exe("hexer") 195 self.sox = win_find_exe("sox") 196 self.wireshark = win_find_exe("wireshark", "wireshark") 197 self.extcap_folders = [ 198 os.path.join(os.environ.get("appdata", ""), "Wireshark", "extcap"), 199 os.path.join(os.environ.get("programfiles", ""), "Wireshark", "extcap"), 200 ] 201 self.powershell = win_find_exe( 202 "powershell", 203 installsubdir="System32\\WindowsPowerShell\\v1.0", 204 env="SystemRoot" 205 ) 206 self.cmd = win_find_exe("cmd", installsubdir="System32", 207 env="SystemRoot") 208 209 210def _exec_cmd(command): 211 # type: (str) -> Tuple[bytes, int] 212 """Call a CMD command and return the output and returncode""" 213 proc = sp.Popen(command, 214 stdout=sp.PIPE, 215 shell=True) 216 res = proc.communicate()[0] 217 return res, proc.returncode 218 219 220conf.prog = WinProgPath() 221 222if conf.prog.tcpdump and conf.use_npcap: 223 def test_windump_npcap(): 224 # type: () -> bool 225 """Return whether windump version is correct or not""" 226 try: 227 p_test_windump = sp.Popen([conf.prog.tcpdump, "-help"], stdout=sp.PIPE, stderr=sp.STDOUT) # noqa: E501 228 stdout, err = p_test_windump.communicate() 229 _windows_title() 230 _output = stdout.lower() 231 return b"npcap" in _output and b"winpcap" not in _output 232 except Exception: 233 return False 234 windump_ok = test_windump_npcap() 235 if not windump_ok: 236 log_loading.warning( 237 "The installed Windump version does not work with Npcap! " 238 "Refer to 'Winpcap/Npcap conflicts' in scapy's installation doc" 239 ) 240 del windump_ok 241 242 243def get_windows_if_list(extended=False): 244 # type: (bool) -> List[Dict[str, Any]] 245 """Returns windows interfaces through GetAdaptersAddresses. 246 247 params: 248 - extended: include anycast and multicast IPv6 (default False)""" 249 # Should work on Windows XP+ 250 def _get_mac(x): 251 # type: (Dict[str, Any]) -> str 252 size = x["physical_address_length"] 253 if size != 6: 254 return "" 255 data = bytearray(x["physical_address"]) 256 return str2mac(bytes(data)[:size]) 257 258 def _resolve_ips(y): 259 # type: (List[Dict[str, Any]]) -> List[str] 260 if not isinstance(y, list): 261 return [] 262 ips = [] 263 for ip in y: 264 addr = ip['address']['address'].contents 265 if addr.si_family == socket.AF_INET6: 266 ip_key = "Ipv6" 267 si_key = "sin6_addr" 268 else: 269 ip_key = "Ipv4" 270 si_key = "sin_addr" 271 data = getattr(addr, ip_key) 272 data = getattr(data, si_key) 273 data = bytes(bytearray(data.byte)) 274 # Build IP 275 if data: 276 ips.append(inet_ntop(addr.si_family, data)) 277 return ips 278 279 def _get_ips(x): 280 # type: (Dict[str, Any]) -> List[str] 281 unicast = x['first_unicast_address'] 282 anycast = x['first_anycast_address'] 283 multicast = x['first_multicast_address'] 284 285 ips = [] 286 ips.extend(_resolve_ips(unicast)) 287 if extended: 288 ips.extend(_resolve_ips(anycast)) 289 ips.extend(_resolve_ips(multicast)) 290 return ips 291 292 return [ 293 { 294 "name": plain_str(x["friendly_name"]), 295 "index": x["interface_index"], 296 "description": plain_str(x["description"]), 297 "guid": plain_str(x["adapter_name"]), 298 "mac": _get_mac(x), 299 "type": x["interface_type"], 300 "ipv4_metric": 0 if WINDOWS_XP else x["ipv4_metric"], 301 "ipv6_metric": 0 if WINDOWS_XP else x["ipv6_metric"], 302 "ips": _get_ips(x), 303 "nameservers": _resolve_ips(x["first_dns_server_address"]) 304 } for x in GetAdaptersAddresses() 305 ] 306 307 308def _pcapname_to_guid(pcap_name): 309 # type: (str) -> str 310 """Converts a Winpcap/Npcap pcpaname to its guid counterpart. 311 e.g. \\DEVICE\\NPF_{...} => {...} 312 """ 313 if "{" in pcap_name: 314 return "{" + pcap_name.split("{")[1] 315 return pcap_name 316 317 318class NetworkInterface_Win(NetworkInterface): 319 """A network interface of your local host""" 320 321 def __init__(self, provider, data=None): 322 # type: (WindowsInterfacesProvider, Optional[Dict[str, Any]]) -> None 323 self.cache_mode = None # type: Optional[bool] 324 self.ipv4_metric = None # type: Optional[int] 325 self.ipv6_metric = None # type: Optional[int] 326 self.nameservers = [] # type: List[str] 327 self.guid = None # type: Optional[str] 328 self.raw80211 = None # type: Optional[bool] 329 super(NetworkInterface_Win, self).__init__(provider, data) 330 331 def update(self, data): 332 # type: (Dict[str, Any]) -> None 333 """Update info about a network interface according 334 to a given dictionary. Such data is provided by get_windows_if_list 335 """ 336 # Populated early because used below 337 self.network_name = data['network_name'] 338 # Windows specific 339 self.guid = data['guid'] 340 self.ipv4_metric = data['ipv4_metric'] 341 self.ipv6_metric = data['ipv6_metric'] 342 self.nameservers = data['nameservers'] 343 344 try: 345 # Npcap loopback interface 346 if conf.use_npcap and self.network_name == conf.loopback_name: 347 # https://nmap.org/npcap/guide/npcap-devguide.html 348 data["mac"] = "00:00:00:00:00:00" 349 data["ip"] = "127.0.0.1" 350 data["ip6"] = "::1" 351 data["ips"] = ["127.0.0.1", "::1"] 352 except KeyError: 353 pass 354 super(NetworkInterface_Win, self).update(data) 355 356 def _check_npcap_requirement(self): 357 # type: () -> None 358 if not conf.use_npcap: 359 raise OSError("This operation requires Npcap.") 360 if self.raw80211 is None: 361 val = _get_npcap_config("Dot11Support") 362 self.raw80211 = bool(int(val)) if val else False 363 if not self.raw80211: 364 raise Scapy_Exception("Npcap 802.11 support is NOT enabled !") 365 366 def _npcap_set(self, key, val): 367 # type: (str, str) -> bool 368 """Internal function. Set a [key] parameter to [value]""" 369 if self.guid is None: 370 raise OSError("Interface not setup") 371 res, code = _exec_cmd(_encapsulate_admin( 372 " ".join([_WlanHelper, self.guid[1:-1], key, val]) 373 )) 374 _windows_title() # Reset title of the window 375 if code != 0: 376 raise OSError(res.decode("utf8", errors="ignore")) 377 return True 378 379 def _npcap_get(self, key): 380 # type: (str) -> str 381 if self.guid is None: 382 raise OSError("Interface not setup") 383 res, code = _exec_cmd(" ".join([_WlanHelper, self.guid[1:-1], key])) 384 _windows_title() # Reset title of the window 385 if code != 0: 386 raise OSError(res.decode("utf8", errors="ignore")) 387 return plain_str(res.strip()) 388 389 def mode(self): 390 # type: () -> str 391 """Get the interface operation mode. 392 Only available with Npcap.""" 393 self._check_npcap_requirement() 394 return self._npcap_get("mode") 395 396 def ismonitor(self): 397 # type: () -> bool 398 """Returns True if the interface is in monitor mode. 399 Only available with Npcap.""" 400 if self.cache_mode is not None: 401 return self.cache_mode 402 try: 403 res = (self.mode() == "monitor") 404 self.cache_mode = res 405 return res 406 except Scapy_Exception: 407 return False 408 409 def setmonitor(self, enable=True): 410 # type: (bool) -> bool 411 """Alias for setmode('monitor') or setmode('managed') 412 Only available with Npcap""" 413 # We must reset the monitor cache 414 if enable: 415 res = self.setmode('monitor') 416 else: 417 res = self.setmode('managed') 418 if not res: 419 log_runtime.error("Npcap WlanHelper returned with an error code !") 420 self.cache_mode = None 421 tmp = self.cache_mode = self.ismonitor() 422 return tmp if enable else (not tmp) 423 424 def availablemodes(self): 425 # type: () -> List[str] 426 """Get all available interface modes. 427 Only available with Npcap.""" 428 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 429 self._check_npcap_requirement() 430 return self._npcap_get("modes").split(",") 431 432 def setmode(self, mode): 433 # type: (Union[str, int]) -> bool 434 """Set the interface mode. It can be: 435 - 0 or managed: Managed Mode (aka "Extensible Station Mode") 436 - 1 or monitor: Monitor Mode (aka "Network Monitor Mode") 437 - 2 or master: Master Mode (aka "Extensible Access Point") 438 (supported from Windows 7 and later) 439 - 3 or wfd_device: The Wi-Fi Direct Device operation mode 440 (supported from Windows 8 and later) 441 - 4 or wfd_owner: The Wi-Fi Direct Group Owner operation mode 442 (supported from Windows 8 and later) 443 - 5 or wfd_client: The Wi-Fi Direct Client operation mode 444 (supported from Windows 8 and later) 445 Only available with Npcap.""" 446 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 447 self._check_npcap_requirement() 448 _modes = { 449 0: "managed", 450 1: "monitor", 451 2: "master", 452 3: "wfd_device", 453 4: "wfd_owner", 454 5: "wfd_client" 455 } 456 m = _modes.get(mode, "unknown") if isinstance(mode, int) else mode 457 return self._npcap_set("mode", m) 458 459 def channel(self): 460 # type: () -> int 461 """Get the channel of the interface. 462 Only available with Npcap.""" 463 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 464 self._check_npcap_requirement() 465 return int(self._npcap_get("channel")) 466 467 def setchannel(self, channel): 468 # type: (int) -> bool 469 """Set the channel of the interface (1-14): 470 Only available with Npcap.""" 471 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 472 self._check_npcap_requirement() 473 return self._npcap_set("channel", str(channel)) 474 475 def frequency(self): 476 # type: () -> int 477 """Get the frequency of the interface. 478 Only available with Npcap.""" 479 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 480 self._check_npcap_requirement() 481 return int(self._npcap_get("freq")) 482 483 def setfrequency(self, freq): 484 # type: (int) -> bool 485 """Set the channel of the interface (1-14): 486 Only available with Npcap.""" 487 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 488 self._check_npcap_requirement() 489 return self._npcap_set("freq", str(freq)) 490 491 def availablemodulations(self): 492 # type: () -> List[str] 493 """Get all available 802.11 interface modulations. 494 Only available with Npcap.""" 495 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 496 self._check_npcap_requirement() 497 return self._npcap_get("modus").split(",") 498 499 def modulation(self): 500 # type: () -> str 501 """Get the 802.11 modulation of the interface. 502 Only available with Npcap.""" 503 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 504 self._check_npcap_requirement() 505 return self._npcap_get("modu") 506 507 def setmodulation(self, modu): 508 # type: (int) -> bool 509 """Set the interface modulation. It can be: 510 - 0: dsss 511 - 1: fhss 512 - 2: irbaseband 513 - 3: ofdm 514 - 4: hrdss 515 - 5: erp 516 - 6: ht 517 - 7: vht 518 - 8: ihv 519 - 9: mimo-ofdm 520 - 10: mimo-ofdm 521 - the value directly 522 Only available with Npcap.""" 523 # According to https://nmap.org/npcap/guide/npcap-devguide.html#npcap-feature-dot11 # noqa: E501 524 self._check_npcap_requirement() 525 _modus = { 526 0: "dsss", 527 1: "fhss", 528 2: "irbaseband", 529 3: "ofdm", 530 4: "hrdss", 531 5: "erp", 532 6: "ht", 533 7: "vht", 534 8: "ihv", 535 9: "mimo-ofdm", 536 10: "mimo-ofdm", 537 } 538 m = _modus.get(modu, "unknown") if isinstance(modu, int) else modu 539 return self._npcap_set("modu", str(m)) 540 541 542class WindowsInterfacesProvider(InterfaceProvider): 543 name = "libpcap" 544 libpcap = True 545 546 def _is_valid(self, dev): 547 # type: (NetworkInterface) -> bool 548 # Winpcap (and old Npcap) have no support for PCAP_IF_UP :( 549 if dev.flags == 0: 550 return True 551 return bool(dev.flags & PCAP_IF_UP) 552 553 @classmethod 554 def _pcap_check(cls): 555 # type: () -> None 556 """Performs checks/restart pcap adapter""" 557 if not conf.use_pcap: 558 # Winpcap/Npcap isn't installed 559 return 560 561 _detect = pcap_service_status() 562 563 def _ask_user(): 564 # type: () -> bool 565 if not conf.interactive: 566 return False 567 msg = "Do you want to start it ? (yes/no) [y]: " 568 try: 569 # Better IPython compatibility 570 import IPython 571 return cast(bool, IPython.utils.io.ask_yes_no(msg, default='y')) 572 except (NameError, ImportError): 573 while True: 574 _confir = input(msg) 575 _confir = _confir.lower().strip() 576 if _confir in ["yes", "y", ""]: 577 return True 578 elif _confir in ["no", "n"]: 579 return False 580 if _detect: 581 # No action needed 582 return 583 else: 584 log_interactive.warning( 585 "Scapy has detected that your pcap service is not running !" 586 ) 587 if not conf.interactive or _ask_user(): 588 succeed = pcap_service_start(askadmin=conf.interactive) 589 if succeed: 590 log_loading.info("Pcap service started !") 591 return 592 log_loading.warning( 593 "Could not start the pcap service! " 594 "You probably won't be able to send packets. " 595 "Check your winpcap/npcap installation " 596 "and access rights." 597 ) 598 599 def load(self, NetworkInterface_Win=NetworkInterface_Win): 600 # type: (type) -> Dict[str, NetworkInterface] 601 results = {} 602 if not conf.cache_pcapiflist: 603 # Try a restart 604 WindowsInterfacesProvider._pcap_check() 605 606 legacy_npcap_guid = None 607 windows_interfaces = dict() 608 for i in get_windows_if_list(): 609 # Only consider interfaces with a GUID 610 if i['guid']: 611 if conf.use_npcap: 612 # Detect the legacy Loopback interface 613 if i['name'] == NPCAP_LOOPBACK_NAME_LEGACY: 614 # Legacy Npcap (<0.9983) 615 legacy_npcap_guid = i['guid'] 616 elif "Loopback" in i['name']: 617 # Newer Npcap 618 i['guid'] = conf.loopback_name 619 # Map interface 620 windows_interfaces[i['guid']] = i 621 622 def iterinterfaces() -> Iterator[ 623 Tuple[str, Optional[str], List[str], int, str, Optional[Dict[str, Any]]] 624 ]: 625 if conf.use_pcap: 626 # We have a libpcap provider: enrich pcap interfaces with 627 # Windows data 628 for netw, if_data in conf.cache_pcapiflist.items(): 629 name, ips, flags, _, _ = if_data 630 guid = _pcapname_to_guid(netw) 631 if guid == legacy_npcap_guid: 632 # Legacy Npcap detected ! 633 conf.loopback_name = netw 634 data = windows_interfaces.get(guid, None) 635 yield netw, name, ips, flags, guid, data 636 else: 637 # We don't have a libpcap provider: only use Windows data 638 for guid, data in windows_interfaces.items(): 639 netw = r'\Device\NPF_' + guid if guid[0] != '\\' else guid 640 yield netw, None, [], 0, guid, data 641 642 index = 0 643 for netw, name, ips, flags, guid, data in iterinterfaces(): 644 if data: 645 # Exists in Windows registry 646 data['network_name'] = netw 647 data['ips'] = list(set(data['ips'] + ips)) 648 data['flags'] = flags 649 else: 650 # Only in [Wi]npcap 651 index -= 1 652 data = { 653 'name': name, 654 'description': name, 655 'index': index, 656 'guid': guid, 657 'network_name': netw, 658 'mac': '00:00:00:00:00:00', 659 'ipv4_metric': 0, 660 'ipv6_metric': 0, 661 'ips': ips, 662 'flags': flags, 663 'nameservers': [], 664 } 665 # No KeyError will happen here, as we get it from cache 666 results[netw] = NetworkInterface_Win(self, data) 667 return results 668 669 def reload(self): 670 # type: () -> Dict[str, NetworkInterface] 671 """Reload interface list""" 672 self.restarted_adapter = False 673 if conf.use_pcap: 674 # Reload from Winpcapy 675 from scapy.arch.libpcap import load_winpcapy 676 load_winpcapy() 677 return self.load() 678 679 def _l3socket(self, dev, ipv6): 680 # type: (NetworkInterface, bool) -> Type[SuperSocket] 681 """Return L3 socket used by interfaces of this provider""" 682 if ipv6: 683 return conf.L3socket6 684 else: 685 return conf.L3socket 686 687 688# Register provider 689conf.ifaces.register_provider(WindowsInterfacesProvider) 690 691 692def get_ips(v6=False): 693 # type: (bool) -> Dict[NetworkInterface, List[str]] 694 """Returns all available IPs matching to interfaces, using the windows system. 695 Should only be used as a WinPcapy fallback. 696 697 :param v6: IPv6 addresses 698 """ 699 res = {} 700 for iface in conf.ifaces.values(): 701 if v6: 702 res[iface] = iface.ips[6] 703 else: 704 res[iface] = iface.ips[4] 705 return res 706 707 708def get_ip_from_name(ifname, v6=False): 709 # type: (str, bool) -> str 710 """Backward compatibility: indirectly calls get_ips 711 Deprecated. 712 """ 713 warnings.warn( 714 "get_ip_from_name is deprecated. Use the `ip` attribute of the iface " 715 "or use get_ips() to get all ips per interface.", 716 DeprecationWarning 717 ) 718 iface = conf.ifaces.dev_from_name(ifname) 719 return get_ips(v6=v6).get(iface, [""])[0] 720 721 722def pcap_service_name(): 723 # type: () -> str 724 """Return the pcap adapter service's name""" 725 return "npcap" if conf.use_npcap else "npf" 726 727 728def pcap_service_status(): 729 # type: () -> bool 730 """Returns whether the windows pcap adapter is running or not""" 731 status = get_service_status(pcap_service_name()) 732 return status["dwCurrentState"] == 4 733 734 735def _pcap_service_control(action, askadmin=True): 736 # type: (str, bool) -> bool 737 """Internal util to run pcap control command""" 738 command = action + ' ' + pcap_service_name() 739 res, code = _exec_cmd(_encapsulate_admin(command) if askadmin else command) 740 if code != 0: 741 warning(res.decode("utf8", errors="ignore")) 742 return (code == 0) 743 744 745def pcap_service_start(askadmin=True): 746 # type: (bool) -> bool 747 """Starts the pcap adapter. Will ask for admin. Returns True if success""" 748 return _pcap_service_control('sc start', askadmin=askadmin) 749 750 751def pcap_service_stop(askadmin=True): 752 # type: (bool) -> bool 753 """Stops the pcap adapter. Will ask for admin. Returns True if success""" 754 return _pcap_service_control('sc stop', askadmin=askadmin) 755 756 757if conf.use_pcap: 758 _orig_open_pcap = libpcap.open_pcap 759 760 def open_pcap(device, # type: Union[str, NetworkInterface] 761 *args, # type: Any 762 **kargs # type: Any 763 ): 764 # type: (...) -> libpcap._PcapWrapper_libpcap 765 """open_pcap: Windows routine for creating a pcap from an interface. 766 This function is also responsible for detecting monitor mode. 767 """ 768 iface = cast(NetworkInterface_Win, resolve_iface(device)) 769 iface_network_name = iface.network_name 770 if not iface: 771 raise Scapy_Exception( 772 "Interface is invalid (no pcap match found)!" 773 ) 774 # Only check monitor mode when manually specified. 775 # Checking/setting for monitor mode will slow down the process, and the 776 # common is case is not to use monitor mode 777 kw_monitor = kargs.get("monitor", None) 778 if conf.use_npcap and kw_monitor is not None: 779 monitored = iface.ismonitor() 780 if kw_monitor is not monitored: 781 # The monitor param is specified, and not matching the current 782 # interface state 783 iface.setmonitor(kw_monitor) 784 return _orig_open_pcap(iface_network_name, *args, **kargs) 785 libpcap.open_pcap = open_pcap # type: ignore 786 787 788def _read_routes_c_v1(): 789 # type: () -> List[Tuple[int, int, str, str, str, int]] 790 """Retrieve Windows routes through a GetIpForwardTable call. 791 792 This is compatible with XP but won't get IPv6 routes.""" 793 def _extract_ip(obj): 794 # type: (int) -> str 795 return inet_ntop(socket.AF_INET, struct.pack("<I", obj)) 796 797 def _proc(ip): 798 # type: (int) -> int 799 if WINDOWS_XP: 800 return struct.unpack("<I", struct.pack(">I", ip))[0] 801 return ip 802 routes = [] 803 for route in GetIpForwardTable(): 804 ifIndex = route['ForwardIfIndex'] 805 dest = _proc(route['ForwardDest']) 806 netmask = _proc(route['ForwardMask']) 807 nexthop = _extract_ip(route['ForwardNextHop']) 808 metric = route['ForwardMetric1'] 809 # Build route 810 try: 811 iface = cast(NetworkInterface_Win, dev_from_index(ifIndex)) 812 if not iface.ip or iface.ip == "0.0.0.0": 813 continue 814 except ValueError: 815 continue 816 ip = iface.ip 817 netw = network_name(iface) 818 # RouteMetric + InterfaceMetric 819 metric = metric + iface.ipv4_metric 820 routes.append((dest, netmask, nexthop, netw, ip, metric)) 821 return routes 822 823 824@overload 825def _read_routes_c(ipv6): # noqa: F811 826 # type: (Literal[True]) -> List[Tuple[str, int, str, str, List[str], int]] 827 pass 828 829 830@overload 831def _read_routes_c(ipv6=False): # noqa: F811 832 # type: (Literal[False]) -> List[Tuple[int, int, str, str, str, int]] 833 pass 834 835 836def _read_routes_c(ipv6=False): # noqa: F811 837 # type: (bool) -> Union[List[Tuple[int, int, str, str, str, int]], List[Tuple[str, int, str, str, List[str], int]]] # noqa: E501 838 """Retrieve Windows routes through a GetIpForwardTable2 call. 839 840 This is not available on Windows XP !""" 841 af = socket.AF_INET6 if ipv6 else socket.AF_INET 842 sock_addr_name = 'Ipv6' if ipv6 else 'Ipv4' 843 sin_addr_name = 'sin6_addr' if ipv6 else 'sin_addr' 844 metric_name = 'ipv6_metric' if ipv6 else 'ipv4_metric' 845 if ipv6: 846 lifaddr = in6_getifaddr() 847 routes = [] # type: List[Any] 848 849 def _extract_ip(obj): 850 # type: (Dict[str, Any]) -> str 851 ip = obj[sock_addr_name][sin_addr_name] 852 ip = bytes(bytearray(ip['byte'])) 853 # Build IP 854 return inet_ntop(af, ip) 855 856 for route in GetIpForwardTable2(af): 857 # Extract data 858 ifIndex = route['InterfaceIndex'] 859 dest = _extract_ip(route['DestinationPrefix']['Prefix']) 860 netmask = route['DestinationPrefix']['PrefixLength'] 861 nexthop = _extract_ip(route['NextHop']) 862 metric = route['Metric'] 863 # Build route 864 try: 865 iface = dev_from_index(ifIndex) 866 if not iface.ip or iface.ip == "0.0.0.0": 867 continue 868 except ValueError: 869 continue 870 ip = iface.ip 871 netw = network_name(iface) 872 # RouteMetric + InterfaceMetric 873 metric = metric + getattr(iface, metric_name) 874 if ipv6: 875 _append_route6(routes, dest, netmask, nexthop, 876 netw, lifaddr, metric) 877 else: 878 routes.append((atol(dest), itom(int(netmask)), 879 nexthop, netw, ip, metric)) 880 return routes 881 882 883def read_routes(): 884 # type: () -> List[Tuple[int, int, str, str, str, int]] 885 routes = [] 886 try: 887 if WINDOWS_XP: 888 routes = _read_routes_c_v1() 889 else: 890 routes = _read_routes_c(ipv6=False) 891 except Exception as e: 892 log_loading.warning("Error building scapy IPv4 routing table : %s", e) 893 return routes 894 895 896############ 897# IPv6 # 898############ 899 900 901def in6_getifaddr(): 902 # type: () -> List[Tuple[str, int, str]] 903 """ 904 Returns all IPv6 addresses found on the computer 905 """ 906 ifaddrs = [] # type: List[Tuple[str, int, str]] 907 ip6s = get_ips(v6=True) 908 for iface, ips in ip6s.items(): 909 for ip in ips: 910 scope = in6_getscope(ip) 911 ifaddrs.append((ip, scope, iface.network_name)) 912 # Appends Npcap loopback if available 913 if conf.use_npcap and conf.loopback_name: 914 ifaddrs.append(("::1", 0, conf.loopback_name)) 915 return ifaddrs 916 917 918def _append_route6(routes, # type: List[Tuple[str, int, str, str, List[str], int]] 919 dpref, # type: str 920 dp, # type: int 921 nh, # type: str 922 iface, # type: str 923 lifaddr, # type: List[Tuple[str, int, str]] 924 metric, # type: int 925 ): 926 # type: (...) -> None 927 cset = [] # candidate set (possible source addresses) 928 if iface == conf.loopback_name: 929 if dpref == '::': 930 return 931 cset = ['::1'] 932 else: 933 devaddrs = (x for x in lifaddr if x[2] == iface) 934 cset = construct_source_candidate_set(dpref, dp, devaddrs) 935 if not cset: 936 return 937 # APPEND (DESTINATION, NETMASK, NEXT HOP, IFACE, CANDIDATES, METRIC) 938 routes.append((dpref, dp, nh, iface, cset, metric)) 939 940 941def read_routes6(): 942 # type: () -> List[Tuple[str, int, str, str, List[str], int]] 943 routes6 = [] 944 if WINDOWS_XP: 945 return routes6 946 try: 947 routes6 = _read_routes_c(ipv6=True) 948 except Exception as e: 949 log_loading.warning("Error building scapy IPv6 routing table : %s", e) 950 return routes6 951 952 953def _route_add_loopback(routes=None, # type: Optional[List[Any]] 954 ipv6=False, # type: bool 955 iflist=None, # type: Optional[List[str]] 956 ): 957 # type: (...) -> None 958 """Add a route to 127.0.0.1 and ::1 to simplify unit tests on Windows""" 959 if not WINDOWS: 960 warning("Calling _route_add_loopback is only valid on Windows") 961 return 962 warning("This will completely mess up the routes. Testing purpose only !") 963 # Add only if some adapters already exist 964 if ipv6: 965 if not conf.route6.routes: 966 return 967 else: 968 if not conf.route.routes: 969 return 970 conf.ifaces._add_fake_iface(conf.loopback_name) 971 adapter = conf.ifaces.dev_from_name(conf.loopback_name) 972 if iflist: 973 iflist.append(adapter.network_name) 974 return 975 # Remove all conf.loopback_name routes 976 for route in list(conf.route.routes): 977 iface = route[3] 978 if iface == conf.loopback_name: 979 conf.route.routes.remove(route) 980 # Remove conf.loopback_name interface 981 for devname, ifname in list(conf.ifaces.items()): 982 if ifname == conf.loopback_name: 983 conf.ifaces.pop(devname) 984 # Inject interface 985 conf.ifaces[r"\Device\NPF_{0XX00000-X000-0X0X-X00X-00XXXX000XXX}"] = adapter 986 conf.loopback_name = adapter.network_name 987 if isinstance(conf.iface, NetworkInterface): 988 if conf.iface.network_name == conf.loopback_name: 989 conf.iface = adapter 990 conf.netcache.arp_cache["127.0.0.1"] = "ff:ff:ff:ff:ff:ff" # type: ignore 991 conf.netcache.in6_neighbor["::1"] = "ff:ff:ff:ff:ff:ff" # type: ignore 992 # Build the packed network addresses 993 loop_net = struct.unpack("!I", socket.inet_aton("127.0.0.0"))[0] 994 loop_mask = struct.unpack("!I", socket.inet_aton("255.0.0.0"))[0] 995 # Build the fake routes 996 loopback_route = ( 997 loop_net, 998 loop_mask, 999 "0.0.0.0", 1000 adapter.network_name, 1001 "127.0.0.1", 1002 1 1003 ) 1004 loopback_route6 = ('::1', 128, '::', adapter.network_name, ["::1"], 1) 1005 loopback_route6_custom = ("fe80::", 128, "::", adapter.network_name, ["::1"], 1) 1006 if routes is None: 1007 # Injection 1008 conf.route6.routes.append(loopback_route6) 1009 conf.route6.routes.append(loopback_route6_custom) 1010 conf.route.routes.append(loopback_route) 1011 # Flush the caches 1012 conf.route6.invalidate_cache() 1013 conf.route.invalidate_cache() 1014 else: 1015 if ipv6: 1016 routes.append(loopback_route6) 1017 routes.append(loopback_route6_custom) 1018 else: 1019 routes.append(loopback_route) 1020 1021 1022class _NotAvailableSocket(SuperSocket): 1023 desc = "wpcap.dll missing" 1024 1025 def __init__(self, *args, **kargs): 1026 # type: (*Any, **Any) -> None 1027 raise RuntimeError( 1028 "Sniffing and sending packets is not available at layer 2: " 1029 "winpcap is not installed. You may use conf.L3socket or " 1030 "conf.L3socket6 to access layer 3" 1031 ) 1032 1033 1034####### 1035# DNS # 1036####### 1037 1038def read_nameservers() -> List[str]: 1039 """Return the nameservers configured by the OS (on the default interface) 1040 """ 1041 # Windows has support for different DNS servers on each network interface, 1042 # but to be cross-platform we only return the servers for the default one. 1043 if isinstance(conf.iface, NetworkInterface_Win): 1044 return conf.iface.nameservers 1045 else: 1046 return [] 1047