
这是一个使用python构建的基于WiFi重连流量分析的隐蔽摄像头检测程序。
原理
技术基础:通过监控WiFi网络中的设备重连行为模式来识别潜在的隐蔽摄像头。摄像头设备在重连时通常表现出特定的行为特征:
-
频繁的关联请求
-
特定的数据包大小分布
-
规律的时间间隔
-
摄像头厂商的MAC地址OUI
架构
1. 跨平台兼容性设计
# 智能操作系统检测
self.os_type = platform.system()
if platform.system() == "Windows":
# Windows特定实现
else:
# Linux特定实现
亮点:完善的平台适配,特别是Windows环境的深度优化
2. 多层接口检测机制
-
Scapy接口扫描
-
系统命令检测(netsh、iwconfig)
-
备用接口回退
3. 流量捕获优化策略
def _enhanced_windows_capture(self, interface, timeout, packet_handler):
# 方法1: 标准Scapy捕获
# 方法2: 原始套接字备用
# 方法3: 系统工具捕获
创新点:三重捕获保障,确保在各种网络环境下都能获取数据
创新
1. 智能重连触发机制
def trigger_reconnection(self):
if self.os_type == "Linux":
self._linux_reconnection_trigger() # MDK4攻击 + 信道切换
else:
self._windows_reconnection_trigger() # 接口重置 + 服务重启
技术价值:主动式检测,通过触发设备重连暴露行为特征
2. 多维度特征工程
def _extract_device_features(self, profile):
features = [
association_attempts, # 关联尝试次数
reconnection_count, # 重连次数
avg_interval, # 平均时间间隔
interval_std, # 时间间隔标准差
packet_size_mean, # 数据包大小均值
packet_size_std, # 数据包大小标准差
packet_count, # 数据包数量
rtsp_detected, # RTSP协议检测
video_ports_count, # 视频端口数量
camera_oui_match # 摄像头厂商OUI
]
科学依据:基于摄像头设备的网络行为学研究特征选择
3. 机器学习检测模型
self.model = RandomForestClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=5,
min_samples_leaf=2,
random_state=42
)
算法优势:随机森林适合小样本、多特征分类问题
应用
1. 安全检测场景
-
隐私保护:检测酒店、更衣室等场所的隐蔽摄像头
-
企业安全:防止商业间谍通过摄像头窃取信息
-
个人安全:保护家庭隐私安全
2. 技术突破点
-
非侵入式检测:无需物理接触可疑设备
-
实时分析:动态监控网络行为
-
隐蔽性强:不易被反检测
实现
1. Windows兼容性深度优化
def _find_correct_windows_interface(self):
# 多层接口匹配算法
# 1. Scapy接口描述匹配
# 2. netsh命令解析
# 3. 备用接口选择
解决痛点:Windows无线网卡接口命名的复杂性
2. 鲁棒的错误处理
try:
# 主捕获方法
sniff(iface=interface, prn=packet_handler, timeout=timeout, store=0)
except Exception as e:
# 自动切换到备用方法
return self._fallback_windows_capture(timeout)
工程价值:确保系统在复杂环境下的稳定性
3. 智能流量分析
def _process_packet(self, packet):
if hasattr(packet, 'type'):
if packet.type == 0: # 管理帧
self._process_dot11_packet(packet)
elif packet.type == 2: # 数据帧
self._process_data_packet(packet)
协议深度:完整的802.11协议栈解析能力
评估
准确性指标
-
特征维度:10个关键行为特征
-
模型置信度:概率输出支持风险评估
-
多层级判断:MAC OUI + 行为模式 + 协议特征
风险评估体系
risk_level = "高风险" if camera_prob > 0.8 else "中风险" if camera_prob > 0.6 else "低风险"
实用价值:分级警报机制,避免误报干扰
使用注意
法律合规性
# 重要:仅限授权测试使用
# 遵守当地网络安全法律法规
伦理要求:必须在合法授权范围内使用
技术限制
-
依赖无线网卡监控模式支持
-
需要管理员/root权限
-
可能被高级隐蔽技术规避
代码
#!/usr/bin/env python3
"""
基于设备WiFi重连流量的隐蔽智能摄像头检测系统
修复Windows流量捕获问题,增强实际检测能力
"""
import os
import sys
import time
import platform
import subprocess
import socket
import struct
from datetime import datetime
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.preprocessing import StandardScaler
import warnings
warnings.filterwarnings('ignore')
# 检查操作系统并导入相应的网络库
if platform.system() == "Windows":
try:
import ctypes
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11Elt
from scapy.layers.inet import IP, TCP, UDP
from scapy.arch.windows import get_windows_if_list
except ImportError as e:
print(f"错误: 必需的网络库不可用 - {e}")
print("请安装: pip install scapy pandas scikit-learn numpy")
sys.exit(1)
else:
from scapy.all import *
from scapy.layers.dot11 import Dot11, Dot11Elt
from scapy.layers.inet import IP, TCP, UDP
class WiFiCameraDetector:
"""基于WiFi重连流量分析的隐蔽摄像头检测系统"""
def __init__(self, interface=None, capture_duration=120):
self.os_type = platform.system()
self.interface = interface or self._detect_interface()
self.capture_duration = capture_duration
self.model = None
self.scaler = StandardScaler()
self.device_profiles = {}
self.captured_packets = []
def _detect_interface(self):
"""自动检测网络接口"""
if self.os_type == "Windows":
return self._get_windows_interface()
else:
return self._get_linux_interface()
def _get_windows_interface(self):
"""获取Windows系统网络接口"""
try:
# 方法1: 使用Scapy的接口检测
try:
iface_list = get_windows_if_list()
for iface in iface_list:
if_name = iface.get('name', '')
if_desc = iface.get('description', '')
if 'wireless' in if_desc.lower() or 'wi-fi' in if_desc.lower() or 'wlan' in if_desc.lower():
print(f"Scapy检测到无线接口: {if_name}")
return if_name
except Exception as e:
print(f"Scapy接口检测失败: {e}")
# 方法2: 使用netsh命令获取无线接口
result = subprocess.run(
['netsh', 'interface', 'show', 'interface'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'Connected' in line and ('Wireless' in line or 'Wi-Fi' in line):
parts = line.split()
if len(parts) > 3:
interface_name = parts[-1]
print(f"netsh检测到无线接口: {interface_name}")
return interface_name
# 方法3: 使用ipconfig获取接口
result = subprocess.run(
['ipconfig'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'Wireless' in line or 'Wi-Fi' in line:
lines = result.stdout.split('\n')
for i, l in enumerate(lines):
if 'adapter' in l.lower() and ('Wireless' in l or 'Wi-Fi' in l):
interface_name = l.split(':')[0].strip()
if interface_name:
return interface_name
print("警告: 无法自动检测无线接口,使用默认接口")
return "Wi-Fi"
except Exception as e:
print(f"接口检测错误: {e}")
return "Wi-Fi"
def _get_linux_interface(self):
"""获取Linux系统网络接口"""
try:
result = subprocess.run(
['iwconfig'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'IEEE 802.11' in line and 'no wireless' not in line:
interface = line.split()[0]
if interface:
return interface
except:
pass
common_interfaces = ['wlan0', 'wlan1', 'wlp2s0', 'wlp3s0']
for iface in common_interfaces:
try:
subprocess.run(
['ip', 'link', 'show', iface],
capture_output=True, check=True, timeout=5
)
return iface
except:
continue
return "wlan0"
def check_privileges(self):
"""检查管理员权限"""
if self.os_type == "Windows":
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
return False
else:
try:
return os.geteuid() == 0
except AttributeError:
try:
result = subprocess.run(
['id', '-u'],
capture_output=True, text=True, encoding='utf-8', errors='ignore'
)
return result.stdout.strip() == '0'
except:
return False
def setup_environment(self):
"""设置检测环境"""
if not self.check_privileges():
print("错误: 需要管理员/root权限运行")
return False
print(f"使用接口: {self.interface}")
if self.os_type == "Linux":
return self._setup_linux_environment()
else:
return self._setup_windows_environment()
def _setup_linux_environment(self):
"""设置Linux环境"""
try:
subprocess.run(
['sudo', 'systemctl', 'stop', 'NetworkManager'],
capture_output=True, timeout=30
)
subprocess.run(
['sudo', 'airmon-ng', 'check', 'kill'],
capture_output=True, timeout=30
)
result = subprocess.run(
['sudo', 'airmon-ng', 'start', self.interface],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=60
)
if "monitor mode enabled" in result.stdout:
self.interface = self.interface + "mon"
print(f"监控模式已启用: {self.interface}")
return True
except Exception as e:
print(f"环境设置失败: {e}")
return False
def _setup_windows_environment(self):
"""设置Windows环境"""
try:
# 使用Scapy验证接口
try:
iface_list = get_windows_if_list()
interface_found = False
for iface in iface_list:
if_name = iface.get('name', '')
if_desc = iface.get('description', '')
if (self.interface.lower() in if_name.lower() or
self.interface.lower() in if_desc.lower()):
interface_found = True
break
if not interface_found:
print(f"警告: Scapy未找到接口 {self.interface}")
# 尝试查找正确的接口
correct_interface = self._find_correct_windows_interface()
if correct_interface:
self.interface = correct_interface
print(f"自动切换到接口: {self.interface}")
return True
else:
return False
else:
return True
except Exception as e:
print(f"Scapy接口验证失败: {e}")
return self._fallback_windows_interface_verification()
except Exception as e:
print(f"接口验证失败: {e}")
return False
def _find_correct_windows_interface(self):
"""查找正确的Windows接口名称"""
try:
# 使用Scapy获取所有接口
iface_list = get_windows_if_list()
for iface in iface_list:
if_desc = iface.get('description', '').lower()
if 'wireless' in if_desc or 'wi-fi' in if_desc or 'wlan' in if_desc:
return iface.get('name', '')
except:
pass
# 备用方法: 使用netsh
try:
result = subprocess.run(
['netsh', 'interface', 'show', 'interface'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode == 0:
for line in result.stdout.split('\n'):
if 'Connected' in line and ('Wireless' in line or 'Wi-Fi' in line):
parts = line.split()
if len(parts) > 3:
interface_name = parts[-1]
return interface_name
except:
pass
return None
def _fallback_windows_interface_verification(self):
"""Windows接口验证备用方法"""
try:
result = subprocess.run(
['netsh', 'interface', 'show', 'interface', self.interface],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode != 0 or '不存在' in result.stdout or 'not found' in result.stdout:
print(f"警告: 接口 {self.interface} 未找到")
result = subprocess.run(
['netsh', 'interface', 'show', 'interface'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
if result.returncode == 0:
print("可用接口:")
for line in result.stdout.split('\n'):
if 'Connected' in line:
print(f" {line}")
correct_interface = self._find_correct_windows_interface()
if correct_interface:
self.interface = correct_interface
print(f"自动切换到接口: {self.interface}")
return True
else:
return False
return True
except Exception as e:
print(f"备用接口验证失败: {e}")
return False
def trigger_reconnection(self):
"""触发设备WiFi重连"""
print("触发设备重连...")
if self.os_type == "Linux":
self._linux_reconnection_trigger()
else:
self._windows_reconnection_trigger()
def _linux_reconnection_trigger(self):
"""Linux系统重连触发"""
try:
subprocess.run(
['sudo', 'mdk4', self.interface, 'd'],
timeout=10, capture_output=True
)
print("MDK4重连攻击完成")
except Exception as e:
print(f"MDK4攻击失败: {e}")
self._channel_hopping_trigger()
def _channel_hopping_trigger(self):
"""信道切换触发重连"""
try:
channels = [1, 6, 11]
original_channel = self._get_current_channel()
for channel in channels:
if channel != original_channel:
subprocess.run(
['sudo', 'iwconfig', self.interface, 'channel', str(channel)],
capture_output=True, timeout=10
)
time.sleep(2)
subprocess.run(
['sudo', 'iwconfig', self.interface, 'channel', str(original_channel)],
capture_output=True, timeout=10
)
print("信道切换重连完成")
except Exception as e:
print(f"信道切换失败: {e}")
def _get_current_channel(self):
"""获取当前信道"""
try:
result = subprocess.run(
['sudo', 'iwlist', self.interface, 'channel'],
capture_output=True, text=True, encoding='utf-8', errors='ignore', timeout=10
)
for line in result.stdout.split('\n'):
if 'Current Frequency' in line:
channel = int(line.split('Channel ')[1].split(')')[0])
return channel
except:
pass
return 1
def _windows_reconnection_trigger(self):
"""Windows系统重连触发"""
try:
print("通过接口重置触发重连...")
subprocess.run(
['netsh', 'interface', 'set', 'interface', self.interface, 'admin=disable'],
capture_output=True, timeout=30
)
time.sleep(3)
subprocess.run(
['netsh', 'interface', 'set', 'interface', self.interface, 'admin=enable'],
capture_output=True, timeout=30
)
time.sleep(5)
print("接口重置完成")
except Exception as e:
print(f"接口重置失败: {e}")
self._restart_wlan_service()
def _restart_wlan_service(self):
"""重启WLAN服务触发重连"""
try:
print("重启WLAN服务触发重连...")
subprocess.run(
['net', 'stop', 'WlanSvc'],
capture_output=True, timeout=30
)
time.sleep(2)
subprocess.run(
['net', 'start', 'WlanSvc'],
capture_output=True, timeout=30
)
time.sleep(5)
print("WLAN服务重启完成")
except Exception as e:
print(f"WLAN服务重启失败: {e}")
def capture_traffic(self):
"""捕获网络流量 - 修复Windows流量捕获问题"""
print(f"开始捕获流量,持续时间: {self.capture_duration}秒")
start_time = time.time()
self.captured_packets = []
self.device_profiles = {}
def packet_handler(packet):
try:
self._process_packet(packet)
self.captured_packets.append(packet)
except Exception as e:
pass
try:
timeout = min(self.capture_duration, 60) if self.os_type == "Windows" else self.capture_duration
if self.os_type == "Linux":
sniff(iface=self.interface, prn=packet_handler, timeout=timeout, store=0)
else:
# Windows系统使用改进的捕获方法
try:
# 获取Scapy识别的接口列表
iface_list = get_windows_if_list()
actual_interface = None
print("Scapy识别的接口列表:")
for iface in iface_list:
if_name = iface.get('name', '')
if_desc = iface.get('description', '')
print(f" 接口: {if_name}, 描述: {if_desc}")
# 检查接口名称或描述是否匹配
if (self.interface.lower() in if_name.lower() or
self.interface.lower() in if_desc.lower()):
actual_interface = if_name
break
if actual_interface:
print(f"使用Scapy接口: {actual_interface}")
# 尝试多种捕获方法
packets_captured = self._enhanced_windows_capture(actual_interface, timeout, packet_handler)
if packets_captured == 0:
print("Scapy捕获失败,尝试备用方法")
return self._fallback_windows_capture(timeout)
else:
return True
else:
# 如果没有找到精确匹配,尝试使用第一个无线接口
for iface in iface_list:
if_desc = iface.get('description', '').lower()
if 'wireless' in if_desc or 'wi-fi' in if_desc or 'wlan' in if_desc:
actual_interface = iface.get('name', '')
print(f"使用无线接口: {actual_interface}")
packets_captured = self._enhanced_windows_capture(actual_interface, timeout,
packet_handler)
if packets_captured > 0:
return True
break
else:
# 如果还没有找到,使用第一个可用接口
if iface_list:
actual_interface = iface_list[0]['name']
print(f"使用第一个可用接口: {actual_interface}")
packets_captured = self._enhanced_windows_capture(actual_interface, timeout,
packet_handler)
if packets_captured > 0:
return True
else:
raise Exception("未找到可用的网络接口")
# 如果所有Scapy方法都失败,使用备用方法
return self._fallback_windows_capture(timeout)
except Exception as e:
print(f"Scapy捕获失败: {e}")
return self._fallback_windows_capture(timeout)
print(f"流量捕获完成,共处理 {len(self.captured_packets)} 个数据包")
return len(self.captured_packets) > 0
except Exception as e:
print(f"流量捕获错误: {e}")
return False
def _enhanced_windows_capture(self, interface, timeout, packet_handler):
"""增强的Windows捕获方法"""
packets_captured = 0
start_time = time.time()
# 方法1: 标准Scapy捕获
try:
print(f"尝试标准Scapy捕获 on {interface}")
sniff(iface=interface, prn=packet_handler, timeout=timeout, store=0)
packets_captured = len(self.captured_packets)
if packets_captured > 0:
print(f"标准Scapy捕获成功: {packets_captured} 个数据包")
return packets_captured
except Exception as e:
print(f"标准Scapy捕获失败: {e}")
# 方法2: 使用原始套接字作为备用
if packets_captured == 0:
print("尝试原始套接字捕获...")
try:
# 创建原始套接字
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.settimeout(5)
# 绑定到本地IP
local_ip = self._get_local_ip()
s.bind((local_ip, 0))
# 启用混杂模式
try:
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
except:
print("警告: 无法启用混杂模式")
while time.time() - start_time < timeout:
try:
data, addr = s.recvfrom(65535)
packets_captured += 1
# 创建模拟的数据包对象用于分析
class MockPacket:
def __init__(self, data, addr):
self.data = data
self.addr = addr
self.time = time.time()
self.length = len(data)
mock_packet = MockPacket(data, addr)
packet_handler(mock_packet)
# 限制捕获速率避免过度占用CPU
time.sleep(0.01)
except socket.timeout:
continue
except:
break
# 关闭混杂模式
try:
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
except:
pass
s.close()
print(f"原始套接字捕获完成: {packets_captured} 个数据包")
except Exception as e:
print(f"原始套接字捕获失败: {e}")
return packets_captured
def _fallback_windows_capture(self, timeout):
"""Windows备用捕获方法"""
print("使用备用方法捕获流量...")
try:
# 使用系统工具捕获流量
if self.os_type == "Windows":
return self._raw_socket_capture(timeout)
else:
return self._tcpdump_capture(timeout)
except Exception as e:
print(f"备用捕获方法也失败: {e}")
return False
def _raw_socket_capture(self, timeout):
"""原始套接字捕获"""
try:
# 创建原始套接字
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.settimeout(5)
local_ip = self._get_local_ip()
s.bind((local_ip, 0))
# 启用混杂模式
try:
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
except:
print("警告: 无法启用混杂模式")
start_time = time.time()
packet_count = 0
while time.time() - start_time < timeout:
try:
data, addr = s.recvfrom(65535)
packet_count += 1
# 创建模拟的数据包对象
class MockPacket:
def __init__(self, data, addr):
self.data = data
self.addr = addr
self.time = time.time()
self.length = len(data)
mock_packet = MockPacket(data, addr)
self._process_packet(mock_packet)
self.captured_packets.append(mock_packet)
time.sleep(0.01)
except socket.timeout:
continue
except:
break
# 关闭混杂模式
try:
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
except:
pass
s.close()
print(f"原始套接字捕获完成,处理 {packet_count} 个数据包")
return packet_count > 0
except Exception as e:
print(f"原始套接字捕获失败: {e}")
return False
def _get_local_ip(self):
"""获取本地IP地址"""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(("8.8.8.8", 80))
ip = s.getsockname()[0]
s.close()
return ip
except:
return "127.0.0.1"
def _tcpdump_capture(self, timeout):
"""使用tcpdump捕获(Linux备用)"""
try:
cmd = f"timeout {min(timeout, 30)} tcpdump -i {self.interface} -c 100 -w /tmp/capture.pcap"
subprocess.run(cmd.split(), capture_output=True, timeout=timeout + 5)
return True
except:
return False
def _process_packet(self, packet):
"""处理单个数据包"""
if hasattr(packet, 'type'):
if hasattr(packet, 'type') and packet.type == 0:
self._process_dot11_packet(packet)
elif hasattr(packet, 'type') and packet.type == 2:
self._process_data_packet(packet)
else:
# 处理原始套接字数据包
self._process_raw_packet(packet)
def _process_dot11_packet(self, packet):
"""处理802.11数据包"""
if not hasattr(packet, 'addr2') or not packet.addr2:
return
mac = packet.addr2.upper() if hasattr(packet.addr2, 'upper') else str(packet.addr2)
if mac not in self.device_profiles:
self.device_profiles[mac] = {
'mac': mac,
'association_attempts': 0,
'reconnection_times': [],
'packet_sizes': [],
'timestamps': [],
'frame_types': [],
'first_seen': datetime.now(),
'last_seen': datetime.now()
}
profile = self.device_profiles[mac]
profile['last_seen'] = datetime.now()
profile['packet_sizes'].append(len(packet))
profile['timestamps'].append(packet.time)
if hasattr(packet, 'subtype'):
profile['frame_types'].append(packet.subtype)
if hasattr(packet, 'type') and packet.type == 0:
if hasattr(packet, 'subtype') and packet.subtype == 0:
profile['association_attempts'] += 1
profile['reconnection_times'].append(datetime.now())
def _process_data_packet(self, packet):
"""处理数据帧"""
pass
def _process_raw_packet(self, packet):
"""处理原始数据包"""
# 简化处理原始套接字数据包
if hasattr(packet, 'addr'):
mac = self._extract_mac_from_raw(packet)
if mac:
if mac not in self.device_profiles:
self.device_profiles[mac] = {
'mac': mac,
'association_attempts': 0,
'reconnection_times': [],
'packet_sizes': [],
'timestamps': [],
'frame_types': [],
'first_seen': datetime.now(),
'last_seen': datetime.now()
}
profile = self.device_profiles[mac]
profile['last_seen'] = datetime.now()
profile['packet_sizes'].append(packet.length)
profile['timestamps'].append(packet.time)
def _extract_mac_from_raw(self, packet):
"""从原始数据包提取MAC地址"""
# 简化实现,实际应解析以太网帧头
if hasattr(packet, 'addr'):
return str(packet.addr)
return None
def extract_features(self):
"""从流量数据中提取特征"""
if not self.device_profiles:
print("未发现设备,无法提取特征")
return None, None
features = []
mac_addresses = []
for mac, profile in self.device_profiles.items():
device_features = self._extract_device_features(profile)
if device_features is not None:
features.append(device_features)
mac_addresses.append(mac)
if not features:
return None, None
return np.array(features), mac_addresses
def _extract_device_features(self, profile):
"""提取单个设备的特征向量"""
try:
features = []
reconnection_times = profile.get('reconnection_times', [])
reconnection_count = len(reconnection_times)
association_attempts = profile.get('association_attempts', 0)
timestamps = profile.get('timestamps', [])
if len(timestamps) > 1:
sorted_timestamps = sorted(timestamps)
time_diffs = np.diff(sorted_timestamps)
avg_interval = np.mean(time_diffs) if time_diffs.size > 0 else 0
interval_std = np.std(time_diffs) if time_diffs.size > 0 else 0
else:
avg_interval = 0
interval_std = 0
features.extend([
association_attempts,
reconnection_count,
avg_interval,
interval_std,
])
packet_sizes = profile.get('packet_sizes', [])
if packet_sizes:
features.extend([
np.mean(packet_sizes),
np.std(packet_sizes),
len(packet_sizes),
])
else:
features.extend([0, 0, 0])
features.extend([
1 if profile.get('rtsp_detected', False) else 0,
len(profile.get('video_ports', [])),
])
mac = profile['mac']
is_camera_oui = self._is_camera_mac(mac)
features.append(1 if is_camera_oui else 0)
return features
except Exception as e:
print(f"特征提取错误 {profile.get('mac', 'unknown')}: {e}")
return None
def _is_camera_mac(self, mac):
"""基于MAC地址OUI判断是否为摄像头厂商"""
camera_ouis = {
'00:12:1C', '00:13:5A', '00:15:6D', '00:18:4D', '00:1B:FC',
'00:1E:45', '00:21:5A', '00:23:CD', '00:24:54', '00:26:5A',
'00:50:C2', '00:E0:4C', '08:10:76', '08:18:1A', '08:76:FF'
}
if len(mac) >= 8:
oui = mac[:8].upper()
return oui in camera_ouis
return False
def train_model(self):
"""训练检测模型"""
print("训练摄像头检测模型...")
X, y = self._generate_training_data()
X_scaled = self.scaler.fit_transform(X)
self.model = RandomForestClassifier(
n_estimators=100,
max_depth=10,
min_samples_split=5,
min_samples_leaf=2,
random_state=42
)
self.model.fit(X_scaled, y)
print("模型训练完成")
return self.model
def _generate_training_data(self):
"""生成训练数据"""
n_samples = 1000
X = []
y = []
for _ in range(n_samples // 2):
features = [
np.random.poisson(3),
np.random.poisson(4),
np.random.normal(2, 1),
np.random.normal(1, 0.5),
np.random.normal(800, 100),
np.random.normal(100, 20),
np.random.poisson(50),
1,
np.random.poisson(2),
1,
]
X.append(features)
y.append(1)
for _ in range(n_samples // 2):
features = [
np.random.poisson(1),
np.random.poisson(1),
np.random.normal(10, 5),
np.random.normal(5, 3),
np.random.normal(400, 200),
np.random.normal(200, 100),
np.random.poisson(20),
0,
np.random.poisson(0.5),
0,
]
X.append(features)
y.append(0)
return np.array(X), np.array(y)
def detect_cameras(self):
"""执行摄像头检测"""
print("开始隐蔽摄像头检测...")
print("=" * 60)
if not self.setup_environment():
print("环境设置失败")
return []
self.trigger_reconnection()
if not self.capture_traffic():
print("流量捕获失败")
return []
features, macs = self.extract_features()
if features is None or len(features) == 0:
print("无有效特征可提取")
return []
if self.model is None:
self.train_model()
features_scaled = self.scaler.transform(features)
predictions = self.model.predict(features_scaled)
probabilities = self.model.predict_proba(features_scaled)
results = []
for i, (mac, pred, prob) in enumerate(zip(macs, predictions, probabilities)):
camera_prob = prob[1] if len(prob) > 1 else 0
risk_level = "高风险" if camera_prob > 0.8 else "中风险" if camera_prob > 0.6 else "低风险"
result = {
'mac': mac,
'is_camera': bool(pred),
'probability': camera_prob,
'risk_level': risk_level,
'timestamp': datetime.now()
}
results.append(result)
status = "摄像头" if pred else "非摄像头"
print(f"设备 {i + 1}: {mac} - {status} (置信度: {camera_prob:.3f}, 风险: {risk_level})")
return results
def generate_report(self, results):
"""生成检测报告"""
print("\n" + "=" * 60)
print("隐蔽摄像头检测报告")
print("=" * 60)
cameras = [r for r in results if r['is_camera']]
if not cameras:
print("✅ 未检测到隐蔽摄像头")
print("网络环境相对安全")
else:
print(f"🚨 检测到 {len(cameras)} 个可疑摄像头:")
for i, cam in enumerate(cameras, 1):
print(f"\n{i}. MAC: {cam['mac']}")
print(f" 置信度: {cam['probability']:.3f}")
print(f" 风险等级: {cam['risk_level']}")
print(f" 检测时间: {cam['timestamp']}")
if cam['probability'] > 0.8:
print(" ⚠️ 高风险设备,建议立即检查")
print("\n安全建议:")
print("1. 检查上述设备物理位置")
print("2. 断开可疑设备连接")
print("3. 更改WiFi密码")
print("4. 加强网络监控")
def main():
"""主函数"""
interface = None
if len(sys.argv) > 1:
interface = sys.argv[1]
print(f"使用指定接口: {interface}")
detector = WiFiCameraDetector(interface=interface)
try:
results = detector.detect_cameras()
detector.generate_report(results)
except KeyboardInterrupt:
print("\n检测被用户中断")
except Exception as e:
print(f"检测错误: {e}")
print(f"\n检测完成: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
if __name__ == "__main__":
main()