open_oura 是一个纯 Rust 实现的 Oura Ring 开源工具集,本项目通过逆向官方安卓 App,完整还原了 Oura BLE 协议的常量定义、App 级认证操作(nonce 挑战/加密应答),并给出在 Root 手机上提取 Realm 数据库密钥的全流程方法。跟随这篇指南,你将掌握从反编译 APK 到成功提取 16 字节 auth_key 的每一步。
为什么要逆向 Oura 安卓App?
Oura 戒指的电池、历史事件等核心数据都受App 级认证保护:连接后必须通过一次密钥挑战才能读取数据。官方安卓 App 是唯一"知道"这把 16 字节密钥的地方——它存储在本地的 Realm 数据库(DbRingConfiguration.auth_key)中。
逆向的目标非常明确:
- 找出 App 里的BLE GATT 常量(服务/特征 UUID)
- 还原认证操作的请求/响应字节格式
- 弄清密钥如何生成与加密
- 在 Root 手机上提取 Realm 中的密钥,供 open_oura 的 Rust 客户端使用
全部成果沉淀在 docs/android-app-reversing.md,配合 docs/horizon-ring3-protocol-cheatsheet.md 可对照实际抓包验证。
逆向环境准备:反编译工具链
项目使用以下工具组合完成 APK 反编译(详见 docs/android-app-reversing.md 的 Tools 一节):
| 工具 | 用途 |
|---|---|
apkeep | 从 APKPure 下载com.ouraring.oura的 XAPK 包(版本 7.15.0) |
apktool | 解包资源与 manifest |
jadx | 反编译 dex 为可读 Java 源码 |
如果要从零开始,先克隆仓库:
git clone https://gitcode.com/gh_mirrors/op/open_oura反编译输出的目录(reverse/、captures/)都被 gitignore,只在本机保留——这是逆向项目的标准安全习惯。
从App中提取到的Oura BLE常量
jadx 反编译后,com/ouraring/ourakit/internal/Constants类直接暴露了全部 GATT 定义。这些常量是 Ring 3/4/5 三代通用,也是 open_oura 客户端能跨代工作的基础:
| 常量 | UUID / 值 | 说明 |
|---|---|---|
| 戒指服务 | 98ED0001-A541-11E4-B6A0-0002A5D5C51B | GATT Service |
| 读/通知特征 | 98ED0003-A541-11E4-B6A0-0002A5D5C51B | 响应与通知通道 |
| 写特征 | 98ED0002-A541-11E4-B6A0-0002A5D5C51B | 协议请求通道 |
| 充电器服务 | 8BC5888F-C577-4F5D-857F-377354093F13 | 充电盒专用 |
| 厂商 ID | 0x02b2 | 广播厂商数据前缀 |
📌 协议帧格式为tag | length | payload,扩展操作统一使用外层 tag0x2f,第一个 payload 字节作为扩展操作码。
认证操作全解析:三步完成BLE握手
App 在com/ouraring/ourakit/operations下有明确的官方操作类,逆向后得到三步认证流程:
第一步:GetAuthNonce(请求随机数)
- 请求字节:
2f 01 2b(tag0x2f,扩展 tag0x2b) - 响应:外层 tag
0x2f+ 扩展 tag0x2c,nonce 从响应索引[3, 18)拷贝,固定 15 字节
第二步:Authenticate(加密应答)
- 请求字节:
2f 11 2d+ 16 字节加密后的 nonce - 响应:扩展 tag
0x2e,认证结果在响应第 3 字节
第三步:SetAuthKey(首次配对写入密钥)
- 请求字节:
24 10+ 16 字节密钥 - 响应 tag
0x25,第 2 字节为结果:0x00成功;0x05表示出厂测试缺失(可安全忽略)
⚠️ 注意区分:SetAuthKey只在戒指出厂重置后首次配对时使用;已配对的戒指不能重新写入密钥,必须复用原有的 auth_key——这正是需要提取密钥的原因。
密钥生成与AES加密原理
逆向还还原了两个关键算法细节:
密钥生成(RingOperations.i()[B):生成随机UUID→ 写入 16 字节小端ByteBuffer(先写 most-significant 64 位,再写 least-significant 64 位)。非生产构建则返回静态测试密钥。
nonce 加密:戒指与充电器认证路径均使用AES/ECB/PKCS5Padding:
- 输入:15 字节 nonce
- 处理:PKCS7 补齐到 16 字节,单次 AES-128 加密
- 输出:16 字节密文 → 这就是"15 字节 nonce 为什么变成 16 字节"的答案
open_oura 在 crates/oura-protocol/src/auth.rs 用 Rust 精确复刻了这套加密,并带有已验证的已知答案测试向量(known-answer test),保证与 App 行为逐字节一致。
Root手机提取Realm密钥:完整流程
密钥存储位置(Realm 模型字段):
- 戒指:
DbRingConfiguration.authKey(序列化名auth_key) - 充电器:
DbAccessoryConfiguration.authKey
方法一:Python 辅助脚本(推荐起步)
项目提供 tools/android_oura_key_extract.py,在 Root 手机(Magisk)上执行:
adb devices -l adb shell 'pm list users' adb shell 'pm list packages | grep oura' python3 tools/android_oura_key_extract.py --serial 50380B2617647259脚本的工作方式:
- 遍历所有 Android 用户(Oura 可能装在
/data/user/10等次要用户下,而非/data/data) - 拉取 App 日志与
assa-store.realm到captures/android/(0600权限,绝不打印密钥) - 以
auth_key、serial_number、DbRingConfiguration为锚点,在 Realm 文件附近窗口内扫描高熵 16 字节候选(Shannon 熵 ≥ 3.4) - 唯一候选 → 直接写出密钥文件;多个候选 → 写入候选清单待 BLE 验证
方法二:Realm JS 精确读取(最可靠)
tools/extract_oura_realm_key.js 用realm@20包以只读模式打开 Realm 文件,按serial_number过滤DbRingConfiguration行,取in_active_use === true的活跃行,校验auth_key长度为 16 字节后写出:
npm install realm@20 node tools/extract_oura_realm_key.js assa-store.realm <序列号> oura-android-auth.key🔑 实测案例(2026-07-02,Pixel 8):工厂重置戒指 → 官方安卓 App 重新配对 → 从assa-store.realm成功读出活跃 Ring 5 行(serial_number、mac_address、android_ble_identifier、16 字节auth_key),密钥写入oura-android-auth.key。
验证提取的密钥
把密钥交给 open_oura 的 Rust CLI 验证:
cargo run -p oura-cli -- --key-file captures/android/oura-android-auth.key infoFrida与APK补丁:走过的弯路
项目也尝试过更"激进"的路子,结论记录在案(见 docs/android-app-reversing.md):
- Frida 运行时补丁:tools/frida/oura_bond_bypass.js 可 hook
RxBleRingBondConnector.isBonded/connect绕过绑定检查,但 Frida 17.15.3 在该 App 上触发 nativeSIGBUS崩溃,目前不可行 - 静态 APK 替换:apktool 重打包或只替换
classes5/6.dex均因资源 ID/初始化失败崩溃 - 最终结论:已配对戒指必须复用 App 生成的密钥(新生成密钥无法匹配戒指侧已安装的密钥);从 Realm 直接提取才是最优路线,而非 APK 替换
Ring 5 实机验证:与逆向结果完全吻合
2026-06-21 对本地 Ring 5 的实测(docs/ring-5-observations.md):
- 请求
2f012b→ 响应2f102c 490a55be3b8169e3f24aa279f1e55a - 解析出 15 字节 nonce
490a55be3b8169e3f24aa279f1e55a
与反编译的 Android App 逻辑、Ringverse 的 Ring 4 笔记三方一致——证明逆向出的协议细节可直接用于实机通信。
相关文件速查
| 资料 | 路径 |
|---|---|
| 逆向总文档 | docs/android-app-reversing.md |
| 协议命令速查表 | docs/horizon-ring3-protocol-cheatsheet.md |
| Ring 5 实测记录 | docs/ring-5-observations.md |
| AES 认证实现 | crates/oura-protocol/src/auth.rs |
| Root 提取脚本 | tools/android_oura_key_extract.py |
| Realm 密钥读取 | tools/extract_oura_realm_key.js |
| Frida 绑定绕过 | tools/frida/oura_bond_bypass.js |
| Rust 客户端用法 | crates/README.md |
总结:逆向得到的完整链路
反编译APK → BLE常量 + 三步认证协议 → 密钥存储于Realm ↓ Root手机拉取 assa-store.realm → 提取16字节 auth_key ↓ open_oura Rust CLI (--key-file) → 本地免云端读取戒指数据整个流程的核心收获:Oura 的 App 级认证只是"15 字节 nonce 挑战 + AES-128/ECB 应答",复杂度全在密钥管理上——密钥由官方 App 本地生成并锁在 Realm 数据库里。掌握这条链路后,你就可以用 open_oura 在本地完整同步、存储并分析戒指数据,无需 Oura 云端。
💡安全提示:密钥文件权限请保持0600,切勿提交到任何代码仓库;破坏性命令(如factory-reset)在 open_oura 中均需显式--yes确认。
【免费下载链接】open_oura
A Rust toolkit for the Oura Ring (Gen 3/4/5): reverse-engineered BLE protocol, event decoders, and reimplemented>项目地址:https://gitcode.com/gh_mirrors/op/open_oura
相关推荐
open_oura配对认证详解:如何安全获取Oura戒指16字节Auth Key
open_oura配对认证详解:如何安全获取Oura戒指16字节Auth Key open_oura 是一个用 Rust 编写的 Oura Ring 反向工程工
open_oura逆向实战:如何用Ghidra完整拆解Oura原生解码器libringeventparser.so
open_oura逆向实战:如何用Ghidra完整拆解Oura原生解码器libringeventparser.so 开源项目 open_oura 是一个面向 O
提升Zotero效率的终极Markdown导出插件 - Mdnotes完全指南
提升Zotero效率的终极Markdown导出插件 Mdnotes完全指南 📚 作为一名学术研究者或笔记爱好者,你一定深知Zotero文献管理的重要性。今天我
后端开发工具运维