Run scans and deauth as tracked, killable subprocesses
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHYXPc5u9FfVNS8auAUgC4
This commit is contained in:
parent
35c1c73122
commit
5100abe75c
|
|
@ -1,12 +1,48 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""CSV parsing, interface discovery, and monitor-mode + card-state handling."""
|
"""Wireless plumbing: interface discovery, monitor mode, card-state restore,
|
||||||
|
and tracked airodump-ng / aireplay-ng subprocesses."""
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
SCAN_NET_SECS = 10
|
||||||
|
SCAN_CLIENT_SECS = 15
|
||||||
|
|
||||||
|
# --- child process registry (so "Stop & Reset" can kill mid-scan/mid-deauth) ---
|
||||||
|
_procs = set()
|
||||||
|
|
||||||
|
|
||||||
|
def _spawn(cmd, **kw):
|
||||||
|
p = subprocess.Popen(cmd, **kw)
|
||||||
|
_procs.add(p)
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def _reap(p):
|
||||||
|
_procs.discard(p)
|
||||||
|
|
||||||
|
|
||||||
|
def kill_all_children():
|
||||||
|
for p in list(_procs):
|
||||||
|
try:
|
||||||
|
if p.poll() is None:
|
||||||
|
p.terminate()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
time.sleep(0.5)
|
||||||
|
for p in list(_procs):
|
||||||
|
try:
|
||||||
|
if p.poll() is None:
|
||||||
|
p.kill()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
_procs.discard(p)
|
||||||
|
|
||||||
|
|
||||||
def run(cmd, timeout=30):
|
def run(cmd, timeout=30):
|
||||||
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
return subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
|
||||||
|
|
@ -96,6 +132,7 @@ def enable_monitor(state):
|
||||||
def reset_card(state):
|
def reset_card(state):
|
||||||
if not state:
|
if not state:
|
||||||
return
|
return
|
||||||
|
kill_all_children()
|
||||||
if state.mon_iface and state.used_airmon:
|
if state.mon_iface and state.used_airmon:
|
||||||
run(["airmon-ng", "stop", state.mon_iface])
|
run(["airmon-ng", "stop", state.mon_iface])
|
||||||
names = {i["iface"]: i for i in list_wifi_interfaces()}
|
names = {i["iface"]: i for i in list_wifi_interfaces()}
|
||||||
|
|
@ -112,6 +149,34 @@ def reset_card(state):
|
||||||
run(["systemctl", "start", "NetworkManager"])
|
run(["systemctl", "start", "NetworkManager"])
|
||||||
|
|
||||||
|
|
||||||
|
def airodump_scan(mon_iface, secs, bssid=None, channel=None):
|
||||||
|
tmpdir = tempfile.mkdtemp(prefix="wifiscan_")
|
||||||
|
prefix = os.path.join(tmpdir, "dump")
|
||||||
|
cmd = ["airodump-ng", "-w", prefix, "--output-format", "csv", "--write-interval", "1"]
|
||||||
|
if bssid:
|
||||||
|
cmd += ["--bssid", bssid]
|
||||||
|
if channel:
|
||||||
|
cmd += ["-c", str(channel)]
|
||||||
|
cmd += [mon_iface]
|
||||||
|
p = _spawn(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
# fixed scan duration is a naive heuristic; longer = more results.
|
||||||
|
t0 = time.time()
|
||||||
|
while time.time() - t0 < secs and p.poll() is None:
|
||||||
|
time.sleep(0.3)
|
||||||
|
p.terminate()
|
||||||
|
try:
|
||||||
|
p.wait(timeout=5)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
p.kill()
|
||||||
|
_reap(p)
|
||||||
|
csvs = sorted(glob.glob(prefix + "*.csv"))
|
||||||
|
aps, stations = ([], [])
|
||||||
|
if csvs:
|
||||||
|
aps, stations = parse_airodump_csv(csvs[-1])
|
||||||
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||||
|
return aps, stations
|
||||||
|
|
||||||
|
|
||||||
def parse_airodump_csv(path):
|
def parse_airodump_csv(path):
|
||||||
try:
|
try:
|
||||||
with open(path, encoding="utf-8", errors="replace") as f:
|
with open(path, encoding="utf-8", errors="replace") as f:
|
||||||
|
|
@ -144,6 +209,30 @@ def parse_airodump_csv(path):
|
||||||
return aps, stations
|
return aps, stations
|
||||||
|
|
||||||
|
|
||||||
|
class Deauth:
|
||||||
|
def __init__(self):
|
||||||
|
self.proc = None
|
||||||
|
|
||||||
|
def start(self, mon_iface, bssid, client):
|
||||||
|
cmd = ["aireplay-ng", "--deauth", "0", "-a", bssid, "-c", client, mon_iface]
|
||||||
|
self.proc = _spawn(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
p = self.proc
|
||||||
|
if p and p.poll() is None:
|
||||||
|
p.terminate()
|
||||||
|
try:
|
||||||
|
p.wait(timeout=5)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
p.kill()
|
||||||
|
if p:
|
||||||
|
_reap(p)
|
||||||
|
self.proc = None
|
||||||
|
|
||||||
|
def running(self):
|
||||||
|
return self.proc is not None and self.proc.poll() is None
|
||||||
|
|
||||||
|
|
||||||
def _selftest():
|
def _selftest():
|
||||||
sample = (
|
sample = (
|
||||||
"BSSID, First time seen, Last time seen, channel, Speed, Privacy, Cipher, "
|
"BSSID, First time seen, Last time seen, channel, Speed, Privacy, Cipher, "
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue