Programmatic Wi‑Fi Scanning and Secure Connection Management with Python on Windows
I can’t help with instructions or code that breaks into Wi‑Fi networks or brute‑forces passwords. The material below focuses on lawful Wi‑Fi adapter enumeration, scanning, and connecting to networks you own.
Runtime and librareis
- OS: Windows 10/11
- Python: 3.8+
- Dependencies:
pywifi(pip install pywifi)
Adapter selection
The first step is to locate an available WLAN interface. The function below either picks the only adapter present or selects by index.
import sys
from typing import Optional
from pywifi import PyWiFi
def select_adapter(index: Optional[int] = None):
wifi = PyWiFi()
adapters = wifi.interfaces()
if not adapters:
raise RuntimeError("No WLAN adapters found")
if index is None:
if len(adapters) == 1:
return adapters[0]
# Default to the first adapter if multiple exist; override by passing index
return adapters[0]
if index < 0 or index >= len(adapters):
raise IndexError(f"Adapter index {index} out of range (0..{len(adapters)-1})")
return adapters[index]
def list_adapters():
wifi = PyWiFi()
adapters = wifi.interfaces()
return [(i, iface.name()) for i, iface in enumerate(adapters)]
if __name__ == "__main__":
for i, name in list_adapters():
print(f"[{i}] {name}")
Passsive scanning
Start a scan and poll for results. Some drivers take a moment to populate scan results; the code below handles that with a short polling loop.
import time
from pywifi import const
def scan_networks(iface, wait_s: float = 3.0, poll: float = 0.2):
"""Return a list of visible networks with basic details.
Only gathers broadcast information and does not attempt any authentication.
"""
iface.scan()
deadline = time.monotonic() + wait_s
previous_count = -1
# Poll for results stabilizing or until timeout
while time.monotonic() < deadline:
results = iface.scan_results()
if len(results) == previous_count:
break
previous_count = len(results)
time.sleep(poll)
nets = []
seen = set()
for cell in iface.scan_results():
# Deduplicate by BSSID
if cell.bssid in seen:
continue
seen.add(cell.bssid)
nets.append({
"ssid": cell.ssid or "",
"bssid": cell.bssid,
"signal": cell.signal,
"akm": list(cell.akm), # authentication key management types
"cipher": cell.cipher,
})
return nets
def print_networks(nets):
for n in sorted(nets, key=lambda x: x["signal"], reverse=True):
akm_names = ",".join(str(a) for a in n["akm"]) or "-"
print(f"{n['signal']:>4} dBm {n['bssid']:<18} SSID='{n['ssid']}' AKM=[{akm_names}] CIPHER={n['cipher']}")
Connecting to a known network (single credential)
Create a profile and attempt a single connection using credentials you are authorized to use. This does not iterate over passwords and is intended for managing your own networks or lab setups.
from pywifi import Profile
_STATE_NAMES = {
const.IFACE_DISCONNECTED: "DISCONNECTED",
const.IFACE_SCANNING: "SCANNING",
const.IFACE_INACTIVE: "INACTIVE",
const.IFACE_CONNECTING: "CONNECTING",
const.IFACE_CONNECTED: "CONNECTED",
}
def state_name(code: int) -> str:
return _STATE_NAMES.get(code, f"UNKNOWN({code})")
def build_profile(ssid: str, password: str, *,
akm=const.AKM_TYPE_WPA2PSK, cipher=const.CIPHER_TYPE_CCMP) -> Profile:
p = Profile()
p.ssid = ssid
p.auth = const.AUTH_ALG_OPEN
p.akm = [akm]
p.cipher = cipher
p.key = password
return p
def connect_once(iface, ssid: str, password: str, timeout_s: float = 12.0) -> bool:
"""Attempt a single connection to the specified SSID using one password.
Returns True if connected, False otherwise.
"""
iface.remove_all_network_profiles()
profile = build_profile(ssid, password)
tmp = iface.add_network_profile(profile)
iface.connect(tmp)
deadline = time.monotonic() + timeout_s
last = None
while time.monotonic() < deadline:
code = iface.status()
if code != last:
print(f"status: {state_name(code)}")
last = code
if code == const.IFACE_CONNECTED:
return True
if code == const.IFACE_DISCONNECTED:
break
time.sleep(0.2)
# Ensure we clean up
iface.disconnect()
return False
Example: scan and connect to an owned SSID
if __name__ == "__main__":
# Pick an adapter and scan
adapter = select_adapter()
print(f"Using adapter: {adapter.name()}")
visible = scan_networks(adapter)
print_networks(visible)
# Replace with your own SSID and passphrase (authorized use only)
target_ssid = "YourHomeOrLabSSID"
passphrase = "YourStrongPassphrase"
print(f"\nConnecting to '{target_ssid}'...")
ok = connect_once(adapter, target_ssid, passphrase)
print("Connected" if ok else "Connection failed")
Windows alternative: scanning via netsh
If you prefer not to rely on third‑party modules for scenning, Windows exposes WLAN information through netsh. The snippet below parses the output of netsh wlan show networks mode=Bssid.
import re
import subprocess
from collections import defaultdict
def scan_with_netsh():
cmd = ["netsh", "wlan", "show", "networks", "mode=Bssid"]
text = subprocess.check_output(cmd, encoding="utf-8", errors="ignore")
blocks = re.split(r"\r?\n\r?\n", text)
entries = []
ssid = None
for line in text.splitlines():
m_ssid = re.match(r"\s*SSID\s*\d+\s*:\s*(.*)$", line)
if m_ssid:
ssid = m_ssid.group(1).strip()
continue
m_bssid = re.match(r"\s*BSSID\s*\d+\s*:\s*(.*)$", line)
if m_bssid and ssid is not None:
bssid = m_bssid.group(1).strip()
entries.append({"ssid": ssid, "bssid": bssid})
continue
m_signal = re.match(r"\s*Signal\s*:\s*(\d+)%", line)
if m_signal and entries:
entries[-1]["signal_pct"] = int(m_signal.group(1))
m_auth = re.match(r"\s*Authentication\s*:\s*(.*)$", line)
if m_auth and entries:
entries[-1]["auth"] = m_auth.group(1).strip()
m_cipher = re.match(r"\s*Encryption\s*:\s*(.*)$", line)
if m_cipher and entries:
entries[-1]["cipher"] = m_cipher.group(1).strip()
# Coalesce per SSID for a quick summary
by_ssid = defaultdict(list)
for e in entries:
by_ssid[e["ssid"]].append(e)
for ssid, cells in by_ssid.items():
strongest = max(cells, key=lambda c: c.get("signal_pct", 0))
print(f"{ssid:<32} best={strongest.get('signal_pct', 0):>3}% APs={len(cells)}")
return entries
Notes on security and compliance
- Use these APIs only to manage networks for which you have explicit authorization.
- Avoid storing passphrases in source code; prefer enviroment variables or a secure secret store.
- Prefer WPA2/WPA3 with strong, unique passphrases and disible legacy protocols where possible.
- For red‑team or penetration testing, operate within a legal scope and use dedicated lab environments.