不久前,某安上有大佬发现 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。这两个文件都不含任何单台设备的身份数据,我已经打包好:
下载: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
- 相机关机并取下电池;
- 按住 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
|
第一步:只读 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。
本文实机布局为:
| 分区 | LUN | 起始扇区 | 扇区数 | 状态 |
|---|
devinfo | 4 | 239752 | 1 | 公共分区 |
abl_a | 4 | 66566 | 256 | Active True |
abl_b | 4 | 190279 | 256 | Active False |
本文后续命令只适用于完全相同的布局且活动槽为 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
|
预期差异:
如果还有第三行差异,停止。
第四步:写入 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:
第七步:验证 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。升级后先检查:
不要为了恢复 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
|
结语
搞机有风险,折腾需谨慎,一定要记得备份!