b00mhauercode/rgbboomarm-controller

GitHub: b00mhauercode/rgbboomarm-controller

逆向工程实现的MANKA RGB灯带蓝牙离线控制方案,附带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 服务 | 服务 UUID | 特征值 | 属性 | 用途 | |---|---|---|---| | `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 日志 转折点在于直接从 Android 手机捕获蓝牙 HCI snoop 日志,同时 HomeLinking 应用正在主动控制灯带。 **过程:** 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 ⚠️ 未测试 Apple 提供了一个名为 **PacketLogger** 的工具,可以从连接的 iPhone 捕获蓝牙 HCI 日志,类似于 Android 的 HCI snoop 日志。 **要求:** - 安装了 HomeLinking 且已配对 MANKA 灯带的 iPhone - Mac (PacketLogger 仅限 macOS) - 免费 Apple 开发者账号 **第 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. 前往 **文件 → 新建 iOS 蓝牙日志** — 选择你的 iPhone 4. 在 iPhone 上打开 HomeLinking,连接到灯带,并更改一些颜色 5. 在 PacketLogger 中停止捕获 **第 3 步 — 查找滚动码** PacketLogger 使用其自有的 `.pklg` 格式。你可以直接在其界面中查看 ATT Write 数据包 — 查找以 `fb fb fb 0a` 开头的 20 字节载荷。`fb fb fb 0a` 之后的 4 个字节即为你的滚动码。 或者,通过 **文件 → 另存为** 导出,如果可用的话选择 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 连接使用初始配对期间建立的密钥进行加密。被动嗅探器只有在初始配对交换期间在场以捕获会话密钥时,才能解密流量。如果你的设备已经配对,嗅探器将只能看到它无法读取的加密载荷。 如果满足以下条件,这*也许*仍然有效: - 你在运行嗅探器捕获配对握手时取消配对并重新配对设备 - 设备根本不使用 BLE 配对/绑定(一些廉价设备不使用) **设置(如果你想尝试):** 1. 购买 [Nordic nRF52840 Dongle](https://www.nordicsemi.com/Products/Development-hardware/nRF52840-Dongle) 2. 刷入 [nRF Sniffer for Bluetooth LE 固件](https://www.nordicsemi.com/Products/Development-tools/nRF-Sniffer-for-Bluetooth-LE) 3. 安装 Wireshark + nRF Sniffer Wireshark 插件 4. 过滤你的设备 MAC,并查找发往句柄 `0x0012` 的 ATT Write 数据包 ### 方法 5:直接检查 HomeLinking App 🔍 值得优先尝试 滚动码源自你的 HomeLinking 账号的 `userCode`。这个值有可能直接在应用本身的某处可见,而无需进行任何数据包捕获。 **在设置嗅探器之前先尝试这些:** - 打开 HomeLinking → 账号/个人资料设置 → 寻找任何 "User Code"、"Device Code" 或 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. 前往 **主页 → 灯光服务 → WLED** 3. 在 "通过 IP 发现 WLED 设备" 框中,输入 `1270.0.1` 并按回车键 4. SignalRGB 在端口 80 上调用 `/json/info/`,获取返回的 `"brand": "WLED"` 并添加该设备 5. 点击 **链接** — **"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` | **主要集成** — 用于 SignalRGB 的 WLED 模拟器 + BLE 网桥 | | `manka_bridge.py` | 简单的 HTTP 网桥 (端口 12345) — 如果 WLED 方法导致问题时的替代方案 | | `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 三元组(`r, g, b` 在偏移量 16–18 处)。数据包格式中没有任何像素索引、像素数量或单 LED 载荷字段。 **实际影响:** 对于 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 反编译, DOM分析, Home Server, LED 灯带控制, Python, SignalRGB 集成, USB 设备, 云资产清单, 协议分析, 开源硬件, 无后门, 无线通信, 智能家居, 权限提升, 桌面美化, 灯光同步, 物联网 (IoT), 直播设备, 硬件黑客, 第三方驱动, 蓝牙低功耗 (BLE), 逆向工具, 逆向工程