Add channel hopping feature and track channels for detected APs
This commit is contained in:
parent
2e3484ed83
commit
6ae40ca9c5
1 changed files with 30 additions and 5 deletions
35
listener.py
35
listener.py
|
@ -6,6 +6,7 @@ import signal
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
@ -27,6 +28,10 @@ current_channel = None
|
||||||
include_probes = False
|
include_probes = False
|
||||||
deadpoint_candidates = set()
|
deadpoint_candidates = set()
|
||||||
unlinked_candidates = set()
|
unlinked_candidates = set()
|
||||||
|
bssid_channels = {}
|
||||||
|
|
||||||
|
CHANNEL_LIST = [1, 6, 11, 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 149, 153, 157, 161, 165] # Channels to hop
|
||||||
|
CHANNEL_HOP_INTERVAL = 5 # Seconds per channel
|
||||||
|
|
||||||
# === Signal handling ===
|
# === Signal handling ===
|
||||||
def stop_sniff(signum, frame):
|
def stop_sniff(signum, frame):
|
||||||
|
@ -92,6 +97,10 @@ def handle_packet(pkt):
|
||||||
ssid = parse_ssid(pkt)
|
ssid = parse_ssid(pkt)
|
||||||
if ssid:
|
if ssid:
|
||||||
ssid_map[a2] = ssid
|
ssid_map[a2] = ssid
|
||||||
|
# Track channel seen on
|
||||||
|
if pkt.haslayer(RadioTap) and hasattr(pkt[RadioTap], 'ChannelFrequency'):
|
||||||
|
# Nah, be lazy
|
||||||
|
bssid_channels[a2] = current_channel
|
||||||
|
|
||||||
# === Track all seen clients ===
|
# === Track all seen clients ===
|
||||||
if dot11.type == 2:
|
if dot11.type == 2:
|
||||||
|
@ -193,18 +202,30 @@ def print_suspect_aps():
|
||||||
|
|
||||||
if suspects:
|
if suspects:
|
||||||
for bssid, ssid, flags in suspects:
|
for bssid, ssid, flags in suspects:
|
||||||
print(f" - {bssid} (SSID: {ssid}) <-- {' + '.join(flags)}")
|
ch = bssid_channels.get(bssid, "?")
|
||||||
|
print(f" - {bssid} (SSID: {ssid}, Channel: {ch}) <-- {' + '.join(flags)}")
|
||||||
else:
|
else:
|
||||||
print(" None found (yet).")
|
print(" None found (yet).")
|
||||||
|
|
||||||
|
def channel_hopper(interface):
|
||||||
|
global running
|
||||||
|
i = 0
|
||||||
|
while running:
|
||||||
|
channel = CHANNEL_LIST[i % len(CHANNEL_LIST)]
|
||||||
|
set_monitor_channel(interface, channel)
|
||||||
|
i += 1
|
||||||
|
time.sleep(CHANNEL_HOP_INTERVAL)
|
||||||
|
|
||||||
# === Main ===
|
# === Main ===
|
||||||
def main():
|
def main():
|
||||||
parser = ArgumentParser()
|
parser = ArgumentParser()
|
||||||
parser.add_argument("--main-iface", required=True, help="Active interface (used to determine channel)")
|
parser.add_argument("--main-iface", required=True, help="Active interface (used to determine channel)")
|
||||||
parser.add_argument("--monitor-iface", required=True, help="Monitor interface to sniff on")
|
parser.add_argument("--monitor-iface", required=True, help="Monitor interface to sniff on")
|
||||||
parser.add_argument("--outfile", required=True, help="CSV file to append metrics row")
|
parser.add_argument("--outfile", required=True, help="CSV file to append metrics row")
|
||||||
parser.add_argument("--channel", type=int, help="Channel to lock monitor interface to (overrides main iface)")
|
|
||||||
parser.add_argument("--include-probes", action="store_true", help="Include probe responses as valid APs")
|
parser.add_argument("--include-probes", action="store_true", help="Include probe responses as valid APs")
|
||||||
|
group = parser.add_mutually_exclusive_group()
|
||||||
|
group.add_argument("--channel", type=int, help="Channel to lock monitor interface to")
|
||||||
|
group.add_argument("--channel-hop", action="store_true", help="Enable channel hopping")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
@ -237,8 +258,11 @@ def main():
|
||||||
target_ap_bssid = None # Can't determine AP if we're not associated
|
target_ap_bssid = None # Can't determine AP if we're not associated
|
||||||
|
|
||||||
print("[+] Sniffing... (waiting for SIGINT to stop)")
|
print("[+] Sniffing... (waiting for SIGINT to stop)")
|
||||||
|
if args.channel_hop:
|
||||||
|
hopper_thread = threading.Thread(target=channel_hopper, args=(args.monitor_iface,))
|
||||||
|
hopper_thread.daemon = True
|
||||||
|
hopper_thread.start()
|
||||||
|
|
||||||
while running:
|
while running:
|
||||||
sniff(iface=args.monitor_iface, prn=handle_packet, store=False, timeout=5)
|
sniff(iface=args.monitor_iface, prn=handle_packet, store=False, timeout=5)
|
||||||
|
|
||||||
|
@ -247,7 +271,8 @@ def main():
|
||||||
|
|
||||||
for bssid in sorted(aps):
|
for bssid in sorted(aps):
|
||||||
ssid = ssid_map.get(bssid, "<unknown>")
|
ssid = ssid_map.get(bssid, "<unknown>")
|
||||||
print(f" - {bssid} (SSID: {ssid})")
|
ch = bssid_channels.get(bssid, "?")
|
||||||
|
print(f" - {bssid} (SSID: {ssid}, Channel: {ch})")
|
||||||
print(f"[+] Total APsOnChannel: {len(aps)}")
|
print(f"[+] Total APsOnChannel: {len(aps)}")
|
||||||
print_suspect_aps()
|
print_suspect_aps()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue