OSMO Action5Pro 解锁BL并开启adbd
不久前,某安上有大佬发现 Action 5 Pro 居然能通过 9008 打开 Bootloader,更意外的是它的 SoC 是骁龙 8 Gen 2(SM8550/QCS8550), 一台运动相机塞了颗旗舰 SoC,一下子就变得好玩了。此后某安和 B 站陆续有人放出解锁、常驻工厂模式打开 adbd 的教程。
这篇就以 macOS 为例,手把手带你解锁并开启adbd
本文流程已经在我手上的实机完整走通,但不排除后续固件打补丁把这条路堵上。不过 Action 5 Pro 已经很久没更新固件、功能上毫无动静了,重心大概都压在 Action 6 上,个人判断疆子短期内下场补漏的概率不大。(这么快就固件停更,可恶!)
我的实机环境:
- 型号:DJI Osmo Action 5 Pro(AC204)
- SoC:QCS8550/SM8550(KalamaP)
- 固件:
10.00.16.13
9008 线就是某宝几块钱的高通二代工程线,带按钮、能进 EDL 的那种即可。
经过与 Codex 的一系列口喷,最终发现拿到开机自启的 root ADB,本质上只需要动两个分区,userdata 完全不用碰。
- 改本机
devinfo的两个解锁标志,解除 Bootloader 锁定; - 把工厂 ABL 写进当前活动槽,让系统以 engineering 模式启动,从而暴露 USB ADB。
1
2
3
9008
├─ devinfo 两字节补丁 → Bootloader unlocked
└─ 工厂 ABL → engineering 模式 → root ADB + Fastboot 入口
实机走完全程后的验收标准:fastboot getvar unlocked 返回 yes,内核启动参数里能看到 mp_state=engineering androidboot.secure_debug=1,adbd.service 开机自启,adb shell 直接是 uid=0(root)。
顺带说一句:最早某安有大佬分享了自己的 userdata.img,一度被当成“开 ADB 必刷”的东西。实际分析下来根本不用刷——从别的设备回读的 userdata 会带着人家的 UUID、Wi-Fi、蓝牙密钥、相机配置和校准数据,跨设备写进去只会覆盖你自己的信息,弊大于利。
解包固件会发现,Action 5 Pro 其实自带了完整的 ADB 组件:
1
2
3
4
/sbin/adbd
/sbin/launch_adbd
/lib/systemd/system/adbd.service
/sbin/usb/compositions/*
而且 adbd.service 本身就处于 enabled 状态。所以问题从来不是“缺 adbd”,而是原厂 production 模式下的 ABL 没让系统进入工程调试状态,也没有暴露对应的 USB composition。
这两个分区分工很清楚:devinfo 只保存 Bootloader 的锁状态;工厂 ABL 负责在启动时给内核塞进这两行参数:
1
2
mp_state=engineering
androidboot.secure_debug=1
系统随后就会拉起早已存在的 adbd,给出一个无需改动 system 或 userdata 的 root shell。这也是整个流程能做到“最小改动”的原因。
9008/Firehose 是直接对 UFS 裸写,地址写错、供电中断或者用错 loader,都可能让相机直接变砖,这时候你可能就得考虑「随心换」了(
动手之前请逐条确认:
- 能用工程线反复硬件进入
05c6:9008; - 已经拿到匹配的 Firehose loader 和工厂 ABL;
- 已经读过自己设备的 GPT;
- 原始
devinfo、活动槽 ABL 和关键小分区都备份了; - 接受解锁 Bootloader 可能触发数据清除、失去保修、降低设备安全性;
- 操作全程供电稳定,电池已装回。
最重要的一条:本文出现的 LUN 和扇区都是我这台实机的示例值。你的 GPT 只要有一项对不上,就绝对不能照抄写入命令——地址必须以你自己 printgpt 读出来的为准。
本文目录约定:
1
2
3
loader/RedMagicCommon_8Gen2_devprg
img-unlock/abl-factory.img
backup-action5pro/
本文只需要两个外来二进制:Firehose loader 和工厂 ABL。这两个文件都不含任何单台设备的身份数据,我已经打包好:
解压后应得到:
1
2
loader/RedMagicCommon_8Gen2_devprg
img-unlock/abl-factory.img
下载后先核对哈希再使用,与下面一致才继续:
1
2
b85fb0e651f88f12e3c9b7b56c0f7229449c410f87fc20c6bc35698516383297 loader/RedMagicCommon_8Gen2_devprg
658af73f3319af4c07c733fe6ad184f68c299b678819815bbdacbaaff3b12466 img-unlock/abl-factory.img
devinfo、备份和解锁后的devinfo-minimal-unlocked.bin不在压缩包里,也不该有——它们必须由你自己的设备现场读取和生成,附任何人的都会污染你的设备身份。
红魔 8 系列和 Action 5 Pro 都基于 SM8550/QCS8550(Kalama)平台。上述红魔 8 Gen2 programmer 已在 Action 5 Pro 上实测进入 Firehose,但“同 SoC”本身不保证任意 loader 都安全;只使用已经核验过的文件。
工厂 ABL 必须恰好为 1 MiB:
1
2
3
sha256sum loader/RedMagicCommon_8Gen2_devprg img-unlock/abl-factory.img
wc -c loader/RedMagicCommon_8Gen2_devprg img-unlock/abl-factory.img
预期 abl-factory.img 为:
1
2
1048576 bytes
SHA-256 658af73f3319af4c07c733fe6ad184f68c299b678819815bbdacbaaff3b12466
哈希或大小不同就停止。
以下命令适用于 macOS。Linux 安装对应发行版的 libusb、Python 和 Git 即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
brew install libusb git coreutils
export PATH="$(brew --prefix coreutils)/libexec/gnubin:$PATH"
python3 -m venv .edl-venv
git clone https://github.com/bkerler/edl.git edl-tool
git -C edl-tool checkout 2f8e89a848afaaef68997fcbcb5b178d958d497b
.edl-venv/bin/pip install ./edl-tool
定义本次终端会话使用的工具路径:
1
2
3
4
5
EDL_PY=.edl-venv/bin/python
EDL_SCRIPT=edl-tool/edl.py
LOADER=loader/RedMagicCommon_8Gen2_devprg
FACTORY_ABL=img-unlock/abl-factory.img
BACKUP=backup-action5pro
关闭终端后变量会消失;重新操作前需要再次执行这一段。
- 相机关机并取下电池;
- 按住 9008 工程线按钮插入相机;
- 主机枚举出 Qualcomm 9008 后装回电池,避免刷写途中供电中断。
macOS 查看 USB:
1
2
ioreg -p IOUSB -l -w 0 | grep -i QUSB_BULK
应看到 Qualcomm QUSB_BULK 节点。若想确认 VID:PID,注意 ioreg 里 idVendor/idProduct 是十进制,05c6:9008 对应 1478/36872:
1
ioreg -p IOUSB -l -w 0 | grep -iE "idVendor|idProduct"
9008 与 ADB/Fastboot 是不同模式,此时下面两项应为空:
1
2
adb devices -l
fastboot devices
1
2
3
4
"$EDL_PY" "$EDL_SCRIPT" printgpt \
--memory=ufs \
--loader="$LOADER"
核对输出中的:
MemoryName=UFS;- 逻辑块大小;
- LUN 数量;
devinfo所在 LUN、起始扇区和长度;abl_a、abl_b的位置;- 哪个槽显示
Active True。
本文实机布局为:
本文后续命令只适用于完全相同的布局且活动槽为 A。如果你的活动槽是 B,停止;不要自行把文中的 A/B 和扇区机械互换。
确认一致后定义地址:
1
2
3
4
5
6
LUN=4
DEVINFO_START=239752
DEVINFO_SECTORS=1
ABL_START=66566
ABL_SECTORS=256
1
2
3
4
5
6
7
8
mkdir -p "$BACKUP"
"$EDL_PY" "$EDL_SCRIPT" rl "$BACKUP" \
--memory=ufs \
--loader="$LOADER" \
--skip=system_a,system_b,emulated,userdata,blackbox,blackbox_upgrade,rawdump
确认目录中至少存在:
1
2
3
4
test -f "$BACKUP/lun4/devinfo.bin"
test -f "$BACKUP/lun4/abl_a.bin"
test -f "$BACKUP/lun4/abl_b.bin"
先从你自己的 GPT 记录 userdata 的 LUN、起始扇区和扇区数。下方仍是本文实机示例:
1
2
3
4
5
6
7
8
9
10
11
12
USERDATA_LUN=0
USERDATA_START=15214715
USERDATA_SECTORS=25472
mkdir -p "$BACKUP/lun0"
"$EDL_PY" "$EDL_SCRIPT" rs "$USERDATA_START" "$USERDATA_SECTORS" \
"$BACKUP/lun0/userdata.bin" \
--memory=ufs --lun="$USERDATA_LUN" \
--loader="$LOADER"
1
2
3
4
5
sha256sum "$BACKUP"/lun*/* > "$BACKUP/SHA256SUMS"
sha256sum -c "$BACKUP/SHA256SUMS"
把整个备份目录复制到外置盘,并在外置盘再次运行校验。不要只保存教程作者的备份。
先检查本机原始文件的结构,再生成副本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
python3 - "$BACKUP/lun4/devinfo.bin" img-unlock/devinfo-minimal-unlocked.bin <<'PY'
from pathlib import Path
import sys
src, dst = map(Path, sys.argv[1:])
original = src.read_bytes()
assert len(original) == 4096, f"unexpected devinfo size: {len(original)}"
assert original.startswith(b"ANDROID-BOOT!"), "unexpected devinfo magic"
assert original[0x10] == 0, f"unexpected is_unlocked: {original[0x10]}"
assert original[0x18] == 0, f"unexpected is_unlock_critical: {original[0x18]}"
patched = bytearray(original)
patched[0x10] = 1
patched[0x18] = 1
dst.write_bytes(patched)
PY
严格验证只有两个字节变化:
1
2
3
4
5
6
7
8
test "$(wc -c < img-unlock/devinfo-minimal-unlocked.bin)" -eq 4096
cmp -l "$BACKUP/lun4/devinfo.bin" img-unlock/devinfo-minimal-unlocked.bin
sha256sum "$BACKUP/lun4/devinfo.bin" img-unlock/devinfo-minimal-unlocked.bin
预期差异:
1
2
17 0 1
25 0 1
如果还有第三行差异,停止。
1
2
3
4
5
6
7
8
9
10
11
PRE_DEVINFO=$(mktemp /tmp/action5-devinfo-pre.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$DEVINFO_START" "$DEVINFO_SECTORS" \
"$PRE_DEVINFO" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$PRE_DEVINFO")" -eq 4096
cmp "$PRE_DEVINFO" "$BACKUP/lun4/devinfo.bin"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
"$EDL_PY" "$EDL_SCRIPT" ws "$DEVINFO_START" \
img-unlock/devinfo-minimal-unlocked.bin \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
POST_DEVINFO=$(mktemp /tmp/action5-devinfo-post.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$DEVINFO_START" "$DEVINFO_SECTORS" \
"$POST_DEVINFO" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$POST_DEVINFO")" -eq 4096
sha256sum "$POST_DEVINFO" img-unlock/devinfo-minimal-unlocked.bin
cmp "$POST_DEVINFO" img-unlock/devinfo-minimal-unlocked.bin
后文所有写入后的回读校验都是这个套路:以
cmp的退出码为准——无输出且返回 0 才算与目标镜像逐字节一致,一旦有差异cmp会直接打印首个不同字节的位置。旁边的sha256sum行只是把哈希打出来供你肉眼留档,不需要手动逐位比对。
安全起见,先只验证 devinfo,不同时写 ABL:
1
2
"$EDL_PY" "$EDL_SCRIPT" reset --resetmode=reset --loader="$LOADER"
设备断开时工具可能报告 USB I/O error;如果 9008 已从 USB 消失,这通常只是复位导致的断连。相机没有自动启动时按电源键。
本文实测此时相机可正常进入系统,但仍没有 ADB。这一步的价值是确认两字节 devinfo 没破坏正常启动。
再次通过工程线进入 05c6:9008,重新执行变量定义,并重新读取 LUN4 GPT。确认仍为相同布局和活动槽 A 后继续。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
PRE_ABL=$(mktemp /tmp/action5-abl-pre.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$ABL_START" "$ABL_SECTORS" \
"$PRE_ABL" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$PRE_ABL")" -eq 1048576
cmp "$PRE_ABL" "$BACKUP/lun4/abl_a.bin"
test "$(wc -c < "$FACTORY_ABL")" -eq 1048576
printf '%s %s\n' \
658af73f3319af4c07c733fe6ad184f68c299b678819815bbdacbaaff3b12466 \
"$FACTORY_ABL" | sha256sum -c -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"$EDL_PY" "$EDL_SCRIPT" ws "$ABL_START" "$FACTORY_ABL" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
POST_ABL=$(mktemp /tmp/action5-abl-post.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$ABL_START" "$ABL_SECTORS" \
"$POST_ABL" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$POST_ABL")" -eq 1048576
sha256sum "$POST_ABL" "$FACTORY_ABL"
cmp "$POST_ABL" "$FACTORY_ABL"
只有全部成功才重启:
1
"$EDL_PY" "$EDL_SCRIPT" reset --resetmode=reset --loader="$LOADER"
若没有自动开机,按电源键正常启动。
1
2
3
4
5
6
7
8
adb kill-server
adb start-server
adb wait-for-device
adb devices -l
adb shell id
正确结果应包含:
1
2
<serial> device product:qcs8550_ac204
uid=0(root)
如果状态为 offline,重新插拔普通 USB 线后重试;如果没有设备,不要刷 userdata,先检查工厂 ABL 是否真的写入活动槽且回读哈希一致。
确认工程启动参数和服务状态:
1
2
3
4
5
6
7
adb shell 'cat /proc/cmdline'
adb shell 'systemctl is-enabled adbd.service'
adb shell 'systemctl is-active adbd.service'
adb shell 'systemctl status adbd.service --no-pager'
预期关键字段:
1
2
3
4
mp_state=engineering
androidboot.secure_debug=1
enabled
active
进入交互式 root shell:
1
adb shell
1
2
3
4
5
6
7
8
adb reboot bootloader
fastboot devices
fastboot getvar unlocked
fastboot getvar current-slot
fastboot getvar product
预期:
1
2
3
4
<serial> fastboot
unlocked: yes
current-slot: a
product: kalama
返回系统并确认 ADB 自动恢复:
1
2
3
fastboot reboot
adb wait-for-device
adb shell id
以后正常开机、连接普通 USB 线后直接使用:
1
2
adb devices -l
adb shell
前提:仍能通过工程线进入 9008。恢复前重新读取 GPT,重新定义变量,并确认布局未变。
如果两项都要撤销,先恢复原厂 ABL,再恢复原始 devinfo;全部回读成功后再重启。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"$EDL_PY" "$EDL_SCRIPT" ws "$ABL_START" "$BACKUP/lun4/abl_a.bin" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
RESTORED_ABL=$(mktemp /tmp/action5-abl-restored.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$ABL_START" "$ABL_SECTORS" \
"$RESTORED_ABL" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$RESTORED_ABL")" -eq 1048576
sha256sum "$RESTORED_ABL" "$BACKUP/lun4/abl_a.bin"
cmp "$RESTORED_ABL" "$BACKUP/lun4/abl_a.bin"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"$EDL_PY" "$EDL_SCRIPT" ws "$DEVINFO_START" "$BACKUP/lun4/devinfo.bin" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
RESTORED_DEVINFO=$(mktemp /tmp/action5-devinfo-restored.XXXXXX)
"$EDL_PY" "$EDL_SCRIPT" rs "$DEVINFO_START" "$DEVINFO_SECTORS" \
"$RESTORED_DEVINFO" \
--memory=ufs --lun="$LUN" \
--loader="$LOADER"
test "$(wc -c < "$RESTORED_DEVINFO")" -eq 4096
sha256sum "$RESTORED_DEVINFO" "$BACKUP/lun4/devinfo.bin"
cmp "$RESTORED_DEVINFO" "$BACKUP/lun4/devinfo.bin"
两项均验证成功后再复位:
1
"$EDL_PY" "$EDL_SCRIPT" reset --resetmode=reset --loader="$LOADER"
工厂 ABL 让 root ADB 开机暴露。连接不可信主机前应认识到:这比普通量产系统的攻击面更大。
OTA 可能覆盖活动槽 ABL,使系统恢复 production 模式并关闭 ADB。升级后先检查:
1
adb devices -l
不要为了恢复 ADB 直接重复盲刷。重新进入 9008,读取当前 GPT、活动槽和 ABL 哈希后再决定。
出现任一情况就停止写入:
- loader 无法稳定进入 Firehose;
- 型号、固件或 GPT 与本文适用范围不一致;
- 活动槽不是 A;
devinfo不是 4096 B,或没有ANDROID-BOOT!魔数;- 原始解锁字段不是预期的
0; - 工厂 ABL 大小或 SHA-256 不匹配;
- 写前备份没有第二份副本;
- 写后回读的尺寸、哈希或
cmp不通过; - 相机异常发热、供电不稳或 USB 反复掉线;
- 操作要求写 GPT、XBL、校准或设备身份分区。
最小且已验证的路径只有:
1
2
3
4
5
6
读取本机 GPT并备份
→ 本机 devinfo 两字节解锁
→ 当前活动槽 A 写入已核验的工厂 ABL
→ engineering 模式启动
→ root ADB
→ Fastboot unlocked: yes
搞机有风险,折腾需谨慎,一定要记得备份!