Xiaomi MTK Bootloader Unlock — HyperOS/MIUI14+ RPMB Bypass
Scope: Xiaomi MTK devices with Kamakiri BROM access; compatibility is LK-build-specific and not universal
XDA Thread: xdaforums.com/t/4784527
Author: Jz8root
The Problem
On affected MIUI 14 and HyperOS builds, mtkclient da seccfg unlock can succeed
without errors while the bootloader remains locked after reboot. The current
hardware evidence is tracked in this repository rather than inferred from old
device lists: earth/MT6769 eMMC (#8), fire/MT6769Z eMMC
(issue #10),
MT6877 devices (issue #11,
#13) and agate/MT6893
(issue #14).
These reports do not all share one root cause, which is why the procedure is
gated on both LK analysis and live RPMB state.
Root Cause
After reverse engineering the Little Kernel (LK) bootloader from HyperOS using Ghidra, I found that Xiaomi introduced a dual-layer lock verification:
Starting with HyperOS, Xiaomi stores a magic string in the RPMB (Replay Protected Memory Block). The LK reads it before falling back to seccfg — when present, RPMB overrides seccfg even if seccfg says UNLOCK.
The fix: erase the RPMB magic. This forces the LK to fall back to seccfg, which mtkclient can freely write to UNLOCK.
Why it persists across reboots on confirmed devices: on the Tier 1 devices (MT6781, MT6769Z, MT6833), no boot stage restores the magic once seccfg has been set to UNLOCK, so the erase holds and the device boots to the unlocked state. RPMB dumps taken days or weeks after the original unlock still show the magic region zeroed. See proof/VERIFICATION.md for dump evidence.
Where this does not hold: on the measured MT6877 rubypro and pissarro reports, and on earth (Redmi 12C, MT6769Z eMMC), the RPMB procedure ends with fastboot still locked. Dump-level tests on rubypro and earth show why: the erase reaches flash and survives a reset plus cold BROM re-entry, but the normal boot chain restores the 8 magic bytes. pissarropro has a separate matching failure report without the same dump-level trace. The exact writer (preloader, another loader, ATF/TEE or LK) has not yet been isolated. These devices are listed in COMPATIBILITY.md under Tier 3b (investigation open, no bypass yet).

| Lock State Value |
Meaning |
| 1 |
DEFAULT (RPMB not initialized, fallback to seccfg) |
| 3 |
UNLOCKED |
| 4 |
LOCKED |
The Method
The procedure depends on whether your specific LK binary implements the RPMB magic check.
Don’t assume — scan your LK first. Two devices with the same codename and same SoC can
ship different LK builds with different lock implementations.
Prerequisites
- mtkclient with Kamakiri BROM exploit
- Access to BROM mode (see Entering BROM Mode below for the precise procedure)
- Quality USB DATA cable (not charge-only)
- A USB 2.0 port. Not USB 3.0. See below, this one silently ruins runs.
- MANDATORY: Full backup of NV partitions (commands below)
Use a USB 2.0 port; avoid USB 3.x for writes
At least one earth report showed RPMB and seccfg writes acknowledged through a USB 3.x host
controller without the expected state change. The tool printed Successfully wrote seccfg, the
operation looked correct, and the flags remained unchanged. This is a host/device observation,
not a claim that every USB 3.x controller universally drops MTK writes.
The signature is unmistakable once you know it: every command reports success, and every flag is
exactly as it was before you started.
fastboot oem lks -> lks = 1 unchanged
fastboot getvar unlocked -> unlocked: no unchanged
fastboot getvar secure -> secure: yes unchanged
Observed on earth/MT6769Z in #8, where the reporter had switched to the blue port without
realising. Later tests on another earth device also found a separate early-boot restoration block,
so USB transport failure and Tier 3b restoration must not be conflated. Use a known USB 2.0 path
for writes, and prefer a direct connection over an unpowered hub.
Two traps worth knowing:
- some laptops have a physically black port that is internally wired to the xHCI 3.0
controller anyway, through sloppy board routing. If a second attempt on the same port fails
the same silent way, try a different port, a USB 2.0 hub, or another machine entirely
- USB-C ports are almost always 3.x. Prefer a USB-A 2.0 port with an A-to-C cable
Always verify by re-reading the RPMB, never by trusting the log line:
python mtk.py da rpmb r rpmb_check.bin
grep -boa "Jz8PNRUF" rpmb_check.bin
Linux Pre-flight
On any modern Linux distro that ships with ModemManager and the option USB-serial driver
(Manjaro, Arch, Ubuntu, Fedora, openSUSE, etc.), mtkclient sessions silently fail with
DaHandler - Please disconnect, start mtkclient and reconnect. even though the udev rules
from Setup/Linux/ are installed correctly.
The root cause is in dmesg immediately after the phone enters BROM:
usb 1-1: New USB device found, idVendor=0e8d, idProduct=0003
usb 1-1: GSM modem (1-port) converter now attached to ttyUSB0
The kernel’s option driver (via usb_wwan/usbserial) claims the MediaTek port instantly on
enumeration and blocks libusb’s CLAIM_INTERFACE. ModemManager grabs anything that looks like a
modem. mtkclient’s udev rules grant userspace access but don’t prevent kernel-driver binding.
Run this before each mtkclient BROM session:
sudo systemctl stop ModemManager
sudo modprobe -r option usb_wwan
# verify
lsmod | grep -E '^option|^usb_wwan' || echo "ok — modules unloaded"
systemctl is-active ModemManager # should print: inactive
usbserial is built into the kernel on most distros — you don’t need to (and can’t) unload it.
Only option and usb_wwan matter.
The modules can auto-reload on udev events between sessions. If a later command fails with the
same Please disconnect, start mtkclient and reconnect. symptom, re-run the modprobe -r step.
When you’re done with the unlock, restore the normal modem stack:
sudo systemctl start ModemManager
# option/usb_wwan will re-load automatically on next USB modem connect, or after reboot
Entering BROM Mode
BROM is the boot-ROM USB-download mode that runs before the preloader. It only stays active if
the volume keys are held during its ~100 ms startup window — and that window is before USB power
arrives, not after. If you plug first and press keys after, BROM has already handed off to
preloader → LK, and the keys then trigger fastboot or recovery instead.
The procedure that works reliably:
- Phone fully off. Not transitioning. Not in fastboot. Not displaying a logo. If unsure,
hold Power for 15 s to force-off, then wait 5 s. This is fine before you start, but once
the RPMB is erased the Power button is off limits, see
the rule below.
- USB unplugged.
- Press and hold Vol+ AND Vol− (both, simultaneously). On some devices Vol+ alone or
Vol− alone works; try both keys first.
- While still holding the keys, plug the USB cable in.
- Keep holding for 15+ seconds after the cable is in.
- Release.
The screen must stay completely black throughout. If you see anything — Xiaomi logo,
“FASTBOOT” text, the unlocked-warning screen, a vibration — the timing was wrong. Unplug,
force-off, retry.
If this happens after an RPMB erase, the normal boot chain has run and may have restored the magic.
Re-dump to confirm, redo the erase, and retry the BROM entry using only the volume keys. See
the rule below.
mtkclient should already be running before you plug in. Start mtk.py r ... /
mtk.py da rpmb r ... / mtk.py da seccfg unlock first — it will print
Waiting for PreLoader VCOM, please reconnect mobile/iot device to brom mode and sit there
until USB enumeration happens. That way mtkclient grabs the device in the first ~100 ms after
enumeration, before any kernel driver can race for the interface.
Between every da command, the phone needs a fresh BROM entry. Unplug, redo the key combo,
replug.
🚨 This is the single most important rule in this document, and it is the reason most reported
failures fail.
The early boot chain can rewrite the RPMB magic on start-up. Reaching the Mi logo is enough.
So anything that starts the phone between your erase and your unlock can silently undo the erase,
and nothing tells you. The exact writer is still under investigation; the operational rule does
not depend on which boot stage performs the write.
Re-entering BROM does not start the phone, so it is safe:
| Action |
Safe? |
Why |
| unplug, hold Vol+ and Vol-, replug |
✅ |
BROM only, the normal boot chain never runs |
| Power button to shut down or restart |
❌ |
normal boot starts, magic may be rewritten |
| letting the Mi logo appear at all |
❌ |
the boot chain has already run |
fastboot reboot |
❌ |
same |
So the erase and the unlock are two ordinary BROM sessions, with a normal unplug and key-combo
in between. Nothing exotic is required:
python mtk.py da rpmb e --sector <YOUR_SECTOR> --sectors 4
# unplug, hold Vol+ and Vol-, replug. Do not touch the Power button.
python mtk.py da seccfg unlock
How this was established. LucaCraft89 (#13) ran three dumps on rubypro/MT6877. After the
erase and a full cold BROM re-entry with Kamakiri re-run and the DA re-uploaded, the magic was
gone. He then allowed a normal start-up, re-read, and the magic was back. vivaad (#8)
later isolated the same boundary on earth/eMMC: the sector stayed zero after mtk.py reset and a
cold BROM re-entry, then exactly the 8 magic bytes returned after reaching the Mi logo. This proves
the writer runs during the normal boot chain, but does not by itself identify which stage writes it.
Why the phone sometimes ends up in a strange state: after da rpmb e completes, mtkclient
calls neither close() nor shutdown(). The command returns and leaves the device with the DA
still running and still powered. On some units the Power button then reboots instead of shutting
down, which is how katavnnn (#12) first ran into this on sea/MT6781.
If the phone gets stuck and you need to reset it
Stuck in an intermediate MTK state (e.g. 0e8d:20ff not recognised by mtkclient, or fastboot
wedged after a killed mtkclient run)? Three options, least invasive first. None of them are
needed in the normal flow where you simply unplug and redo the key combo.
- Reset the USB endpoint without unplugging, which does not touch the phone’s power state:
sudo python3 -c "import os, fcntl; fcntl.ioctl(os.open('/dev/bus/usb/BBB/DDD', os.O_WRONLY), 21780, 0)"
(replace BBB/DDD with the bus/device numbers from lsusb).
python mtk.py reset issues a DA shutdown with bootmode=0. It works on some units and
fails on others with DeviceClass - [Errno 2] Entity not found, and note that the
Reset command was sent message is printed unconditionally by mtkclient whether or not the
command actually went through, so it is not a confirmation.
- Disconnect the battery connector from the board, reconnect, then hold Vol+ and Vol- before
inserting the cable. Proven on hardware by katavnnn (#12) and it cannot start the phone
by accident, but it means opening the back cover. Last resort.
Step 0 — Mandatory backups
In BROM, dump everything that matters before any write operation:
# Critical partitions in one BROM session (sizes are mtkclient's uniform read, real content at the start)
python mtk.py r \
nvdata,nvcfg,nvram,persist,protect1,protect2,seccfg,lk_a,lk_b,boot_a,boot_b \
nvdata.bin,nvcfg.bin,nvram.bin,persist.bin,protect1.bin,protect2.bin,seccfg.bin,lk_a.bin,lk_b.bin,boot_a.bin,boot_b.bin
# RPMB (separate BROM session — phone must re-enter BROM between sessions)
python mtk.py da rpmb r rpmb_backup.bin
Without nvdata / nvcfg / nvram / protect* you risk “NV data corrupted” → bricked
IMEI and baseband. Keep these backups for months — they’re your recovery path.
Step 1 — Identify your LK with scan_lk.py ⟵ DECISION POINT
Scan both slots — they can differ. After an OTA update, slot A and slot B can carry different LK builds. Confirmed independently on Redmi 12 (fire, HyperOS 2.0) and POCO M4 Pro 4G (fleur, HyperOS 1.0.2.0): in both reports lk_a = COMPATIBLE_SECCFG_ONLY while the active lk_b = COMPATIBLE_FULL. The device boots on the active slot — use that result.
python3 scan_lk.py lk_a.bin
python3 scan_lk.py lk_b.bin
# Use the result matching your active slot (check: fastboot getvar current-slot)
The verdict drives which path you take:
| Verdict |
Meaning |
Path |
COMPATIBLE_FULL |
Known magic and seccfg support are present in LK; live RPMB initialization is not proven by the binary alone |
Path A candidate below |
COMPATIBLE_SECCFG_ONLY |
Known magic and contradictory mi_get_lock_state signal are absent |
Path B candidate below |
MAYBE / UNKNOWN |
Evidence is incomplete or contradictory. Investigate manually and share scan_lk.py --json |
neither |
Do not read the Path B score as a confidence level. A COMPATIBLE_SECCFG_ONLY verdict
tops out at 44/100 by construction, because 47 of the 100 points are the magic string
that a Path B device does not have, and the mi_check_magic check keys off that same
string. A 31/100 Path B verdict is not a weak result: the absent magic is the finding.
Only Path A verdicts should be read against the 0-100 scale. A MAYBE verdict with
mi_get_lock_state is different: issue #10 shows that contradictory RPMB signals must be
investigated rather than treated as ordinary Path B.
Older fleur units (and other “HyperOS-eligible” devices) ship the MIUI-13-era LK
that Xiaomi never updated. On those, the RPMB lock layer simply isn’t implemented
and the destructive RPMB erase step is unnecessary. The scanner is the only
reliable way to tell from outside.
Path A — COMPATIBLE_FULL (known RPMB mechanism in LK)
Three commands, one BROM cycle each:
⚠️ Before erasing: locate the live RPMB magic first. Its mapping depends on
the LK/storage driver family and cannot be inferred safely from the SoC name.
Dump your RPMB and grep for the magic string:
python3 mtk.py da rpmb r rpmb_dump.bin
grep -boa "Jz8PNRUF" rpmb_dump.bin
# byte_offset / 256 = your sector
# Known: MT6781/MT6769Z = 57344 | MT6833 gold = 65504 | MT6833 light = 57344
# MT6833 varies by LK family: do not assume 65504, always grep. Unknown SoC: use grep result.
If grep returns more than one offset, they are not separate copies. The dump mtkclient
produces is larger than the physical RPMB, so the same location shows up several times. Hits
separated by exactly 16 MiB (0x1000000) are aliases of each other, because the address field in
an RPMB frame is two bytes and therefore wraps at 65536 sectors:
122880 mod 65536 = 57344 same physical location, seen twice
Erase the lowest offset of each aliased pair, once. On rubypro, grep returns 14680064 and
31457280, which is sector 57344 and its 16 MiB alias, so only 57344 gets erased. On miel it
returned four hits collapsing to two real locations (16352 and 57344), and 57344 alone was
sufficient.
Then erase the sector you found:
# STEP A1: Erase RPMB magic region (replace <YOUR_SECTOR> with grep result above)
python mtk.py da rpmb e --sector <YOUR_SECTOR> --sectors 4
# STEP A2: Unlock seccfg
python mtk.py da seccfg unlock
(Step 0 already produced the mandatory rpmb_backup.bin.) Between every da command,
the phone needs a fresh BROM entry: unplug, redo the key combo, replug.
A1 and A2 are the critical pair. If the phone boots between them, the magic can come back, and
A2 then unlocks a seccfg that the restored magic will override on the next boot. Never touch the
Power button in this window. Unplug and redo the key combo, that is all that is needed. See
the rule above.
If your device needs a specific preloader, add --preloader preloader_CODENAME.bin to each command.
Note on the erase: only 4 sectors are touched — just the magic region. Absent magic alone forces
the DEFAULT→seccfg fallback path.
If you re-dump to check the erase, do it in a separate cold session
⚠️ A da rpmb r issued without a cold DA shutdown in between can reattach to the still-live DA
session and return a volatile view in which the magic already looks erased, whether or not
anything reached the flash. That is a false positive, and it is convincing enough to make you
believe the erase worked when it did not.
# WRONG: chained read, may show a magic that is not actually gone
python mtk.py da rpmb e --sector <YOUR_SECTOR> --sectors 4
python mtk.py da rpmb r rpmb_check.bin
# RIGHT: DA shutdown, cold BROM entry, then read without a normal boot
python mtk.py da rpmb e --sector <YOUR_SECTOR> --sectors 4
python mtk.py reset
# ... unplug, hold Vol+ and Vol-, replug directly into BROM ...
python mtk.py da rpmb r rpmb_check.bin
grep -boa "Jz8PNRUF" rpmb_check.bin
Discovered by LucaCraft89 (#13) while investigating an erase that reported success and did not
persist. Three dumps were needed to see it: cold baseline, same-session read, and cold read after
a DA shutdown plus fresh BROM entry. Only the third one tells you the truth.
Reading the signature field is a better check than the magic. If the record at 0xE20000
contains wrong unlock data instead of milock state, the boot chain has flagged the record as
tampered, which means it ran and inspected the RPMB. That distinguishes “erased then restored”
from “never erased”, which magic presence alone cannot. Observed by LucaCraft89 (#13).
Storage vendor alone does not determine the sector. An earlier version called 57344 the
“Samsung layout”, but rubypro carries SK Hynix H9QT1G6DN6X132 and has the same measured sector.
Conversely, two MT6833 LK families use different sectors: gold uses 65504 while light uses
57344. The mapping is therefore driver-family-specific, not a reliable function of vendor or SoC.
Always use the live dump.
If your dump has no magic and scan_lk.py returned COMPATIBLE_SECCFG_ONLY, skip to Path B. If
the scanner returns MAYBE because an exact RPMB lock signal such as mi_get_lock_state remains,
stop for manual analysis; issue #10 proves that absent Jz8PNRUF alone is not sufficient.
Path B — COMPATIBLE_SECCFG_ONLY (clean seccfg-only candidate)
One command:
python mtk.py da seccfg unlock
That’s the entire write. Do not add an RPMB erase to a clean Path B result.
Look for these lines in the mtkclient output to confirm success:
XFlashExt - Detected V4 Lockstate
SecCfgV4 - hwtype found: V4
Sej - AES128 CBC - HACC init/run/terminate
DaHandler - Successfully wrote seccfg.
Step 2 — Verify in fastboot
After mtkclient finishes, fully power-off the phone, then hold Vol- + plug USB to enter
fastboot. Run:
fastboot getvar unlocked # AUTHORITATIVE. Expected: unlocked: yes
fastboot getvar secure # Expected: secure: no
fastboot oem lks # Optional. Expected: (bootloader) lks = 0
fastboot getvar unlocked is the source of truth. If it returns yes, the unlock is in
seccfg and persistent. Stop there, you are done.
oem lks is a hidden OEM command that not every LK build implements. On builds that don’t, it
returns an error while the device is perfectly unlocked:
fastboot oem lks
FAILED (remote: Unable to do OEM command Status(-7, EX_UNSUPPORTED_OPERATION)
fastboot getvar unlocked
unlocked: yes
Reported on sea/MT6781 by katavnnn (#12). Treat oem lks as a bonus signal, never as the
verification step. An error there means the command is missing, not that the unlock failed.
Step 3 — Handle the first boot state if required
First verify fastboot getvar unlocked: yes, then try the normal boot. Some devices may reject the
pre-existing userdata or metadata after the trust-state change and show a dm-verity/corruption screen.
If that happens, return to fastboot and wipe the affected state:
fastboot erase userdata
fastboot erase metadata
fastboot reboot
The thunder report in issue #18 booted Android without an explicit userdata or metadata erase, so
the wipe is not universal. Treat the entire procedure as data-destructive anyway: no report can
guarantee preservation for a different firmware, LK or pre-existing userdata state.
RPMB Sector Reference (click to expand)
Proven: POCO M4 Pro 4G (fleur, Samsung UFS)
Verified by RPMB dump analysis. See proof/VERIFICATION.md for full evidence.
| Data |
Dump Byte Offset |
mtkclient Sector |
Content |
| Lock magic |
0xE00000 |
57344 |
Jz8PNRUF, 8 bytes |
| Lock state length |
0xE10000 |
57600 |
length of the string at 0xE20000 |
| Signature data |
0xE20000 |
57856 |
milock state, or wrong unlock data after tampering |
Sector math: byte_offset / 256 = sector → 0xE00000 / 0x100 = 57344
Command: da rpmb e --sector 57344 --sectors 4
0xE10000 is a length field, not a counter. It holds the length of the string stored at
0xE20000, and it is easy to misread as a write counter. On rubypro it was seen going from
0x0C to 0x11 alongside the string changing from milock state (12 characters) to
wrong unlock data (17 characters). Both fields are updated together by the boot chain, so a
change there means something inspected the record, not that a write was authenticated. The real
RPMB write counter is not in the addressable data area at all, it is a controller register
returned inside the response frame.
Note: The LK code internally uses offset 0x3FE0 when calling rpmb_read(). The mapping
from this LK-internal address to the physical dump offset 0xE00000 is handled by the RPMB
driver. For users, only the mtkclient sector number matters.
Other SoCs
The sector is LK/storage-driver-family-specific, not storage-vendor-specific. rubypro carries
SK Hynix H9QT1G6DN6X132 and has its magic at 57344 like the Samsung MT6781 parts, while the
gold and light MT6833 LK families differ despite sharing a SoC family. Always grep the dump.
Known values:
| SoC / LK family |
Sector |
Dump offset |
Confirmed by |
Unlock status |
| MT6781 (Helio G96) |
57344 |
0xE00000 |
fleur, miel, sea |
Tier 1 |
| MT6769Z (Helio G85/G88 family) |
57344 |
0xE00000 |
fire, earth |
Tier 1 on fire; Tier 3b on earth |
| MT6833 Dimensity 6080, gold LK |
65504 |
0xFFE000 |
gold, found by KTS618 |
Tier 1 |
| MT6833 Dimensity 700, light LK |
57344 |
0xE00000 |
thunder, found by ogmkp |
Tier 1 |
| MT6877 (Dimensity 920/1080) |
57344 |
0xE00000 |
rubypro, pissarro |
Tier 3b (sector detected, early boot chain restores magic, no unlock achieved) |
| MT6769Z eMMC (earth) |
57344 |
0xE00000 |
ssg-enjoyer, vivaad |
Tier 3b (same restoration pattern as MT6877) |
The right column distinguishes the sector we have detected on the device from whether the unlock
has actually been achieved on hardware. On Tier 3b entries the sector is correct and the erase
reaches flash, but the early boot chain restores the magic on every boot and fastboot verification always
reports locked. See COMPATIBILITY.md for the full breakdown.
If your SoC is not listed, dump the RPMB and grep. And read the note on multiple hits above:
offsets exactly 16 MiB apart are aliases of one location, not separate copies.
scan_lk.py — How the Scanner Works
The scanner that gates the procedure above analyzes any Xiaomi MTK LK binary using string
searches and byte pattern matching:
- Whether the RPMB magic string
Jz8PNRUF is present in the binary (exact match)
- UFS RPMB type detection (heuristic based on constant values)
- RSA key detection (exact prefix match for the known Xiaomi key, entropy heuristic fallback)
- Function presence detection via string references — note: this is string-based, not ARM instruction analysis, so a Xiaomi build that strips or obfuscates debug strings could fool it
- Compatibility verdict with score (0-100%)
The scanner accepts LK binaries from any source:
# From firmware ZIP
unzip firmware.zip # -> images/lk.img or lk_a.img
# From device via mtkclient (recommended — matches what's actually running)
python mtk.py r lk_a lk_a.bin
# Run scanner — scan BOTH slots (lk_a and lk_b can differ after OTA)
python3 scan_lk.py lk_a.bin
python3 scan_lk.py lk_b.bin
# Use the result matching your active slot (fastboot getvar current-slot)
python3 scan_lk.py lk_a.bin --json # structured output
See COMPATIBILITY.md for the full device database — entries are indexed
by LK build hash because the same codename can ship multiple LK generations.
For a reference-quality hardware report, see issue #18: it includes exact firmware and LK hashes, storage identifiers, slot state, measured byte changes, cold-boot verification and explicit limits on what was not tested.
FAQ (click to expand)
Q: How can you erase RPMB without the device-specific RPMB key?
A: mtkclient derives the RPMB authentication key via the HACC engine through SEJ after the Kamakiri BROM exploit — the same mechanism it uses to handle SecCfgV4 encryption. The key derivation happens transparently when you run the da rpmb e command.
Q: Will this wipe my data?
A: Assume yes and back up everything. Some devices require a userdata/metadata wipe after the trust-state change, while the thunder hardware report booted without an explicit wipe. There is no cross-build guarantee of data preservation.
Q: Can I relock after?
A: Yes. mtk da seccfg lock relocks. Full lock/unlock cycle tested and reversible.
Q: Why erase only 4 sectors?
A: We only need to remove the magic string. The lock state length and signature data at higher sectors are irrelevant once the magic is absent — the LK falls back to seccfg without reading them.
Q: Every command said success but nothing is unlocked. What happened?
A: Three known causes, listed from most to least common. All three are silent and all three print a perfect log.
- You were on a USB 3.0 port. Writes get acknowledged but never reach flash. Move to a black USB 2.0 port and redo the whole procedure. See Use a USB 2.0 port.
- The phone started up between the erase and the unlock. The early boot chain can rewrite the magic, and the Mi logo alone is enough. Never use the Power button in that window. See the rule.
- Your device is in Tier 3b (MT6877 or earth / Redmi 12C eMMC). On these, the early boot chain restores the magic on every tested boot path, including the boot into fastboot. See the next question.
Diagnose by re-reading rather than by reading logs: dump the RPMB in a fresh BROM session and grep -boa "Jz8PNRUF". If the magic is present, the erase did not survive. If the record at 0xE20000 reads wrong unlock data instead of milock state, the boot chain ran and reverted you.
Q: I did the full sequence correctly, the erase persists in a cold BROM re-read, but fastboot still says unlocked: no. What is happening?
A: Your device is almost certainly in Tier 3b. Dump-level tests on MT6877 rubypro and Redmi 12C earth show an early boot stage restoring the 8 magic bytes before fastboot; pissarro independently reaches the same locked result with the same MT6877 RPMB layout. pissarropro has a matching failure report but no public dump-level trace. The exact stage has not yet been isolated.
What is proven:
- The erase reaches flash and persists in a cold BROM re-read (confirmed by LucaCraft89’s three-dump test on rubypro)
seccfg unlock succeeds at the flash level (seccfg reads lock_state=3)
- The boot-time restorer changes only the 8 magic bytes, nothing else in the sector is touched (0xAA marker test)
- On earth, the sector remains zero after
mtk.py reset plus cold BROM re-entry, then the magic returns after the Mi logo appears
- The literal
Jz8PNRUF does not appear in earth’s preloader boot1 dump, so the responsible stage remains unidentified
This is a hard block for the current method. The realistic next step is comparing the MT6877 and earth boot chains in Ghidra, starting with callers of the LK magic-write path and then following evidence into the preloader, ATF/TEE or another loader as needed. That work is open. If you have the hardware to help, see the “What would help” section in COMPATIBILITY.md.
Q: fastboot oem lks returns an error. Did it fail?
A: Not necessarily. That OEM command is not implemented on every LK build. On sea/MT6781 it returns EX_UNSUPPORTED_OPERATION while the device is genuinely unlocked. fastboot getvar unlocked is the authoritative check.
Q: My device is MT6833 and the scanner says sector 65504, but my grep returned 57344. Which do I trust?
A: The grep. MT6833 ships with two different LK driver families (gold and light), and they use different internal RPMB offset logic. On gold (Dimensity 6080, Redmi Note 13 5G) the magic lives at sector 65504. On light (Dimensity 700, POCO M4 5G thunder) it lives at sector 57344. ogmkp traced the divergence to a uxth truncation at LK offset 0x6AD96 on the light driver, which clamps the RPMB address to 16 bits and reduces both internal base values to 57344. Both are verified on hardware. Always grep your dump.
Q: RPMB reads fail with unpack requires a buffer of 12 bytes followed by TypeError: object of type 'int' has no len(). Is the phone unsupported?
A: Not necessarily. On the thunder MT6833 report, the supplied DA placed a 16-bit Thumb alignment NOP between the ufshcd_probe_hba return and its UFS HBA literal. mtkclient read at a fixed +8 offset and produced 0x6CD0BF00 instead of the aligned pointer 0x40066CD0, so ordinary UFS access worked while RPMB access failed. This is a host-side DA-extension patching bug, not evidence that the RPMB or phone is unsupported. Do not hardcode those addresses for another DA; the upstream fix must detect the optional alignment NOP. See mtkclient PR #426 and the original measured evidence in issue #18.
Warnings
- BACKUP RPMB FIRST. Step 1 is your safety net. Never skip it.
- BACKUP NV PARTITIONS (nvdata, nvcfg, nvram, persist, protect1, protect2) — without these, you risk “NV data corrupted” which breaks IMEI and baseband.
- ASSUME DATA LOSS. Whether an explicit wipe is required varies by build; back up before any write.
- NEVER TOUCH THE POWER BUTTON between the RPMB erase and
seccfg unlock. The early boot
chain can rewrite the magic on start-up, and reaching the Mi logo is enough. See
the rule.
- USB 2.0 ONLY. A USB 3.0 controller makes writes report success without committing to flash.
See Use a USB 2.0 port.
- NO CONFIRMED BROM ACCESS = NO GO. Do not infer support by comparing MediaTek part numbers:
MT6877 and MT6893 were previously misclassified that way, and Kamakiri runs on both. Support
must be indexed by the exact SoC generation and observed BROM exchange. See
COMPATIBILITY.md.
- HyperOS 1, 2, and 3 all have hardware confirmations. That proves version coverage, not universal device compatibility. Always scan both LK slots and inspect live RPMB state.
- SCAN BOTH LK SLOTS. lk_a and lk_b can carry different LK builds after an OTA. Confirmed independently on Redmi 12 (
fire) and POCO M4 Pro 4G (fleur) — slot B had the RPMB lock while slot A did not.
- VERIFY BY RE-READING, NOT BY READING LOGS. Both failure modes above print success. Only a
fresh RPMB dump and
fastboot getvar unlocked tell you the truth.
- Unlocking the bootloader voids manufacturer warranty.
Credits
- bkerler/mtkclient — the foundation that makes BROM access possible
- The Kamakiri exploit researchers
- Everyone who reported the seccfg bug on GitHub — your reports helped map the scope
License
AGPL-3.0 — If you use this code, you must publish your source code too. No closed-source paid tools.
Researched & tested by: Jz8root
评论