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 完全不用碰。

  1. 改本机 devinfo 的两个解锁标志,解除 Bootloader 锁定;
  2. 把工厂 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。这两个文件都不含任何单台设备的身份数据,我已经打包好:

下载:action5pro-unlock-files.zip

解压后应得到:

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

哈希或大小不同就停止。

安装 EDL 工具

以下命令适用于 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

  1. 相机关机并取下电池;
  2. 按住 9008 工程线按钮插入相机;
  3. 主机枚举出 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

第一步:只读 GPT

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"

单独备份 userdata

先从你自己的 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"

把整个备份目录复制到外置盘,并在外置盘再次运行校验。不要只保存教程作者的备份。

第三步:生成本机专属解锁 devinfo

先检查本机原始文件的结构,再生成副本:

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

如果还有第三行差异,停止。

第四步:写入 devinfo

写前最后回读

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 没破坏正常启动。

第五步:重新进入 9008,刷工厂 ABL

再次通过工程线进入 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 -

写入活动槽 ABL 并回读

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"

若没有自动开机,按电源键正常启动。

第六步:验证 root ADB

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

第七步:验证 Fastboot 和 Bootloader 状态

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

回退:恢复原始 ABL 和 devinfo

前提:仍能通过工程线进入 9008。恢复前重新读取 GPT,重新定义变量,并确认布局未变。

如果两项都要撤销,先恢复原厂 ABL,再恢复原始 devinfo;全部回读成功后再重启。

恢复原厂 ABL A

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"

恢复原始 devinfo

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"

OTA 和日常安全

工厂 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

结语

  搞机有风险,折腾需谨慎,一定要记得备份!