Maku-hub/TrikiEmu

GitHub: Maku-hub/TrikiEmu

TrikiEmu是一个基于ESP32的Żabka Triki智能瓶盖BLE软件模拟器,通过还原设备身份和注入PC端运动数据实现与官方应用的互操作。

Stars: 1 | Forks: 0

# TrikiEmu [![CI](https://github.com/Maku-hub/TrikiEmu/actions/workflows/ci.yml/badge.svg)](https://github.com/Maku-hub/TrikiEmu/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Platforma](https://img.shields.io/badge/platforma-ESP32%20%7C%20M5StickC%20Plus2-blue)](firmware/platformio.ini) [![Release](https://img.shields.io/github/v/release/Maku-hub/TrikiEmu)](https://github.com/Maku-hub/TrikiEmu/releases) **Żabka Triki 瓶盖的软件模拟器** — 扮演 BLE *peripheral*(GATT 服务器)的 ESP32 设备,用于模拟瓶盖:像 Triki 一样进行广播,提供 Nordic UART 服务并发射 IMU 数据流。**Żappka 应用会像连接真实瓶盖一样连接它**,而你可以通过电脑(键盘/文件/模式)输入运动数据。 这是 **[TrikiScope](https://github.com/Maku-hub/TrikiScope)**(瓶盖读取器/检查器)的姊妹项目。[TrikiScope](https://github.com/Maku-hub/TrikiScope) 充当 BLE *central*(客户端);而 TrikiEmu 扮演相反的角色 —— *peripheral*。所有关于协议的知识均来自于对 [TrikiScope](https://github.com/Maku-hub/TrikiScope) 的逆向工程 (RE) 以及自有的 Android HCI snoop 抓包。 ![演示操作](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/c4a75aab1a145851.gif) ## 状态 - **测试 [TrikiScope](https://github.com/Maku-hub/TrikiScope)**(central)— 已通过:广播 + GATT 一致,数据流约 98 Hz。 - **Żappka 应用** — **已连接并对运动做出反应**(互操作性已在 M5StickC Plus2 上确认)。 - 通过 USB 串口从 PC 控制运动:键盘(实时)、CSV 文件、模式、端口自动检测。 - **保存游戏成绩需要真实的瓶盖** — 受加密设备身份验证限制(参见 [成绩保存 — RE 发现](#zapis-wyniku-w-grach--uwierzytelnienie-kryptograficzne-odkrycie-re))。 完整的协议规范(广播、GATT、命令、IMU 数据帧)及实现陷阱:[firmware/README.md](firmware/README.md)。 ## 工作原理 ``` PC (źródło ruchu: klawiatura / plik / wzorzec) │ USB-serial: M,gx,gy,gz,ax,ay,az | R | BLE,1 | BLE,0 ▼ ESP32 (TrikiEmu) ── BLE: reklama jak Triki + NUS + strumień IMU ──► telefon (Żappka) ``` ESP32 还原了瓶盖的身份和配置;PC 实时提供运动数据,固件将其打包为 Triki 帧(14 B)并通过 BLE 发送。在 M5StickC Plus2 上,你也可以使用内置的 IMU 作为运动数据源。 ## 准备工作 **硬件:** - 任何 **ESP32** 开发板(已在 **M5StickC Plus2** / ESP32-PICO-V3-02 上测试) - USB 线缆 - 带有蓝牙的电脑 - 安装了 Żappka 的手机 **软件:** - **PlatformIO Core**(CLI):`python -m pip install --user platformio`(或 VSCode 中的 PlatformIO 插件) - **Python 3** + `pyserial` — 用于 PC 端控制工具(`pip install pyserial`) - **[TrikiScope](https://github.com/Maku-hub/TrikiScope)** ## 构建与烧录 在 `firmware/` 目录下(完整说明:[firmware/README.md](firmware/README.md))。 ## 关键点:BLE 身份(为了让 Żappka 接受) Żappka 通过 **MAC 地址**识别已记住的瓶盖,模拟器必须**还原它** —— 使用其他 MAC 地址的模拟器会收到“未找到 triki”。**MAC 地址是特定于各个设备的**,因此对于你自己的瓶盖,你必须输入它的地址 —— [firmware/README.md](firmware/README.md) 描述了如何读取它以及在哪里设置(固件中的 `BLE_ADDR_LE`)。 模拟器还原的完整身份契约(广播、GATT profile、manufacturer data、service UUID、固件/电池、NUS)都集中在一个地方:**[firmware/README.md](firmware/README.md)**。 ## PC 控制(`tools/` 中的工具) 所有工具在打开端口时均**不会重置**开发板,并且能**自动检测 COM**(通过 ESP32 桥接器的 VID:PID);只有在连接多个开发板时才需要提供 `--port`。 | 工具 | 用途 | |---|---| | `pc_control.py` | 单次命令:`ble-on` / `ble-off` / `rest` / `raw "..."` | | `pc_keyboard.py` | **通过键盘实时控制**:旋转/倾斜(游戏中的左/右)+ `f`=拍打/“猛拉”(FlappyBird 等游戏) | | `pc_motion_file.py` | **从 CSV 文件回放运动序列** | | `pc_motion_feed.py` | 模式生成器(`--pattern spin\|tilt\|rest`) | | `pc_replay_capture.py` | **回放真实的录制会话**(来自 btsnoop,复用解析器) | **键盘**(`python tools/pc_keyboard.py`)— 一次调用,两种模式(`t` 切换): - **游戏模式**(默认):左/右(`q`=左,`e`=右,`a/d`=X,`w/s`=Y,`[`/`]`=强度,空格=重置)+ FlappyBird(`f`=拍打,`,`/`.`=拍打强度)。 - **投掷模式**(抛掷+旋转):游戏按键禁用;输入**两个数字 00–20** → 自动进行投掷。形状基于真实的 capture(带有饱和陀螺仪的旋转 + 振荡的加速度矢量);自动测试根据该振荡计算旋转(已确认:回放录制的视频可得到准确的计数)。 - 通用:`t`=进入/退出投掷模式,`b/n`=BLE 开/关,`x`=退出。图例显示在顶部一次;状态显示在单行刷新中(不滚动控制台)。 **文件**(`python tools/pc_motion_file.py tools/motion_examples/demo.csv`):7 列 CSV `t,gx,gy,gz,ax,ay,az` = 按 `--rate` 插值的 keyframe;6 列 = 原始样本。 串口协议(如果你想编写自己的工具)在 [firmware/README.md](firmware/README.md) 中有说明。 ## 可移植性(任何 ESP32) 核心(BLE/NUS/数据流/串口)可在**任何 ESP32** 上运行;M5StickC Plus2 的功能(屏幕、按钮、IMU、电池、深度睡眠)是可选的。在 **M5StickC Plus2** 上制作并测试。详细信息(PlatformIO 环境,裸 ESP32 与 M5 的区别,`-D HAS_M5` 标志,按钮/屏幕控制):[firmware/README.md](firmware/README.md)。 ## 考虑的方案 本项目以 **方案 A — 软件模拟整个 BLE 通信**(ESP32 模拟整个瓶盖)的形式实现。也曾考虑过以下替代方案: - **B — 瓶盖内部总线上的虚假 IMU**(考虑过,**未构建**)。 想法:断开真实的 ST LSM6DSL 传感器,将 ESP32 作为“虚假传感器”接入(作为 SPI/I²C 从机,以合成的运动进行响应),同时保留**真实的 BLE 协议栈和瓶盖身份**。优点:绕过身份验证/绑定/握手(由真实的瓶盖完成)。缺点:LGA-14 封装需要微焊接,且有损坏瓶盖的风险。为了更简单的纯软件方案 A 而放弃。 选择的平台:**M5StickC Plus2 (ESP32-PICO-V3-02)**;核心可移植到任何 ESP32。 协议契约和实现的详细信息:[firmware/README.md](firmware/README.md)。 ## 游戏成绩保存 — 加密身份验证(RE 发现) 从逆向工程(使用**真实**瓶盖的 HCI snoop 转储,对 `tools/btsnoop_att.py` 的分析)来看,**最佳成绩的保存受到加密设备身份验证**(challenge-response)的限制,该验证交织在**整个回合**中,而不是在最后的单个消息中: ``` faza apka → kapsel (RX 6e400002) kapsel → apka (TX 6e400003) ────────────────────────────────────────────────────────────────────────────── START strumienia 20 10 .. INIT sesji 0a <16B LOSOWY id> 21 .. (ack) + 8a ← liczone z klucza w trakcie (~1/s) 09 20 <32B> 89 / 21 <..> (ramki IMU 22 00 wplecione) STOP 20 00 domknięcie 09 20 <32B> (×~2) 89 / 8a .. ``` - 会话标识符 (`id`) 在**每回合都是随机的** → 瓶盖的响应**无法**从记录中还原。 - 响应 opcode = 请求 opcode `| 0x80` (`0a`→`8a`, `09`→`89`);`21` 是状态/ACK。 - 响应 `8a`/`89` 是**从瓶盖的密钥计算得出的**。应用程序会验证它们以确认是真实的瓶盖,**只有这样才能保存成绩**。 瓶盖的密钥位于其固件中,受 **APPROTECT** 保护(无法提取),而随机的会话 `id` 排除了重放 (replay) 的可能。 首先,这是一种**设备身份验证 / 反作弊**机制,旨在保护奖励/排名系统的完整性 —— 绕过它**超出了本项目的范围**(见下文)。**游戏过程本身基于原始的 IMU 数据流运行**(因此模拟器可以玩游戏),但**保存成绩需要真实的瓶盖**。 ### 这到底能不能被伪造?(分析,供感兴趣者参考) 这是一种经典的**设备认证**(challenge-response:在不泄露秘密的情况下证明你拥有它)。它能否被绕过,取决于**应用程序如何验证响应** —— 而这从数据流中是看不出来的。三种模型及其后果: - **对称加密(共享密钥)** — 应用也知道密钥,自行计算并进行比较。在这种情况下,**密钥在应用程序中** → 可以从 APK 中提取。这是成本最低且最薄弱的模式;此时的攻击目标是应用程序,而不是瓶盖。 - **非对称加密(签名)** — 瓶盖使用**私钥**进行签名,应用使用**公钥**进行验证。应用程序中没有秘密 → 需要从瓶盖获取私钥。这是很强的模式。 - **服务器验证** — 应用将 challenge+response 发送给 Żabka 的服务器;在本地你提取不到任何东西。 攻击面(类别): - **重放**录制的响应 → **无效**,因为会话 `id` 是随机的(nonce)。这一点他们做得很好。 - **应用程序逆向工程** → 仅在对称模型(密钥在应用中)中可行。 - **从硬件提取密钥** → 读取保护(APPROTECT / flash encryption)可能会被已知的硬件攻击类别(fault/voltage glitching、side-channel/DPA、decap + microprobing)绕过 — 成本高,且特定于硬件。 - **协议密码分析** → 如果它是脆弱的/“自制的”加密算法,而不是标准的 AEAD。 **底线:**如果它是非对称加密 + **硬化元件中的唯一密钥** — 那么在没有昂贵的硬件攻击下几乎是不可行的(这正是它的目的:防伪认证,与 mutual-auth + nonce + secure element 中的密钥理念相同)。真实的漏洞通常在于**实现**(应用中的密钥、脆弱的 nonce、可被 glitch 的 MCU、在需要验证的地方缺乏验证)— 而不在于理念本身。 ## 为 RE 进行抓包:蓝牙日志 (adb) 和手机屏幕 (scrcpy) 用于得出上述协议分析的工具。它们需要**USB 调试**(手机上的开发者选项),并在连接数据线时确认“允许”。 **蓝牙通信日志 (HCI snoop)** — 手机与瓶盖之间交换的内容: 1. 手机:开发者选项 → 启用 **“启用蓝牙 HCI 信息收集日志”**,然后**关闭并重新打开蓝牙**(日志记录开始)。 2. 复现场景(例如,玩一局直到保存成绩),然后**关闭蓝牙**(这会结束日志)。在提取日志之前**不要再次打开蓝牙** — 重新打开会截断日志。 3. PC(adb 包含在 scrcpy 包中或 Android SDK Platform-Tools 中):运行 `adb bugreport bugreport.zip`。日志位于 zip 文件内的 `FS/data/log/bt/btsnoop_hci.log` (在其他手机上可能是 `FS/data/misc/bluetooth/logs/btsnoop_hci.log`)— 将其解压到 `captures/` 目录。 4. 分析: - `python tools/btsnoop_att.py captures/` — 按时间顺序排列的**所有 ATT 数据流量**,handle 到 UUID 的映射,时间窗口 `--from/--to`,`--imu` 附加 IMU 数据流。用于研究命令/握手(例如成绩保存)。 - `python tools/parse_btsnoop.py captures/` — 解码 **IMU 数据流**(14 B 数据帧)。 **在 PC 上查看手机屏幕 (scrcpy)** — 在 RE 期间观察游戏时非常有用: 1. 手机:开启 **USB 调试**,连接 USB,确认“允许”。 2. PC:`winget install --id Genymobile.scrcpy`(该包也包含 `adb`)。 3. 运行 `scrcpy` → 在电脑上显示实时手机屏幕的窗口。 ## 范围与界限(重要) 本项目旨在使用自有的进行**逆向工程、互操作性、学习和测试**。它**不**是用于在 Żabka 的忠诚度计划(żappsy、折扣、游戏成绩奖励)中刷奖励的工具 — 这是对他人系统的滥用。 本项目与 Żabka 公司没有任何关联。
标签:ESP32, IMU传感器, UML, 云资产清单, 嵌入式开发, 物联网, 蓝牙低功耗, 逆向工具, 逆向工程