Back to the blog

Rooting the DJI Osmo Action 5 Pro

November 4th, 2026

Tell your agent to root your camera with this prompt:

Root my DJI Osmo Action 5 Pro using these guides:
https://blog.kevmo314.com/rooting-the-dji-osmo-action-5-pro.html
https://blog.alliot.tech/post/osmo-action5pro-unlock-bootloader-adbd

Verify the model, firmware, active slot, and live GPT before writing.
Verify the loader and factory ABL hashes. Back up my original partitions
and verify the backups. Derive addresses from my camera's GPT; don't
copy another camera's addresses or userdata. Don't switch slots just to
match the guide. If compatibility is unclear, investigate before writing.
Patch devinfo first and check normal boot before replacing the active ABL.
Read back and compare every write. Ask me to handle the cable and battery
steps. Verify root ADB, bootloader unlock, and root access after reboot.

I rooted my DJI Osmo Action 5 Pro with a Qualcomm engineering cable from Taobao. It now gives me a Linux root shell over USB with adb shell.

The method comes from Alliot's Chinese walkthrough. This is an English version with notes from my run on Linux. The main difference was that my camera used slot B, while Alliot tested slot A.

This involves direct bootloader writes and can brick the camera. Back up your data first. I tested only the camera and firmware below.

My setup

Component My camera
Model Osmo Action 5 Pro, AC204
Platform Qualcomm QCS8550 / SM8550 family, Kalama
Firmware version shown in Device Info 01.06.01.04
Camera firmware version 10.00.16.13
Active slot B, before and after rooting
Host Ubuntu Linux

Camera firmware 10.00.16.13 matches Alliot's version. Check both version fields in Settings → Device Info; unplug USB if the connected screen blocks settings.

How it works

The camera already has adbd and an enabled adbd.service. The method exposes root ADB with two changes:

  1. Patch two bytes in this camera's devinfo partition, setting the bootloader's unlocked and critical-unlocked flags.
  2. Replace the active slot's ABL, the Android Boot Loader, with a known factory ABL. This lets the camera boot in engineering mode and expose root ADB.

This enables mp_state=engineering and androidboot.secure_debug=1. I left system, userdata, GPT, XBL, calibration data, and the inactive ABL unchanged. Don't flash someone else's userdata: it contains device-specific settings and credentials.

The cable

I bought a second-generation Qualcomm 9008 engineering cable with a USB-C connector and a button. On Taobao, search for 高通二代工程线 9008 Type-C. A regular USB-C cable won't trigger EDL this way.

There is also an eBay listing for a v2.0 EDL cable, though I haven't tested that seller's cable. If you'd like to buy one from me, email me.

1. Get the tools and verify the images

I used bkerler's EDL client at Alliot's pinned commit. On Ubuntu, install Python with venv support, Git, libusb, curl, unzip, ADB, and Fastboot. Run these commands in Bash:

set -euo pipefail
mkdir action5-root
cd action5-root

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

curl -fL https://github.com/AlliotTech/action-control/releases/download/action5pro-unlock-files.zip/action5pro-unlock-files.zip \
  -o action5pro-unlock-files.zip
unzip action5pro-unlock-files.zip

sha256sum loader/RedMagicCommon_8Gen2_devprg img-unlock/abl-factory.img
wc -c img-unlock/abl-factory.img

The expected SHA-256 hashes are:

b85fb0e651f88f12e3c9b7b56c0f7229449c410f87fc20c6bc35698516383297  loader/RedMagicCommon_8Gen2_devprg
658af73f3319af4c07c733fe6ad184f68c299b678819815bbdacbaaff3b12466  img-unlock/abl-factory.img

The factory ABL must be exactly 1048576 bytes, and both hashes must match. The RedMagic programmer is the exact file Alliot tested and I used; don't substitute another loader just because it targets the same SoC.

Set up the paths and a helper:

EDL_PY="$PWD/.edl-venv/bin/python"
EDL_SCRIPT="$PWD/edl-tool/edl.py"
LOADER="$PWD/loader/RedMagicCommon_8Gen2_devprg"
FACTORY_ABL="$PWD/img-unlock/abl-factory.img"
BACKUP="$PWD/backup-original"

edl() {
  "$EDL_PY" "$EDL_SCRIPT" "$@" --loader="$LOADER"
}

Set up USB permissions with udev rules or run the client with the required privileges. I temporarily stopped ModemManager with sudo systemctl stop ModemManager and restarted it afterward. The client's optional Capstone and Keystone warnings didn't prevent flashing.

2. Enter EDL and read the partition table

  1. Power off the camera and unplug USB. Unmount any camera storage on the host before unplugging.
  2. Remove the battery.
  3. Hold the engineering cable's button while plugging it into the camera.
  4. Confirm that lsusb shows 05c6:9008, then release the button.
  5. Reinstall the battery before continuing, leaving the cable attached.

The screen stays blank in EDL, and ADB and Fastboot won't see the camera. Check for 05c6:9008, then read GPT:

lsusb -d 05c6:9008
edl printgpt --memory=ufs | tee gpt-before.log

The tool loads the programmer into RAM and reads the partition tables. My camera reported UFS, six LUNs, and 4096-byte blocks. These were its addresses, in decimal:

Partition LUN Start sector Sectors Active
devinfo 4 239752 1 Shared
abl_a 4 66566 256 No
abl_b 4 190279 256 Yes
userdata 0 15214715 25472 Shared

Read your own GPT before using any addresses. Check the model, block size, partition sizes, and active slot. Stop if they differ from what you expect.

The slot B issue

Alliot says to stop if slot B is active, so I initially stopped. Before changing anything, I needed to check whether the factory ABL could boot B. Switching to A could have booted older firmware.

I backed up both ABLs and the active system image, decompressed the factory and stock B ABLs, and compared their ARM64 code with Capstone. The checked slot-detection, selection, and attribute routines matched after resolving moved string addresses. Both images contained _a and _b suffixes and selected a slot using GPT active and priority bits.

That supported trying B on the matching camera firmware. I changed devinfo first and checked that the camera still booted normally, then replaced abl_b. It worked without switching slots. This is an additional result from my camera, not a guarantee for other firmware.

3. Back up this camera

Back up GPT and the small partitions. This skips the large data partitions:

mkdir -p "$BACKUP"
edl rl "$BACKUP" --memory=ufs \
  --skip=system_a,system_b,emulated,userdata,blackbox,blackbox_upgrade,rawdump

test -f "$BACKUP/lun4/devinfo.bin"
test -f "$BACKUP/lun4/abl_a.bin"
test -f "$BACKUP/lun4/abl_b.bin"

The lun4 paths match my layout. I also read the full userdata partition and active system_b image. This doesn't back up recordings, the SD card, or blackbox data; copy those separately.

Set DEVINFO_LUN, DEVINFO_START, DEVINFO_SECTORS, ABL_LUN, ABL_START, and ABL_SECTORS from your GPT. Set ORIGINAL_DEVINFO and ORIGINAL_ABL to your backup files, using the ABL for the active slot. Derive the userdata variables the same way:

mkdir -p "$BACKUP/lun$USERDATA_LUN"
edl rs "$USERDATA_START" "$USERDATA_SECTORS" \
  "$BACKUP/lun$USERDATA_LUN/userdata.bin" \
  --memory=ufs --lun="$USERDATA_LUN"

(cd "$BACKUP" && sha256sum lun*/*.bin > SHA256SUMS)
(cd "$BACKUP" && sha256sum -c SHA256SUMS)

Check backup sizes against GPT and verify the hashes after copying. I also verified the GPT CRCs. My second backup copy was on the same SSD, so it wouldn't protect against disk failure. Use a separate disk if available, and keep device backups private.

4. Unlock two flags, then test a normal boot

Read devinfo and the active ABL again. Both must match the backups before you write:

edl rs "$DEVINFO_START" "$DEVINFO_SECTORS" devinfo-before.bin \
  --memory=ufs --lun="$DEVINFO_LUN"
cmp devinfo-before.bin "$ORIGINAL_DEVINFO"

edl rs "$ABL_START" "$ABL_SECTORS" abl-before.bin \
  --memory=ufs --lun="$ABL_LUN"
cmp abl-before.bin "$ORIGINAL_ABL"

Patch your own devinfo. The script checks its size, magic, and flags, then changes only offsets 0x10 and 0x18:

python3 - "$ORIGINAL_DEVINFO" devinfo-unlocked.bin <<'PY'
from pathlib import Path
import sys

src, dst = map(Path, sys.argv[1:])
original = src.read_bytes()
assert len(original) == 4096
assert original.startswith(b"ANDROID-BOOT!")
assert original[0x10] == original[0x18] == 0

patched = bytearray(original)
patched[0x10] = patched[0x18] = 1
assert [i for i, (a, b) in enumerate(zip(original, patched)) if a != b] == [0x10, 0x18]
dst.write_bytes(patched)
PY

# First persistent write: one devinfo sector, with only two bytes changed.
edl ws "$DEVINFO_START" devinfo-unlocked.bin --memory=ufs --lun="$DEVINFO_LUN"
edl rs "$DEVINFO_START" "$DEVINFO_SECTORS" devinfo-after.bin \
  --memory=ufs --lun="$DEVINFO_LUN"
cmp devinfo-after.bin devinfo-unlocked.bin

# Only reset after the complete readback matches.
edl reset --resetmode=reset

Reset printed a USB I/O error when the camera disconnected; the 9008 device disappeared as expected. If it stays in EDL or readback fails, stop. I pressed power, unplugged USB, and checked the live view and touch controls. ABL was still original and ADB wasn't available yet.

5. Replace only the active ABL

Enter EDL again and reinstall the battery. Re-read GPT and check that the layout and active slot are unchanged. Devinfo must match the patched image; ABL must match the original. Verify the factory ABL's hash and size again before writing:

edl printgpt --memory=ufs | tee gpt-second-stage.log
edl rs "$DEVINFO_START" "$DEVINFO_SECTORS" devinfo-second-stage.bin \
  --memory=ufs --lun="$DEVINFO_LUN"
cmp devinfo-second-stage.bin devinfo-unlocked.bin

edl rs "$ABL_START" "$ABL_SECTORS" abl-second-stage.bin \
  --memory=ufs --lun="$ABL_LUN"
cmp abl-second-stage.bin "$ORIGINAL_ABL"

edl ws "$ABL_START" "$FACTORY_ABL" --memory=ufs --lun="$ABL_LUN"
edl rs "$ABL_START" "$ABL_SECTORS" abl-after.bin \
  --memory=ufs --lun="$ABL_LUN"
cmp abl-after.bin "$FACTORY_ABL"
sha256sum abl-after.bin "$FACTORY_ABL"

# Again, only after a byte-for-byte match:
edl reset --resetmode=reset

I wrote only abl_b. The full 1 MiB readback matched the factory image before I reset.

6. Verify root and persistence

Power on and connect USB without holding the cable button. The connected screen is normal. Check ADB from the host:

adb devices -l
adb shell id
adb shell cat /proc/cmdline
adb shell systemctl is-enabled adbd.service
adb shell systemctl is-active adbd.service

My results included:

uid=0(root) gid=0(root) ...
mp_state=engineering
androidboot.secure_debug=1
systemd.setenv="SLOT_SUFFIX=_b"
enabled
active

One early ADB command failed while USB was re-enumerating; the next attempt worked. Give it time to settle. If ADB remains unavailable, check USB permissions and boot state before considering another flash.

Check Fastboot, then reboot:

adb reboot bootloader
fastboot devices
fastboot getvar unlocked
fastboot getvar current-slot
fastboot getvar product

# My output:
# unlocked: yes
# current-slot: b
# product: kalama

fastboot reboot
adb wait-for-device
adb shell id

Root ADB returned after reboot. Storage mounted, and the live view and touch controls worked. A FactoryMode overlay appeared, which I left in place. I haven't fully tested recording/playback or long-term stability.

Recovery and everyday use

To undo this, enter EDL and verify GPT again. Restore your original active ABL, read it back and compare, then do the same for devinfo. Reset only after both match:

edl ws "$ABL_START" "$ORIGINAL_ABL" --memory=ufs --lun="$ABL_LUN"
edl rs "$ABL_START" "$ABL_SECTORS" abl-restored.bin \
  --memory=ufs --lun="$ABL_LUN"
cmp abl-restored.bin "$ORIGINAL_ABL"

edl ws "$DEVINFO_START" "$ORIGINAL_DEVINFO" --memory=ufs --lun="$DEVINFO_LUN"
edl rs "$DEVINFO_START" "$DEVINFO_SECTORS" devinfo-restored.bin \
  --memory=ufs --lun="$DEVINFO_LUN"
cmp devinfo-restored.bin "$ORIGINAL_DEVINFO"
edl reset --resetmode=reset

I kept the original images but haven't tested rollback. Keep the loader and engineering cable too; recovery depends on working EDL access.

For daily use, connect USB and run adb shell. Root ADB starts automatically, so use trusted hosts. An OTA may replace ABL and remove root access; check the new firmware and active slot before flashing again.

References: Alliot's original post (CC BY-NC-SA 4.0), the pinned EDL client, and Action Control, Alliot's web console for rooted cameras. I haven't installed the console.