b00mhauercode/rgbboomarm-wled-bridge

GitHub: b00mhauercode/rgbboomarm-wled-bridge

对 MANKA/SUNMON 蓝牙 LED 灯带的 BLE 通信协议进行逆向工程,并通过模拟 WLED 协议实现 SignalRGB 的本地桥接控制,摆脱云端依赖。

Stars: 0 | Forks: 0

# MANKA-LED-STRIP BLE 逆向工程 ## 目录 1. [设备识别](#device-identification) 2. [逆向工程之旅](#the-reverse-engineering-journey) 3. [协议规范](#protocol-specification) 4. [寻找您的滚动码](#finding-your-rolling-code) 5. [安装](#installation) 6. [CLI 使用方法](#cli-usage) 7. [SignalRGB 集成](#signalrgb-integration) 8. [Mac Mini / 家用服务器](#mac-mini--home-server) 9. [系统架构](#architecture) 10. [此仓库中的文件](#files-in-this-repo) 11. [未来工作](#future-work) 12. [法律声明](#legal) ## 设备识别 | 字段 | 值 | |---|---| | 品牌 / App | Manka / HomeLinking (`com.hle.lhzm`) | | 产品 | SUNMON RGB 悬臂麦克风支架 LED 灯带 | | 型号 | SC1TW3CQ21NB | | 固件版本 | 1.0.25 | | BLE 广播名称 | `MANKA-LED-STRIP` | | 电源 | 5V USB | ### BLE 服务 | Service UUID | Characteristic | 属性 | 用途 | |---|---|---|---| | `FFF0` | `FFF3` | Write, Write-No-Response | **命令通道** — 在此发送所有控制数据包 | | `FFF0` | `FFF4` | Notify, Read | 响应/通知通道 — 在写入前订阅 | | `5833FF01` | `5833FF02` | Write | OTA 固件更新 — **忽略** | ## 逆向工程之旅 ### 我们最初尝试的方法(浪费了时间) HomeLinking 应用程序需要登录 **AWS 云账号**才能配对设备。这立刻使事情复杂化了——我们无法观察到任何简单的本地配对过程。 **尝试 1 — APK 反编译 (JADX)** 我们反编译了 HomeLinking APK 并找到了两个候选的传输协议: 1. **FBFBFB 协议** — `q1/c.java` + `r1/b.java` → 使用 FFF0/FFF3/FFF4,带有 `0xFB 0xFB 0xFB` 的魔数头部。发现的命令:`B3` (颜色)、`A0` (电源)、`B5` (查询)、`A6` (状态查询)。 2. **WiCom 协议** — `j1/q.java` → 使用 28 字节格式:`[wid 4B][dst 6B][src 6B][opCode+seq 4B][payload]` 我们花费了大量时间实现了这两个协议。反编译得到的 FBFBFB/B3 数据包格式看起来是正确的——它包含 RGB、亮度、场景 ID 字段——但**什么也没发生**。灯光没有改变,FFF4 也没有响应。我们尝试了: - 多种滚动码推导方式(基于 MAC 地址、全零、`0xFFFFFFFF`) - B3 和 A0 命令字节 - 完整的 WiCom 28 字节格式 - 扫描 APK 寻找任何加密或混淆 这一切都没有起作用。 **为什么暴力破解方法失败了:** APK 中有多个代码路径。我们实现了正确的*结构*,但使用了错误的*命令字节*,而且我们从 MAC 地址推导的滚动码是完全错误的——实际的滚动码来自云账号会话,而不是设备 MAC。如果没有真实的数据包抓取,我们只能靠猜测。 ### 突破口 — Android HCI Snoop 日志 转折点在于,当 HomeLinking 应用程序主动控制灯带时,我们直接从 Android 手机上抓取了蓝牙 HCI snoop 日志。 **过程:** 1. 在 Android 上启用“开发者选项” → 启用 **“蓝牙 HCI snoop 日志”** 2. 打开 HomeLinking,配对并控制灯带(改变颜色、开/关) 3. 通过 ADB 拉取日志: # 三星设备将日志存储在 bugreport zip 文件中 adb bugreport bugreport.zip # 解压路径:FS/data/log/bt/btsnoop_hci.log 4. 使用 `parse_btsnoop.py` 进行解析,以提取 ATT Write 数据包 snoop 日志立刻揭示了应用程序正在发送的**实际数据包**。拿到真实的捕获数据后,几分钟内一切就水落石出了。 ### Snoop 日志揭示了什么 可用的命令字节是 **`0x0A`** — 而不是 APK 反编译中发现的 `B3`。 滚动码**不是基于 MAC 派生的**。它是 HomeLinking 账号的 `userCode` 字段最后 4 个字符的 ASCII 字节。在我们的例子中,userCode 以 `"ABCD"` 结尾 → `bytes.fromhex("41424344")`。 ## 协议规范 ### 数据包格式 所有控制数据包均为 **20 字节**,作为 ATT Write-No-Response 发送至特征值 `FFF3`。 ``` Offset Size Field Value / Notes ────── ──── ───────────── ──────────────────────────────────────────── 0 3 Magic header 0xFB 0xFB 0xFB 3 1 Command 0x0A = set color/state 4 4 Rolling code ASCII bytes of last 4 chars of userCode (account-specific) 8 2 Scene ID 0x00 0x00 (solid color scene) 10 1 Mode byte 0x22 = solid color ON | 0x00 = OFF 11 1 Brightness 0–100 decimal 12 2 Speed 0x00 0x00 14 2 Reserved 0x00 0x00 16 1 Red 0–255 17 1 Green 0–255 18 1 Blue 0–255 19 1 Padding 0x00 ``` ### 颜色命令 ``` def pkt_color(r, g, b, lum=100): return bytes([0xFB, 0xFB, 0xFB, 0x0A]) + ROLLING + bytes([ 0x00, 0x00, # scene_id 0x22, # solid color mode (ON) lum & 0xFF, # brightness 0–100 0x00, 0x00, # speed 0x00, 0x00, # reserved r, g, b, 0x00, ]) ``` ### 关闭命令 ``` def pkt_off(): return bytes([0xFB, 0xFB, 0xFB, 0x0A]) + ROLLING + bytes(12) ``` 关闭数据包的结构相同,但所有尾随字节均清零 — 模式字节 `0x00` 表示关闭。 ### 发送数据包 在写入 FFF3 之前,请始终订阅 FFF4 的通知。设备需要进行此握手。 ``` async with BleakClient(DEVICE_MAC) as client: await client.start_notify(FFF4_UUID, lambda sender, data: None) await asyncio.sleep(0.5) await client.write_gatt_char(FFF3_UUID, packet, response=False) ``` ## 寻找您的滚动码 滚动码为 4 个字节 — 即您的 HomeLinking 账号 `userCode` 最后 4 个字符的 ASCII 编码。 ### 方法 1:Android HCI Snoop 日志 ✅ 已验证 这是我们使用过并确知有效的方法。您需要一部安装了 HomeLinking 应用且设备已完成配对的 Android 手机。 **步骤 1 — 在 Android 上启用 HCI 日志记录** 1. 进入 **设置 → 关于手机** → 连续点击“版本号” 7 次以启用“开发者选项” 2. 进入 **设置 → 开发者选项** → 启用 **“蓝牙 HCI snoop 日志”** 3. 关闭蓝牙,然后重新打开 **步骤 2 — 抓取流量** 1. 打开 HomeLinking,连接到您的灯带 2. 改变颜色,将其打开和关闭 3. 这会将真实的 BLE 数据包写入日志文件 **步骤 3 — 拉取日志** *在三星设备上 (Android 13+):* ``` adb bugreport bugreport.zip # 解压并查找:FS/data/log/bt/btsnoop_hci.log ``` *在原生 Android / Pixel 设备上:* ``` adb pull /sdcard/btsnoop_hci.log # 或 adb pull /data/misc/bluetooth/logs/btsnoop_hci.log ``` **步骤 4 — 解析** ``` python parse_btsnoop.py btsnoop_hci.log ``` 查找类似以下的行: ``` [pkt 123] SENT ATT WriteCmd handle=0x0012 payload(20B): fbfbfb0a41424344000022640000000000ff000000 ^^^^^^^^ rolling code bytes (4 bytes after fbfbfb0a) ``` **步骤 5 — 更新脚本** ``` # 在 manka.py / manka_wled_bridge.py 中: ROLLING = bytes.fromhex("YOUR8HEXCHARS") ``` ### 方法 2:iPhone + Mac 使用 PacketLogger ⚠️ 未测试 苹果提供了一个名为 **PacketLogger** 的工具,可以从连接的 iPhone 捕获蓝牙 HCI 日志,类似于 Android 的 HCI snoop 日志。 **要求:** - 安装了 HomeLinking 且 MANKA 灯带已完成配对的 iPhone - Mac (PacketLogger 仅限 macOS) - 免费的 Apple Developer 账号 **步骤 1 — 获取 PacketLogger** 1. 在 [developer.apple.com/download/all](https://developer.apple.com/download/all) 登录(免费账号) 2. 搜索 **"Additional Tools for Xcode"** 并下载与您的 Xcode 版本匹配的工具 3. 打开 DMG 文件 → 导航到 `Hardware/` → 将 `PacketLogger.app` 复制到“应用程序”中 **步骤 2 — 从 iPhone 捕获** 1. 通过 USB 线缆将 iPhone 连接到 Mac 2. 打开 PacketLogger 3. 进入 **File → New iOS Bluetooth Log** — 选择您的 iPhone 4. 在 iPhone 上打开 HomeLinking,连接到灯带,并改变一些颜色 5. 在 PacketLogger 中停止捕获 **步骤 3 — 查找滚动码** PacketLogger 使用其专有的 `.pklg` 格式。您可以直接在其界面中查看 ATT Write 数据包 — 查找以 `fb fb fb 0a` 开头的 20 字节 payload。`fb fb fb 0a` 之后的 4 个字节就是您的滚动码。 或者,通过 **File → Save As** 导出,如果可用,请选择 btsnoop 格式,然后对输出文件运行 `parse_btsnoop.py`。 ### 方法 3:Windows HCI 日志记录 ⚠️ 未测试,较为复杂 Windows 10/11 通过注册表项内置了 HCI 日志记录功能: 1. 以管理员身份打开 **注册表编辑器** 2. 导航至: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\BTHPORT\Parameters 3. 创建一个新的 DWORD 值:`EnableHciLogging` = `1` 4. 重启 5. 日志将出现在 `C:\Windows\Temp\bthport.log`(ETL 格式,而非 btsnoop) 问题在于:这捕获的是您的 *Windows 机器*的蓝牙流量,而不是手机的。要使用此方法,您需要通过 Android 模拟器(BlueStacks、LDPlayer 等)在 Windows 上运行 HomeLinking,并将真正的蓝牙适配器直通给模拟器 — 这条路径在技术上是可行的,但难度较大且不太可能稳定支持 BLE。 **更实用的 Windows 替代方案:** 使用专用的 BLE 嗅探器 — 参见方法 4。 ### 方法 4:硬件 BLE 嗅探器 ⚠️ 未测试,存在加密流量问题 刷入了 nRF Sniffer 固件的 **Nordic Semiconductor nRF52840 Dongle**(约 10 美元)可以被动捕获 BLE 数据包,并将其提供给任何操作系统(Windows、Mac、Linux)上的 Wireshark。 **障碍 — BLE 加密:** 已配对设备之间的 BLE 连接使用初始配对期间建立的密钥进行加密。被动嗅探器只有在最初的配对交换期间在场以捕获会话密钥时,才能解密流量。如果您的设备已经配对,嗅探器将看到的是无法读取的加密 payload。 如果在以下情况下,这*可能*仍然有效: - 您在嗅探器运行以捕获配对握手时,取消配对并重新配对设备 - 设备完全不使用 BLE 配对/绑定(一些廉价设备不使用) **设置(如果您想尝试):** 1. 购买 [Nordic nRF52840 Dongle](https://www.nordicsemi.com/Products/Development-hardware/nRF52840-Dongle) 2. 刷入 [nRF Sniffer for Bluetooth LE firmware](https://www.nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE) 3. 安装 Wireshark + nRF Sniffer Wireshark 插件 4. 过滤您的设备 MAC 并查找发往句柄 `0x0012` 的 ATT Write 数据包 ### 方法 5:直接检查 HomeLinking 应用 🔍 建议优先尝试 滚动码源自您的 HomeLinking 账号的 `userCode`。此值可能直接显示在应用程序本身的某个地方,而无需任何数据包抓取。 **在设置嗅探器之前先试试这些:** - 打开 HomeLinking → 账号 / 个人资料设置 → 寻找任何“用户码”、“设备码”或 ID 字段 - 检查应用是否有“分享设备”或“邀请”功能,可能会显示一个代码 - 在 Android 上:检查应用的本地存储 `/data/data/com.hle.lhzm/shared_prefs/`(需要 root 权限或 Android 备份提取) 如果您找到了 userCode 字段,请提取最后 4 个字符并将它们转换为其 ASCII 十六进制字节: ``` # 示例:如果你的 userCode 以 "AB12" 结尾 "".join(f"{ord(c):02x}" for c in "AB12") # → "41423132" ROLLING = bytes.fromhex("41423132") ``` ### 为什么滚动码是特定于账号的? HomeLinking 应用程序在登录时向 AWS 进行身份验证,并接收一个会话 token / userCode。该 userCode 的一部分作为身份验证 token 嵌入到每个 BLE 数据包中,以便设备仅响应其配对的账号。它**不是**从设备 MAC 或任何硬件标识符派生出来的。 ## 安装 ### 要求 - Python 3.9+ - [bleak](https://github.com/hbldh/bleak) 0.21+ - Windows 10/11, macOS 12+, 或带有 BlueZ 的 Linux ``` pip install bleak ``` ### 克隆 / 下载 下载 `manka.py` 和 `manka_wled_bridge.py`(以及其他可选脚本)。 在两个文件中设置您的滚动码: ``` ROLLING = bytes.fromhex("41424344") # ← replace with yours (e.g. "ABCD" → "41424344") ``` 设置您的设备 MAC(如果不同,请使用 BLE 扫描仪应用进行检查): ``` DEVICE_MAC = "AA:BB:CC:DD:EE:FF" # ← replace with yours ``` ## CLI 使用方法 ``` python manka.py on python manka.py off python manka.py red python manka.py green python manka.py blue python manka.py white python manka.py warm python manka.py cyan python manka.py magenta python manka.py yellow python manka.py orange python manka.py purple python manka.py rgb 255 0 128 # custom RGB python manka.py rgb 255 0 0 50 # RGB + brightness (0–100) python manka.py bright 75 # brightness only, keeps current color # Scene/effect 探索 — 探测硬件动画模式 python manka.py scene 1 # try scene_id=1 (0x22 solid mode) python manka.py effect 0x01 # try mode byte 0x01 (0x22 = confirmed solid) ``` ## SignalRGB 集成 灯带作为 **WLED 设备**暴露给 SignalRGB — SignalRGB 具有内置的 WLED 支持,因此不需要自定义插件。 ### 架构 ``` SignalRGB (canvas effect) │ │ UDP port 21324 [DRGB streaming packets] ▼ manka_wled_bridge.py │ HTTP port 80 [WLED /json/info + /json/ API] │ │ BLE GATT Write to FFF3 ▼ MANKA-LED-STRIP ``` ### 设置 **步骤 1 — 启动桥接(在 Windows 上以管理员身份运行,80 端口需要此权限)** ``` python manka_wled_bridge.py ``` 您应该会看到: ``` WLED UDP streaming on :21324 WLED HTTP API on :80 Connecting to BLE AA:BB:CC:DD:EE:FF... BLE connected! ``` **步骤 2 — 在 SignalRGB 中添加设备** 1. 打开 SignalRGB 2. 进入 **Home → Lighting Services → WLED** 3. 在“通过 IP 发现 WLED 设备”框中,键入 `127.0.01` 并按 Enter 4. SignalRGB 调用 80 端口上的 `/json/info/`,返回 `"brand": "WLED"` 并添加该设备 5. 点击 **Link** — **"MANKA LED Strip"** 现在是您画布上的一个可控制设备 **步骤 3 — 分配特效** 将 MANKA LED Strip 区块拖动到 SignalRGB 画布上的任意位置。它将遵循分配给该画布位置的任何特效或氛围颜色。 ### Windows 自动启动 要让桥接随 Windows 自动启动: 1. 打开 **任务计划程序** 2. 点击 **“创建任务”**(而不是“基本任务”) 3. **常规 选项卡:** 将其命名为 `MANKA BLE Bridge`,勾选 **“使用最高权限运行”** 4. **触发器:** 新建 → 在系统启动时 → 延迟 30 秒(给蓝牙时间进行初始化) 5. **操作:** 新建 → 启动程序 - 程序: `python` - 参数: `C:\Users\YourName\Desktop\manka_wled_bridge.py` - 起始于: `C:\Users\YourName\Desktop` 6. **设置:** 勾选“如果错过了计划启动时间,应尽快运行任务” 7. 保存 — 在提示时输入您的 Windows 密码 ## Mac Mini / 家用服务器 该桥接无需修改代码即可在 macOS 上运行。Mac mini 必须在灯带的蓝牙范围(约 10 米)内。 ### macOS 设置 ``` pip3 install bleak ``` 在出现提示时授予 Python 蓝牙访问权限,或者进入 **系统设置 → 隐私与安全性 → 蓝牙** 并添加“终端”/Python。 **80 端口替代方案**(避免以 root 身份运行): ``` # 一次性:在 OS 层面将端口 80 重定向至 8080 echo "rdr pass on lo0 tcp from any to 127.0.0.1 port 80 -> 127.0.0.1 port 8080" | sudo pfctl -ef - ``` 然后在 `manka_wled_bridge.py` 中将 `HTTP_PORT = 80` 更改为 `HTTP_PORT = 8080`。 ### 使用 launchd 自动启动 创建 `/Library/LaunchDaemons/com.manka.bridge.plist`: ``` Label com.manka.bridge ProgramArguments /usr/bin/python3 /Users/YourName/manka_wled_bridge.py RunAtLoad KeepAlive StandardOutPath /var/log/manka_bridge.log StandardErrorPath /var/log/manka_bridge.log ``` ``` sudo launchctl load /Library/LaunchDaemons/com.manka.bridge.plist ``` `KeepAlive: true` 意味着如果桥接崩溃或 BLE 断开连接,macOS 将自动重新启动它。 ## 系统架构 一切都在单台机器上本地运行。无需云、无需互联网、无需外部服务器。 ``` ╔══════════════════════════════════════════════════════════════════════╗ ║ YOUR PC / MAC MINI ║ ║ (localhost / 127.0.0.1 only) ║ ║ ✗ not reachable from internet ║ ║ ║ ║ ┌────────────────────┐ ┌─────────────────────────────────┐ ║ ║ │ SignalRGB │ │ manka_wled_bridge.py │ ║ ║ │ │ │ │ ║ ║ │ Canvas effect │─────▶│ HTTP :80 (WLED discovery) │ ║ ║ │ assigns color │ UDP │ UDP :21324 (color stream) │ ║ ║ │ to MANKA block │─────▶│ │ ║ ║ └────────────────────┘ │ Color queue (thread-safe) │ ║ ║ │ BLE asyncio loop, max 20 Hz │ ║ ║ └────────────────┬────────────────┘ ║ ║ │ Bluetooth LE ║ ╚═════════════════════════════════════════════════╪════════════════════╝ │ ┌───────▼────────┐ │ MANKA-LED-STRIP │ │ BLE GATT FFF3 │ └─────────────────┘ ``` **网络边界说明:** - 两台服务器均绑定到 `127.0.0.1` — 流量绝不会离开本机 - 蓝牙仅限于本地无线电 — 约 10 米范围,无需网络 - 运行时无需账号、无需云调用、无需出站连接 ## 此仓库中的文件 | 文件 | 用途 | |---|---| | `manka.py` | 独立的 CLI 控制器 — 开/关/颜色/rgb/亮度 | | `manka_wled_bridge.py` | **主要集成** — WLED 模拟器 + SignalRGB 的 BLE 桥接 | | `parse_btsnoop.py` | HCI snoop 日志解析器 — 使用它来查找您的滚动码 | | `manka_test.py` | 开发测试脚本 — 发送颜色序列以验证灯带是否有响应 | | `manka_query.py` | BLE 查询/调试工具 — 测试查询命令并记录 FFF4 响应 | ## LED 硬件说明 ### 单区域灯带 — 不支持单 LED 寻址 SUNMON 悬臂包含一个多 LED 灯带,但整个灯带被作为**单一区域**驱动。无法通过已知的协议对单个 LED 进行寻址。 这已通过扫描所有 256 个模式字节 (0x00–0xFF) 并观察结果得到确认: - 每种模式都同时影响整个灯带 — 没有追逐效果,也没有分段颜色 - 某些模式字节会触发内置动画(渐变、呼吸效果),这证实了该灯带具有许多由 PWM 驱动的 LED - 没有任何模式能产生对不同 LED 的独立颜色控制 该协议在每个数据包中发送单个 RGB 三元组(偏移量 16–18 处的 `r, g, b`)。数据包格式中的任何地方都没有像素索引、像素数量或单 LED 的 payload 字段。 **实际意义:** 对于 SignalRGB 或任何其他氛围照明工具,应将灯带配置为 **1 像素设备**。发送多个像素毫无意义 — 只有第一种(或平均)颜色会被应用。 ## 未来工作 - [ ] **Apple HomeKit / Homebridge** — 通过 Homebridge 插件将灯带作为 HomeKit 配件暴露出来 ## 致谢 该协议是通过 Android HCI 蓝牙 snoop 日志捕获 + `parse_btsnoop.py` 发现的。HomeLinking APK 反编译 (JADX) 提供了有用的结构上下文,但确切可用的数据包字节只有从实时捕获中才变得清晰 — 如果您尝试做类似的事情,**请直接跳到 HCI 日志**。 ## 法律声明 此项目是为了作者所拥有硬件的个人互操作性用途而开发的。出于互操作性目的的逆向工程根据 DMCA §1201(f) (美国) 和其他司法管辖区的等效条款是被允许的。 此项目不隶属于、未受认可于、也未与 Manka、HomeLinking、SUNMON 或任何相关公司有关联。所有商标均为其各自所有者的财产。HomeLinking APK 仅用于本地分析,并未在此处分发。 使用此项目需自行承担风险,并受您所在司法管辖区法律的约束。作者不对任何误用负责。
标签:APK反编译, AWS云破解, BLE, CSV导出, HomeLinking, IoT, JADX, LED灯带控制, Manka, OTA升级, RGB灯效, SignalRGB, SUNMON, WLED, 云资产清单, 协议分析, 固件分析, 开源硬件, 智能家居, 智能家居集成, 权限提升, 物联网, 蓝桥, 蓝牙低功耗, 软硬件黑客, 逆向工具, 逆向工程