Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Read and write IMEI(s) in the NVRAM `LD0B_001` file on **DuoQin F21 Pro** (single-SIM), **DuoQin F25** (dual-SIM), and **TIQ M5** (dual-SIM, MT6761) — or in a full `nvdata` partition image — offline, no device needed.

> `live_patch.sh` is an interactive ADB script that patches a live rooted MTK device. On dual-SIM devices it prompts for slot 1 or 2 — see [Live device patching](#live-device-patching).
> `live_patch.sh` is an interactive ADB script that patches a live rooted MTK device. On dual-SIM devices it prompts for slot 1 or 2 — see [Live device patching](#live-device-patching). `termux_patch.sh` does the same thing on-device from Termux, no host PC required — see [On-device patching (Termux)](#on-device-patching-termux).

## Install

Expand All @@ -12,7 +12,7 @@ cd mtk-imei-switcheroo
pip install pycryptodome
```

That's it. Then run `./live_patch.sh` for the interactive flow, or call `python3 imei_tool.py` directly. Needs Python 3.6+; for live patching you also need `adb` (and `fastboot` if you'd rather flash the partition image).
That's it. Then run `./live_patch.sh` for the interactive flow, or call `python3 imei_tool.py` directly. Needs Python 3.6+; for live patching you also need `adb` (and `fastboot` if you'd rather flash the partition image). To patch from the device itself instead of a host PC, run `./termux_patch.sh` from Termux — it auto-installs `python` and `pycryptodome` on first run if missing.

## How it works

Expand Down Expand Up @@ -60,6 +60,10 @@ The tool auto-detects whether the input is a standalone `LD0B_001` or a partitio

- [`flipphoneguy/f21-imei-switcheroo-app`](https://github.com/flipphoneguy/f21-imei-switcheroo-app) — Java/Android port. Cross-verified bit-for-bit against `imei_tool.py`: same AES key, slot offsets `{0x40, 0x60}`, plaintext layout, and MD5-XOR checksum.

## On-device patching (Termux)

`termux_patch.sh` is the same flow as `live_patch.sh` but runs entirely on the device from Termux — no host PC, no ADB. It checks for `python` and `pycryptodome` (offers to `pkg install` / `pip install` if missing), reads `LD0B_001` directly via `su -c`, runs `imei_tool.py` to rewrite it, copies it back into place, and offers to reboot. Like `live_patch.sh`, it counts populated slots and prompts `[1/2/n]` on dual-SIM or `[y/N]` on single-SIM. Termux must be granted root (Magisk → Superuser → allow Termux).

## Credits

- AES key derivation algorithm from [bkerler/mtkclient](https://github.com/bkerler/mtkclient).
Expand Down
93 changes: 93 additions & 0 deletions termux_patch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
# Termux interactive IMEI changer for rooted MTK devices.

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TOOL="$SCRIPT_DIR/imei_tool.py"

IMEI_PATH="/mnt/vendor/nvdata/md/NVRAM/NVD_IMEI/LD0B_001"
WORK="$(pwd)/tmp"
mkdir -p "$WORK"
BACKUP="$WORK/backup_LD0B_001.bin"
PATCHED="$WORK/patched_LD0B_001.bin"

die() { echo "Error: $1" >&2; exit 1; }

echo "Checking dependencies..."

if ! command -v python3 >/dev/null 2>&1; then
read -p "Python not installed. Install now? [y/N] " ans
if [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then
pkg update && pkg install python || die "Python install failed"
echo "Installed python"
else
die "Python not installed"
fi
fi

if ! python3 -c "from Crypto.Cipher import AES" 2>/dev/null \
&& ! python3 -c "from Cryptodome.Cipher import AES" 2>/dev/null; then
read -p "pycryptodome not installed. Install now with pip? [y/N] " ans
if [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then
pip install pycryptodome || die "pycryptodome install failed"
echo "Installed pycryptodome"
else
die "pycryptodome not installed"
fi
fi

echo "All dependencies are installed"

push_replace() {
su -c "mount -o remount,rw /mnt/vendor/nvdata" </dev/null >/dev/null 2>&1
su -c "mount -o remount,rw /" </dev/null >/dev/null 2>&1
su -c "cp $PATCHED $IMEI_PATH" </dev/null || die "cp $PATCHED to $IMEI_PATH failed"
su -c "chmod 660 $IMEI_PATH" </dev/null
su -c "chown root:system $IMEI_PATH" </dev/null
}

su -c id </dev/null 2>/dev/null | grep -q "uid=0" \
|| die "su -c failed: device must be rooted and root must be granted to termux shell"
echo "Device is rooted, continuing..."

echo "Reading current IMEIs from device..."
su -c "cat $IMEI_PATH" </dev/null > "$BACKUP" 2>/dev/null \
|| die "Cannot read LD0B_001 from device"

file_size=$(wc -c < "$BACKUP")
[ "$file_size" -eq 384 ] || die "LD0B_001 is $file_size bytes (expected 384) - read may have corrupted the file"

echo ""
read_output=$(python3 "$TOOL" read "$BACKUP") || die "Read failed (imei_tool.py error above)"
echo "$read_output" | sed 's/^/ /'
echo ""

populated=$(echo "$read_output" | grep -cv '(empty)')

if [ "$populated" -ge 2 ]; then
read -p "Change which IMEI? [1/2/n] " choice
case "$choice" in
1|2) slot="$choice" ;;
*) echo "No changes made."; exit 0 ;;
esac
else
slot=1
read -p "Change IMEI? [y/N] " ans
case "$ans" in
y|Y) ;;
*) echo "No changes made."; exit 0 ;;
esac
fi

read -p " New IMEI $slot (15 digits): " new_imei
echo "$new_imei" | grep -qE '^[0-9]{15}$' || die "IMEI must be exactly 15 digits"
echo " Patching IMEI $slot..."
python3 "$TOOL" write "$BACKUP" "$new_imei" -s "$slot" -o "$PATCHED" \
|| die "Patch failed (imei_tool.py error above)"
push_replace
echo " IMEI $slot updated."

echo ""
read -p "Reboot device now? [y/N] " ans
if [ "$ans" = "y" ] || [ "$ans" = "Y" ]; then
su -c "reboot" </dev/null
fi