From a454ef48361e3786c416a1fe1af059d00fb88e02 Mon Sep 17 00:00:00 2001
From: gpotter2 <gpotter2@users.noreply.github.com>
Date: Tue, 20 Dec 2016 15:15:32 +0100
Subject: [PATCH] [Windows/Networking] Fix LOOPBACK_NAME + IPv4 read_routes
 (#405)

---
 scapy/arch/consts.py           | 11 ++++--
 scapy/arch/windows/__init__.py | 64 +++++++++++++++++++++++++++-------
 scapy/route.py                 |  6 ++--
 3 files changed, 64 insertions(+), 17 deletions(-)

diff --git a/scapy/arch/consts.py b/scapy/arch/consts.py
index 581d09f0..57a33dae 100644
--- a/scapy/arch/consts.py
+++ b/scapy/arch/consts.py
@@ -5,6 +5,7 @@
 
 import os
 from sys import platform
+import platform as platform_lib
 
 LINUX = platform.startswith("linux")
 OPENBSD = platform.startswith("openbsd")
@@ -18,9 +19,15 @@ BSD = DARWIN or FREEBSD or OPENBSD or NETBSD
 if WINDOWS:
     X86_64 = False
     ARM_64 = False
+    try:
+        if float(platform_lib.release()) >= 8.1:
+            LOOPBACK_NAME = "Microsoft KM-TEST Loopback Adapter"
+        else:
+            LOOPBACK_NAME = "Microsoft Loopback Adapter"
+    except ValueError:
+        LOOPBACK_NAME = "Microsoft Loopback Adapter"
 else:
     uname = os.uname()
     X86_64 = uname[4] == 'x86_64'
     ARM_64 = uname[4] == 'aarch64'
-
-LOOPBACK_NAME = "lo" if LINUX else "lo0"
+    LOOPBACK_NAME = "lo" if LINUX else "lo0"
diff --git a/scapy/arch/windows/__init__.py b/scapy/arch/windows/__init__.py
index 1d6a4028..5ff08a02 100755
--- a/scapy/arch/windows/__init__.py
+++ b/scapy/arch/windows/__init__.py
@@ -18,11 +18,13 @@ from scapy.utils import atol, itom, inet_aton, inet_ntoa, PcapReader
 from scapy.base_classes import Gen, Net, SetGen
 import scapy.plist as plist
 from scapy.data import MTU, ETHER_BROADCAST, ETH_P_ARP
+from scapy.arch.consts import LOOPBACK_NAME
 
 conf.use_pcap = False
 conf.use_dnet = False
 conf.use_winpcapy = True
 
+WINDOWS = (os.name == 'nt')
 
 #hot-patching socket for missing variables on Windows
 import socket
@@ -37,8 +39,6 @@ if not hasattr(socket, 'IPPROTO_ESP'):
 from scapy.arch import pcapdnet
 from scapy.arch.pcapdnet import *
 
-WINDOWS = True
-
 def _exec_query_ps(cmd, fields):
     """Execute a PowerShell query"""
     ps = sp.Popen([conf.prog.powershell] + cmd +
@@ -49,12 +49,19 @@ def _exec_query_ps(cmd, fields):
     for line in ps.stdout:
         if not line.strip(): #skip empty lines
             continue
-        l.append(line.split(':', 1)[1].strip())
+        sl = line.split(':', 1)
+        if len(sl) == 1:
+            l[-1] += sl[0].strip()
+            continue
+        else:
+            l.append(sl[1].strip())
         if len(l) == len(fields):
             yield l
             l=[]
 
 def _vbs_exec_code(code):
+    if not WINDOWS:
+        return
     tmpfile = tempfile.NamedTemporaryFile(suffix=".vbs", delete=False)
     tmpfile.write(code)
     tmpfile.close()
@@ -69,6 +76,8 @@ def _vbs_exec_code(code):
     os.unlink(tmpfile.name)
 
 def _vbs_get_iface_guid(devid):
+    if not WINDOWS:
+        return
     try:
         devid = str(int(devid) + 1)
         guid = _vbs_exec_code("""WScript.Echo CreateObject("WScript.Shell").RegRead("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards\\%s\\ServiceName")
@@ -99,6 +108,8 @@ def _exec_query_vbs(cmd, fields):
     supported.
 
     """
+    if not WINDOWS:
+        return
     assert len(cmd) == 2 and cmd[0] == "Get-WmiObject"
     fields = [_VBS_WMI_FIELDS.get(cmd[1], {}).get(fld, fld) for fld in fields]
     values = _vbs_exec_code("""Set wmi = GetObject("winmgmts:")
@@ -144,6 +155,8 @@ def _where(filename, dirs=None, env="PATH"):
 
 def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
     """Find executable in current dir, system path or given ProgramFiles subdir"""
+    if not WINDOWS:
+        return
     for fn in [filename, filename+".exe"]:
         try:
             if installsubdir is None:
@@ -157,6 +170,16 @@ def win_find_exe(filename, installsubdir=None, env="ProgramFiles"):
     return path
 
 
+def is_new_release():
+    release = platform.release()
+    try:
+        if float(release) >= 8:
+            return True
+    except ValueError:
+        if (release=="post2008Server"):
+            return True
+    return False
+
 class WinProgPath(ConfClass):
     _default = "<System default>"
     # We try some magic to find the appropriate executables
@@ -190,11 +213,15 @@ def is_interface_valid(iface):
     return False
 
 def get_windows_if_list():
-    if platform.release()=="post2008Server" or platform.release()=="8":
+    if is_new_release():
         # This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
+        # Careful: this is weird, but Get-NetAdaptater works like: (Name isn't the interface name)
+        # Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
+        # ----                      --------------------                    ------- ------       ----------             ---------
+        # Ethernet                  Killer E2200 Gigabit Ethernet Contro...      13 Up           D0-50-99-56-DD-F9         1 Gbps
         query = exec_query(['Get-NetAdapter'],
-                           ['Name', 'InterfaceIndex', 'InterfaceDescription',
-                            'InterfaceGuid', 'MacAddress'])
+                           ['InterfaceDescription', 'InterfaceIndex', 'Name',
+                            'InterfaceGuid', 'MacAddress']) #Weird order, but normal
     else:
         query = exec_query(['Get-WmiObject', 'Win32_NetworkAdapter'],
                            ['Name', 'InterfaceIndex', 'InterfaceDescription',
@@ -223,15 +250,18 @@ class NetworkInterface(object):
         self.pcap_name = None
         self.description = None
         self.data = data
+        self.invalid = False
         if data is not None:
             self.update(data)
 
     def update(self, data):
         """Update info about network interface according to given dnet dictionary"""
-        self.name = data["name"]
+        self.name = data['name']
         self.description = data['description']
         self.win_index = data['win_index']
         self.guid = data['guid']
+        if 'invalid' in data:
+            self.invalid = data['invalid']
         # Other attributes are optional
         self._update_pcapdata()
 
@@ -251,6 +281,8 @@ class NetworkInterface(object):
             pass
 
     def _update_pcapdata(self):
+        if self.is_invalid():
+            return
         for i in winpcapy_get_if_list():
             if i.endswith(self.data['guid']):
                 self.pcap_name = i
@@ -258,6 +290,9 @@ class NetworkInterface(object):
 
         raise PcapNameNotFoundError
 
+    def is_invalid(self):
+        return self.invalid
+
     def __repr__(self):
         return "<%s %s %s>" % (self.__class__.__name__, self.name, self.guid)
 
@@ -273,7 +308,7 @@ class NetworkInterfaceDict(UserDict):
             except (KeyError, PcapNameNotFoundError):
                 pass
         
-        if len(self.data) == 0:
+        if len(self.data) == 0 and conf.use_winpcapy:
             log_loading.warning("No match between your pcap and windows network interfaces found. "
                                 "You probably won't be able to send packets. "
                                 "Deactivating unneeded interfaces and restarting Scapy might help."
@@ -322,6 +357,8 @@ def pcapname(dev):
 
     """
     if type(dev) is NetworkInterface:
+        if dev.is_invalid():
+            return None
         return dev.pcap_name
     try:
         return IFACES.dev_from_name(dev).pcap_name
@@ -347,7 +384,7 @@ pcapdnet.open_pcap = lambda iface,*args,**kargs: _orig_open_pcap(pcapname(iface)
 
 _orig_get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr
 pcapdnet.get_if_raw_hwaddr = lambda iface, *args, **kargs: (
-    ARPHDR_ETHER, IFACES.dev_from_pcapname(iface.pcap_name).mac.replace(':', '').decode('hex')
+    ARPHDR_ETHER, mac2str(IFACES.dev_from_pcapname(pcapname(iface)).mac.replace('-', ':'))
 )
 get_if_raw_hwaddr = pcapdnet.get_if_raw_hwaddr
 
@@ -383,16 +420,16 @@ def read_routes_7():
                            ['Name', 'Mask', 'NextHop', 'InterfaceIndex']):
         try:
             iface = dev_from_index(line[3])
+            routes.append((atol(line[0]), atol(line[1]), line[2], iface, iface.ip))
         except ValueError:
             continue
-        routes.append((atol(line[0]), atol(line[1]), line[2], iface, iface.ip))
     return routes
-
+        
 def read_routes():
     routes = []
     release = platform.release()
     try:
-        if release in ["post2008Server", "8"]:
+        if is_new_release():
             routes = read_routes_post2008()
         elif release == "XP":
             routes = read_routes_xp()
@@ -406,7 +443,6 @@ def read_routes():
     return routes
        
 def read_routes_post2008():
-    # XXX TODO: FIX THIS XXX
     routes = []
     if_index = '(\d+)'
     dest = '(\d+\.\d+\.\d+\.\d+)/(\d+)'
@@ -423,6 +459,8 @@ def read_routes_post2008():
         if match:
             try:
                 iface = dev_from_index(match.group(1))
+                if iface.ip == "0.0.0.0":
+                    continue
             except:
                 continue
             # try:
diff --git a/scapy/route.py b/scapy/route.py
index fbfff008..d0f81213 100644
--- a/scapy/route.py
+++ b/scapy/route.py
@@ -8,7 +8,7 @@ Routing and handling of network interfaces.
 """
 
 import socket
-from scapy.arch.consts import LOOPBACK_NAME, WINDOWS
+from scapy.arch.consts import LOOPBACK_NAME
 from scapy.utils import atol,ltoa,itom
 from scapy.config import conf
 from scapy.error import Scapy_Exception,warning
@@ -36,7 +36,7 @@ class Route:
             rt += "%-15s %-15s %-15s %-15s %-15s\n" % (ltoa(net),
                                               ltoa(msk),
                                               gw,
-                                              (iface.name if WINDOWS else iface),
+                                              (iface.name if not isinstance(iface, basestring) else iface),
                                               addr)
         return rt
 
@@ -137,6 +137,8 @@ class Route:
         dst = atol(dst)
         pathes=[]
         for d,m,gw,i,a in self.routes:
+            if not a: # some interfaces may not currently be connected
+                continue
             aa = atol(a)
             if aa == dst:
                 pathes.append((0xffffffffL,(LOOPBACK_NAME,a,"0.0.0.0")))
-- 
GitLab