DavidWang1231/WashroomSweep
GitHub: DavidWang1231/WashroomSweep
基于 ESP32 被动分析 WiFi 流量的低成本隐蔽直播摄像头检测器,通过上下行流量不对称和光照刺激下的可变比特率响应来判断空间内是否存在正在流媒体传输的联网摄像头。
Stars: 0 | Forks: 0
# WashroomSweep
一款低成本、独立的扫描设备,可以在您**进入封闭空间**(洗手间、更衣室、短租房)**之前**,告知您是否有联网摄像头**正在直播该空间**。
它回答的是*“此时此刻是否有东西在这里录像”*——而不是*“这个网络上有多少设备”*。
由两名滑铁卢大学本科生为**Wireless for Humanity 学生设计竞赛**开发。

*真实抓拍,重放演示。被标记的设备维持了 622 KB/s 的上传且零下行;房间内的所有其他设备均比其低三个数量级。*
## 工作原理
处于 WiFi promiscuous mode 的 ESP32 会被动计算每个源 MAC 的字节数。
无需解密,无需网络关联——仅使用 802.11 header fields(发送方地址、帧长度、RSSI)。
两种结合使用的检测信号:
1. **Uplink/downlink asymmetry(上行/下行不对称)。** 直播摄像头发送的数据远大于接收的数据;手机和笔记本电脑则完全相反。这是一个粗略的过滤器。
2. **对比特率对刺激的响应。** 摄像头使用 variable-bitrate encoding(可变比特率编码),因此改变摄像头*看到的*内容,就会改变它*发送的*数据量。我们以已知的方波(关闭 3 秒 / 开启 3 秒,重复 5 次)来切换房间灯光,并将每个设备的字节率序列与该参考进行 cross-correlate(互相关)匹配,同时进行小幅度的延迟搜索(摄像头会缓冲,因此允许最高约 2 秒的偏移)。高相关性意味着该设备可以看到这个房间。
判定结果始终为以下之一:
```
NO NETWORKED CAMERA DETECTED
CANDIDATE DETECTED
UNKNOWN - HIGH AMBIENT TRAFFIC
UNKNOWN - INCOMPLETE SWEEP
```
我们刻意不输出“SAFE”或“no camera”:RF 静默并不能证明其不存在。
## 我们的主张与不主张之处
我们**不**主张该机制具有新颖性——基于 traffic-analysis 的摄像头检测是已有的现有技术(DeWiCam、CSI:DeSpy、SnoopDog、Lumos、LocCams)。我们的贡献在于:
- **低成本的 ESP32 传感前端,辅以笔记本电脑分析**(RF 路径中无需笔记本电脑级别的抓取硬件;相关性分析本身在主机上通过约 100 行 Python 代码运行),
- **entry-sweep(入口扫描)**场景:进入空间前的快速检查,
- 使用房间**现有的照明开关**作为刺激源,以及
- 量化**该方法何时失效**。
### 已知的盲区(如实陈述)
- **本地存储摄像头**(录制到 SD 卡而非进行流媒体传输)不产生 RF 流量,通过此方法**无法检测**。
- **有线摄像头**同样在 RF 监控中不可见。
- **仅限 2.4 GHz。** ESP32 radio(涵盖 classic、C3 和 S3)无法看到 5 GHz 或 6 GHz WiFi;在这些频段上进行流媒体传输的摄像头对此硬件是不可见的。
- **802.11ax (WiFi 6) 链路不可见——已实测。** ESP32 radio 仅能解调 802.11b/g/n。在使用现代 iPhone 热点为 MacBook 提供网络的情况下,我们通过 2.4 GHz 信道 6 测得了 **6.6 MB/s** 的下载速度,而停泊在同一信道上的 sniffer 仅恢复出了 **~2 KB/s —— 约 0.03%** 的数据。同时验证了接收路径状态良好:驱动程序报告了 `channel=6`,并且来自附近 AP 的 beacons 以正确的 100 ms 节奏到达,信号强度为 −50 到 −61 dBm。将 FCS 校验失败的帧作为 airtime 代理进行计算也无济于事——该数字保持平稳(闲置时 415 KB/s,下载时 402 KB/s),因为它受周围网络的环境噪声主导,而不是受测试链路的影响。这是一个硬件极限,没有软件的解决办法。
实际影响:该方法仍然适用于预期的目标,因为廉价的 IP 摄像头通常是 802.11n,但任何协商使用 802.11ax 链路的摄像头都超出了此前端的检测范围。
- **RSSI 不等于距离。** 我们报告信号强度,但不进行定位,也绝不声称能确定设备的位置。
- 繁忙的 RF 环境可能会淹没信号;届时工具会报告 `UNKNOWN - HIGH AMBIENT TRAFFIC`,而不是盲目猜测。
- 如果 serial 数据流在扫描过程中途中断(开发板拔出、信道错误),判定结果将是 `UNKNOWN - INCOMPLETE SWEEP`——开发板每 200 ms 发出的一次 heartbeat 是证明我们确实在监听的必要证据。
## 仓库布局
```
esp32/wifi_sniffer/ Main detector. Promiscuous capture; uplink/downlink split
via the 802.11 ToDS/FromDS bits (works for any BSS, no AP
knowledge needed); per-client byte counts in 200 ms
windows; CSV + heartbeat over serial; MARK for stimulus
alignment; channel lock or scan.
esp32/softap_demo/ The board hosts its own 802.11n network while sniffing it,
which is how a camera gets onto a PHY this radio can read.
Finds the camera on its own and drains the stream so the
phone keeps uploading without a separate viewer.
esp32/ble_scanner/ Justin's BLE board: scans advertisements, reports over
UDP. Needs the Huge APP partition scheme; classic ESP32
only. See its README.
esp32/ble_presence/ Portable BLE variant that also builds for C3/S3.
host/sweep.py Correlator. Reads the serial CSV, builds per-MAC byte-rate
series, generates the square-wave reference at the
operator's mark, cross-correlates with a lag search.
Logs each run to logs/.
dashboard/ Live console (Justin's). Device tables, one-button sweep,
UDP ingest, and a banner that flags the upload-only shape
immediately rather than only after a 30 s sweep.
slides/ Presentation: PDF to present from, .pptx to share, HTML
with Chinese speaker notes.
logs/ Raw CSV from the runs quoted in this README.
```
## 快速开始
**ESP32(Arduino IDE 或 arduino-cli):** 将
`esp32/wifi_sniffer/wifi_sniffer.ino` 刷入检测开发板。Serial 配置为
115200 波特率。通过 serial 发送的命令:`CH `(锁定信道)、`HOP`(切换信道跳频)、`AP ` / `AP OFF`(将计数限制在一个 BSSID / 清除限制)、`MARK`。
**仪表盘**(您实际要监视的内容):
```
cd dashboard
../host/.venv/bin/python monitor.py --port /dev/cu.usbserial-XXXX
```
在 http://localhost:8080 上打开。
**仅运行相关性分析器**(终端,无仪表盘):
```
cd host
python3 -m venv .venv && ./.venv/bin/pip install -r requirements.txt
./.venv/bin/python sweep.py /dev/cu.usbserial-XXXX --channel 6 --ap AA:BB:CC:DD:EE:FF
```
打开端口会重置 ESP32 (DTR),因此 `sweep.py` 会等待重启并自行推送信道/BSSID 配置——不要通过 serial 监视器进行预配置。`--ap` 会在受控测试中抑制邻近网络的影响;在真实的扫描中请省略它,因为您并不清楚摄像头的 AP 是哪个。
在您第一次关**灯**的瞬间按下 Enter 键,然后将灯切换为关闭 3 秒 / 开启 3 秒,持续五个周期。脚本会记录、进行相关性分析,并输出判定结果以及包含证据计数的每台设备表格。
## 验证
目前已验证的内容——以及同样重要的,尚未验证的内容:
- 试点测试(网络侧):一台 iPhone 在进行视频流传输时,测得开灯时约为 5 MB/s,而关灯时约为 2 MB/s(~2.4 倍)——VBR 假设成立。
这是在笔记本电脑上于网络*内部*测量的,而非通过无线电波。
- 主机 pipeline(仅限模拟):针对一个通过 pseudo-terminal 交互并使用真实 serial 协议的模拟 ESP32 进行了端到端验证。一个具有 0.4 秒编码器延迟的类摄像头设备被成功检测到(相关性为 1.00);闲置的手机和正在进行大文件下载的设备均未被标记(仅凭高流量不能判定为摄像头);在扫描中途中断数据流会产生 `UNKNOWN - INCOMPLETE SWEEP`,而不是错误地发出安全信号。
- 两份 sketch 均能在 classic ESP32、ESP32-C3 和 ESP32-S3 上顺利编译
(arduino-esp32 core 3.3.11)。
- 真实硬件上的无线电波抓包(classic ESP32、ESP32-D0WD-V3):
**适用于 802.11b/g/n 流量。** 在锁定的信道上运行 15 秒
恢复了 18 个不同的客户端 MAC,并获得了每台设备的 uplink/downlink 计数
以及极其稳定的 200 ms 报告节奏(heartbeat 间隔在 59 个连续窗口中测得刚好为 200 ms,零重置)。
- **通过 asymmetry 信号进行实时的无线电波检测:已验证。**
为了让替代摄像头出现在此硬件可读取的 PHY 上,我们将
ESP32 作为其自身的 SoftAP 运行(强制使用 802.11n),同时在同一个 radio 上以 promiscuous mode 进行嗅探,并让它持续从连接到该 AP 的手机上耗取 HTTP 摄像头流。在 30 秒的抓包过程中,正在进行流媒体传输的手机显示为
**17.9 MB 上行 / 0 下行 —— 占所有 air traffic 的 99.9%**,比其他所有客户端高出三个数量级(所有其他的流量均 < 12 KB)。单凭 uplink/downlink asymmetry 就能将摄像头与其他所有设备彻底区分开来,这是通过真实的 RF 实现的。
- **无线电波上的光照刺激相关性分析:未在此替代方案中闭环。**
尝试了六次,每一次都因为手机作为摄像头的一个不同特性而受阻,这些失败值得记录下来,因为它们界定了有效的替代方案需要具备什么条件:
1. *固定比特率。* 在其默认模式下,无论场景如何,该应用都以固定的 ~550 KB/s 进行流传输。切换房间灯光仅产生缓慢的 ~2.4x 漂移(与试点测得的比率相匹配),并且未与 3 秒的节奏实现相位锁定;遮挡镜头仅使其移动了 1.4 倍。
2. *自动增益。* 使场景变暗并不能简化它——传感器会进行放大,直到返回充满噪点、细节丰富且全比特率的图像。
3. *双摄像头合成。* 将应用切换到 MJPEG 确实使数据流变得可变(完全遮挡镜头会使数据量降至 ~1 KB,产生 300 倍以上的摆动,证实了该机制的有效性)。但该应用强制合成了前后摄像头的画面,而前置摄像头一直对着房间成像,并将底限维持在 ~430 KB/s,因此仅遮挡后置镜头永远无法使总数据量趋向于零。
因此,相关性分析目前仅在模拟和网络侧试点中得到验证。这是替代方案的保真度极限,而非方法本身的问题:有效的替代方案需要固定曝光、单一传感器和 variable-bitrate encoding——这正是真正的 IP 摄像头所具备的特性,也是在试点测试中测得 2.4 倍响应的来源。
### 由此暴露的缺陷
在针对硬件进行验证期间,发现并修复了两个真实的缺陷,这两个缺陷此前一直在悄无声息地破坏测试结果:
- **缺少 promiscuous filter。** 如果没有显式的
`esp_wifi_set_promiscuous_filter`,驱动程序也会交付 FCS 校验失败的帧。在繁忙的现代 AP 附近,这种情况频繁发生,而且它们的 header bytes 本质上是随机的——测量显示它们在所有八个 ToDS/FromDS 组合中呈近乎均匀的分布,而真正的基础设施流量应该集中在其中两个组合中。这些垃圾帧铸造了大量随机 MAC 地址,导致固定大小的设备表被淹没,并通过 LRU 机制驱逐了真正的站点,从而在每次报告之前将其计数器清零。
- **浮点分箱。** 计算为 `int(seconds / 0.2)` 的 bin indices(分箱索引)导致约 12% 的样本被错误归类(在纯净无噪音的测试中,151 个 bin 中有 18 个出现错误),将原本为 1.00 的真实相关性拖低至 0.71——这足以使原本可检测到的摄像头降至阈值以下。目前所有的分箱操作均已改为使用整数毫秒。
## 团队
- **David (Jiacheng) Wang** —— 检测 pipeline(ESP32 sniffer + 主机相关性分析器)
- **Justin Fang** —— 设备扫描基础工作
([washroom_security](https://github.com/justinfang37/washroom_security)),
BLE 存在感应面板
标签:ESP32, UML, 多模态安全, 嵌入式开发, 无线网络分析, 物理安全检测, 物联网, 网络安全, 逆向工具, 防御绕过, 隐私保护