Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Programmatic Wi‑Fi Scanning and Secure Connection Management with Python on Windows

Tech 1

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.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.