diegomarino/faro-fan-flipper-remote

GitHub: diegomarino/faro-fan-flipper-remote

通过 Flipper Zero 利用 433 MHz 吊扇系列的广播后门地址实现通用遥控,并提供完整的逆向工程工具链和信号文件。

Stars: 2 | Forks: 0

# faro-fan-flipper-remote 通过 **Flipper Zero** 控制一个 433 MHz 吊扇系列——还包括完整的逆向工程工具链以及达成目标的记录。 就像大多数故事一样,这一切都是从弄丢遥控器开始的 :D 幸运的是,在家里翻新期间,我安装了好几台同款吊扇——相同的电路板,同为 2017 年批次——因此至少有一个能用的遥控器幸存了下来。但是(总会遇到一个*但是*——那种将你卷入一场充满黑客精神和纯粹固执的传奇故事的转折):这些设备是**出厂配对的,并且没有对码按钮**,因此每个接收器只服从*其自身*遥控器的固定地址。此外,这也是一个完美的借口,可以向我的妻子证明 Flipper Zero 是一份极棒的圣诞礼物——多亏了这件事,它现在可以说已经回本了。 恢复丢失遥控器的地址看起来似乎需要直接从接收器中提取 EEPROM dump……直到后来发现了一个简单得多的事实。 ![Fan Remote UI](https://static.pigsec.cn/wp-content/uploads/repos/cas/67/67ada46311f317306883b875ed177e494c8289e47aacab585e0cd7ef1287a3d0.png) ## 发现:`0xFFFFF` 是一个广播后门 能用的遥控器解码后得到一个稳定的固定码:一个 **20 位的共享地址**(`0xFFF12`)加上一个 9 位的按键字段。地址空间是 20 位,因此最后一个可能的地址是 `0xFFFFF`——**全是 1**。 这个全 1 的地址充当了一个**广播/出厂后门**:该系列中的每个接收器都会对它做出响应,无论它最初配对的专属地址是什么。已在两台吊扇上验证通过。因此,“丢失地址”的问题迎刃而解——你根本不需要餐厅原来的代码。你只需发送 `0xFFFFF`,范围内的*每一台*吊扇都会听从指令。 `signals/` 中的所有内容都是可用遥控器的真实捕获波形,并**被重新定向到了 `0xFFFFF`**(真实的空中传输时序,仅更改了地址位——参见 `tools/clone_patch.py`)。 ## 识别出的硬件 | 部件 | 数值 | |------|-------| | 电路板丝印 | `FNT-AC230W18TG-FIR2 V2.0`(日期为 2016-12-21) | | 接收器贴纸 | `F30-60Q34-L`,批次 `7H11D0182`,2017.08.2x | | 可用遥控器贴纸 | 批次 `7H11D0118`,2017.08.21 | | RF | 433.92 MHz OOK,固定码(非滚动码) | | 帧 | 20 位地址 + 9 位按键字段,约 7.15 ms 同步间隔 | | Burst | 作为 **40 帧**的 Burst 发送——边缘化的接收器需要长 Burst 才能锁存(快速单击会被忽略);真实的遥控器会发送约 52-65 帧 | | 风速编码 | 6 个绝对档位,信息字段 = `32 - 2xlevel`(互补对) | | 配对 | 出厂配对,**没有对码按钮,没有 DIP 开关** | `F30-60Q34-L` 和 `FNT-AC230W18TG-FIR2` 是通用的 OEM 部件——没有公开的数据手册或配对手册。 遥控器贴纸——`7H11D0080`(卧室设备,遥控器现已没电)和 `7H11D0118`(可用的参考遥控器,地址为 `0xFFF12`),同为 2017.08.21 批次: ![Remote stickers 0080 and 0118](https://static.pigsec.cn/wp-content/uploads/repos/cas/b9/b99a048e8dfc7dd6b99fe549411192543444b5cd959d53ea9e1ca8ea35c2b9c0.png) 接收器外壳——序列号 `7H11D0182`,部件号 `F30-60Q34-L`: ![Receiver enclosure labels](https://static.pigsec.cn/wp-content/uploads/repos/cas/d5/d5d7beba9ef2e188d5465f435fb1281d224ce786676080323ff785e6ca93a65a.png) 接收器 PCB(`FNT-AC230W18TG-FIR2 V2.0`,日期为 2016-12-21):左侧是电机 `U/V/W` 接线端子和 `RY1` 灯继电器,中右侧是 QFP MCU,右上角是 6 引脚的 `CN3`(解调后的 RF 数据,而非市电),紧挨着 MCU 右侧的是作为候选 EEPROM 的 SOIC-8 芯片: ![Receiver PCB](https://raw.githubusercontent.com/diegomarino/faro-fan-flipper-remote/main/docs/images/hardware-receiver-board.png) SOIC-8 芯片特写——标有 `SG1A` / `1631`(日期代码 2016 年第 31 周),带有一个类似 MCU 上的螺旋状内部 Logo。这就是通过提取 EEPROM dump 来恢复地址时需要针对的芯片;其确切型号尚未确认(这是一个内部/供应商代码,不是普通的 24Cxx): ![SOIC-8 candidate EEPROM, marked SG1A 1631](https://raw.githubusercontent.com/diegomarino/faro-fan-flipper-remote/main/docs/images/hardware-receiver-chip.png) ## 目录结构 ``` signals/ 10 working .sub files at 0xFFFFF (speeds 1-6, fan/light/reverse/dimmer) app/ fan_remote/ the custom Flipper app (embedded waveforms, prebuilt .fap) fan_debug/ transmit-path diagnostic (logs each TX step on screen) tools/ the reverse-engineering pipeline (Python, stdlib only) captures/ raw .sub recordings of the original remote (source data) docs/ findings.md (technical log) + images ``` ## 使用说明 - 应用程序 这个自定义应用程序**不需要复制任何文件**:波形已经内嵌其中,它会自动进行配置。将预编译好的 `app/fan_remote/dist/momentum/fan_remote.fap` 复制到你的 Flipper(`SD/apps/Sub-GHz/`),或者自行构建它——请参阅 [`app/fan_remote/README.md`](app/fan_remote/README.md)。然后在 Momentum 中启用 **MNTM → Protocols → SubGHz → Bypass Region Lock**(请参阅下方的免责声明)。 控制方式:**上/下** = 风速 1–6,**◄** = 反转,**►** = 吊扇开/关, **●/OK** = 灯光开/关。 ### …或者不使用应用程序 更喜欢使用 Flipper 原生的 **Sub-GHz → Saved** 菜单?只需将 `signals/*.sub` 复制到 SD 卡上的 `/ext/subghz/` 目录下并直接播放它们即可。波形是一样的。 ## 使用说明 - 工具链 `tools/` 下的所有内容都是纯 Python 编写的(仅使用标准库),并将文件路径作为参数。生成 `signals/` 的流水线如下: ``` # 1. 从其 captures 中解码原始 remote -> 共享地址 0xFFF12 python3 tools/subghz.py "captures/RAW-2026*.sub" # 2. 从一次杂乱的 up/down sweep 中重建 1..6 speed 顺序 python3 tools/order_levels.py captures/speeds.sub # (or: selftest) # 3. 将一次真实的 capture 重新定向至 40 frames 的 0xFFFFF broadcast(出厂 # burst length;button 为 light_onoff,即 gen_signals_h.py 读取的名称) python3 tools/clone_patch.py captures/bedroom-light.sub 0xFFFFF light_onoff signals 40 # 4. 从 signals/*.sub 重新生成两个 app 的 embedded signals.h (byte-exact), # 然后验证它们是否同步(gate 在任何 source<->header drift 时都会失败): python3 tools/gen_signals_h.py signals app/fan_remote/signals.h python3 tools/gen_signals_h.py signals app/fan_debug/signals.h python3 tools/verify.py ``` 其他工具:`sweep.py`(对 256 个候选地址进行时间同步的暴力破解,这是在发现广播后门之前的后备方案),`synth.py`(理想化的波形合成——作为参考保留;它在真实硬件上*失败了*,这就是为什么 `clone_patch.py` 要复用真实时序的原因),`label_hypothesis.py`(证伪了将贴纸数字当作地址的猜测)。 ## 走过的死胡同(记录下来以免重蹈覆辙) - **在空中暴力破解 2^20 个组合** - 验证才是最麻烦的问题,而且一旦 `0xFFFFF` 起作用,这就毫无意义了。 `tools/sweep.py` 曾将其范围缩小到 256 个(固定的 `0xFFF` 前导码 + 未知的低位字节),但最终即便是这也被证明是不必要的。 - **贴纸数字(`0118` / `0182`)** - *并非* RF 密钥。在 `tools/label_hypothesis.py` 中已被证伪。 - **读取接收器的 EEPROM** - 这是恢复丢失的设备专属地址的“正规”途径。虽然经过了充分的分析推演,但最终从未派上用场:广播功能使其变得毫无意义。分析详情请参见 `docs/findings.md`。 ## ⚠️ RF 射频传输 - 法律与道德声明 本项目会在 **433.92 MHz** 频率上进行发射,并使用了一个广播地址,能够控制无线电范围内该吊扇系列中的**任何**接收器——包括邻居家的吊扇,而不仅仅是你自己的。 - **频谱合规性由你个人负责。** 各国关于 ISM 频段的法规各不相同;请查看你所在地区允许进行何种低功率的无线电传输。 - **仅对你拥有或获得授权控制的硬件进行发射。** - 启用 Momentum 的 **Bypass Region Lock** 属于*对固件内置监管保护机制的蓄意覆盖*。在进行此操作之前,请务必了解你关闭的到底是什么。 提供此项目旨在实现互操作性、维修和教育目的。请负责任地使用。 ## 许可证 [MIT](LICENSE)。
标签:433MHz射频, Flipper Zero, 云资产清单, 客户端加密, 智能家居, 物联网, 逆向工具, 逆向工程