---
title: 'Wi-Fi сканер на Python: консоль и WebSocket-интерфейс'
summary: 'Примеры кода для сканирования Wi-Fi сетей с помощью Python и scapy: консольный сниффер, WebSocket-вывод в браузер и TUI-интерфейс. Практические советы по запуску и расширению.'
date: '2025-03-11'
draft: false
tags:
- wifi
- python
- scapy
- безопасность
- сниффер
- мониторинг
---
Код для Wi-Fi сниффера на Python, использующего `scapy` и интерфейс в режиме мониторинга. 
### ✅ Скрипт `scanwf.py`

```python
#!/usr/bin/env python3
import sys
import os
import signal
import subprocess
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11Elt

# Завершение по Ctrl+C
def signal_handler(sig, frame):
    print("\n=================")
    print("Завершение работы по Ctrl+C")
    print("=================\n")
    sys.exit(0)

# Инструкция
def usage():
    print("\nИспользование:")
    print("\tsudo python3 scanwf.py -i <интерфейс> [-p]\n")
    sys.exit(1)

# Обработка пакетов
def sniffpackets(packet):
    if packet.haslayer(Dot11):
        mac_src = packet.addr2
        mac_dst = packet.addr1
        bssid = packet.addr3
        ssid = ""

        if packet.haslayer(Dot11Elt):
            ssid = packet[Dot11Elt].info.decode(errors="ignore")
        
        if packet.type == 0 and packet.subtype == 8:
            print(f"[+] SSID: {ssid:30} | BSSID: {bssid} | SRC: {mac_src} | DST: {mac_dst}")

# Перевод интерфейса в режим мониторинга
def setup_monitor(iface):
    print(f"[+] Перевод {iface} в режим мониторинга...")
    subprocess.call(["ifconfig", iface, "down"])
    subprocess.call(["iwconfig", iface, "mode", "monitor"])
    subprocess.call(["ifconfig", iface, "up"])
    return iface

# Проверка root
def check_root():
    if os.geteuid() != 0:
        print("[!] Запуск возможен только от root")
        sys.exit(1)

# Показ интерфейсов
def show_interfaces():
    print("[*] Доступные интерфейсы:\n")
    subprocess.call(["ifconfig"])

# Парсинг аргументов
def parse_args():
    if "-p" in sys.argv:
        show_interfaces()
        sys.exit(0)
    if "-i" not in sys.argv:
        usage()
    idx = sys.argv.index("-i")
    try:
        iface = sys.argv[idx + 1]
    except IndexError:
        usage()
    return iface

# Главная
if __name__ == "__main__":
    signal.signal(signal.SIGINT, signal_handler)
    check_root()
    iface = parse_args()
    setup_monitor(iface)

    print(f"[+] Начинаем прослушку интерфейса {iface}...\nНажмите Ctrl+C для выхода.\n")
    sniff(iface=iface, prn=sniffpackets, store=0)
```

---
### 🔧 Зависимости

Убедись, что установлен `scapy`:
```bash
pip install scapy
```
---

### 🔐 Пример запуска

```bash
sudo python3 scanwf.py -i wlan0
```
Показать интерфейсы:
```bash
python3 scanwf.py -p
```
---

## ✅ Вариант 1: WebSocket-интерфейс для отображения в браузере

Позволяет в реальном времени выводить данные в браузер через WebSocket.

### 📦 Зависимости:

```bash
pip install scapy flask flask-socketio eventlet
```

### 📁 Структура проекта:

```
wifi_sniffer_ws/
├── app.py           # сервер Flask + WebSocket
├── sniffer.py       # логика сниффера
└── templates/
    └── index.html   # браузерный интерфейс
```

### 🔧 `sniffer.py` — сниффер как поток

```python
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11Elt, RadioTap
from datetime import datetime

def sniff_wifi(interface, callback):
    def handle_packet(packet):
        if packet.haslayer(Dot11) and packet.type == 0 and packet.subtype == 8:
            ssid = packet[Dot11Elt].info.decode(errors="ignore") if packet.haslayer(Dot11Elt) else "<unknown>"
            bssid = packet.addr3
            signal = packet.dBm_AntSignal if hasattr(packet, 'dBm_AntSignal') else "?"
            ch = "?"
            el = packet.getlayer(Dot11Elt)
            while el:
                if el.ID == 3:
                    ch = ord(el.info)
                    break
                el = el.payload.getlayer(Dot11Elt)
            callback({
                "time": datetime.now().strftime("%H:%M:%S"),
                "ssid": ssid,
                "bssid": bssid,
                "channel": ch,
                "signal": signal
            })
    sniff(iface=interface, prn=handle_packet, store=0)
```

### 🚀 `app.py` — Flask + WebSocket-сервер

```python
from flask import Flask, render_template
from flask_socketio import SocketIO
from threading import Thread
from sniffer import sniff_wifi

INTERFACE = "wlan0mon"  # заменить на нужный

app = Flask(__name__)
socketio = SocketIO(app)

@app.route('/')
def index():
    return render_template('index.html')

def emit_packet(packet):
    socketio.emit('packet', packet)

def start_sniffer():
    sniff_wifi(INTERFACE, emit_packet)

if __name__ == '__main__':
    thread = Thread(target=start_sniffer)
    thread.daemon = True
    thread.start()
    socketio.run(app, host='0.0.0.0', port=5000)
```

### 🌐 `templates/index.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Wi-Fi Sniffer</title>
  <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/4.4.1/socket.io.min.js"></script>
</head>
<body>
  <h2>📡 Обнаруженные сети:</h2>
  <table border="1">
    <thead>
      <tr><th>Time</th><th>SSID</th><th>BSSID</th><th>Channel</th><th>Signal</th></tr>
    </thead>
    <tbody id="wifiTable"></tbody>
  </table>

  <script>
    const socket = io();
    socket.on('packet', data => {
      const row = document.createElement('tr');
      row.innerHTML = `<td>${data.time}</td><td>${data.ssid}</td><td>${data.bssid}</td><td>${data.channel}</td><td>${data.signal}</td>`;
      document.getElementById('wifiTable').appendChild(row);
    });
  </script>
</body>
</html>
```

---

## ✅ Вариант 2: Консольный TUI (Text User Interface) на `Textual`

Подходит для терминала, удобно для SSH или серверов без GUI.

### 📦 Установка

```bash
pip install scapy textual rich
```

### 🐍 `sniffer_tui.py` — пример TUI-интерфейса

```python
from scapy.all import sniff, Dot11, Dot11Elt
from textual.app import App, ComposeResult
from textual.widgets import Header, Footer, DataTable
from datetime import datetime

class WifiTUI(App):
    def compose(self) -> ComposeResult:
        yield Header()
        self.table = DataTable()
        self.table.add_columns("Time", "SSID", "BSSID", "Channel", "Signal")
        yield self.table
        yield Footer()

    def on_mount(self) -> None:
        self.seen = set()
        sniff(iface="wlan0mon", prn=self.handle_packet, store=0)

    def handle_packet(self, packet):
        if packet.haslayer(Dot11) and packet.subtype == 8:
            ssid = packet[Dot11Elt].info.decode(errors="ignore") if packet.haslayer(Dot11Elt) else "<unknown>"
            bssid = packet.addr3
            signal = packet.dBm_AntSignal if hasattr(packet, 'dBm_AntSignal') else "?"
            channel = "?"
            el = packet.getlayer(Dot11Elt)
            while el:
                if el.ID == 3:
                    channel = ord(el.info)
                    break
                el = el.payload.getlayer(Dot11Elt)
            key = (ssid, bssid)
            if key not in self.seen:
                self.seen.add(key)
                self.table.add_row(
                    datetime.now().strftime("%H:%M:%S"),
                    ssid,
                    bssid,
                    str(channel),
                    str(signal)
                )

if __name__ == "__main__":
    WifiTUI().run()
```

---

## 🧩 Расширения

|Возможность|Поддержка|
|---|---|
|📡 WebSocket в браузере|✅|
|🖥 TUI в терминале|✅ `Textual`|
|🔒 Graylog интеграция|🔜 (через GELF или syslog)|
|📦 Ansible-интеграция|🔜 (в виде роли)|

---
