Rakuten BIG s 提取原厂 boot_a 与解除 APN 新增限制

2129 字
11 分钟
Rakuten BIG s 提取原厂 boot_a 与解除 APN 新增限制

这次实测的设备是 Rakuten BIG s,型号 3917JR,代号 gaea,Android 10,ARM64、A/B 动态分区,当前槽位是 a,Bootloader 已解锁。本文只记录两件事:提取本机当前固件的原厂 boot_a,以及恢复 APN 页面新增 APN 的功能。

下面的命令、分区名和实测数值都来自这台 3917JR。换一台手机后不能盲抄,必须先确认自己的槽位和分区布局。

提取 boot_a#

普通原厂系统里的 ADB shell 没有权限直接读取 boot_a。这里的前提是手机已经进入一个能够执行 su 0 的临时 rooted Android 环境。

用 DSU 启动临时 root 系统#

实际是通过 Android 10 的 DSU 启动一个临时 rooted GSI。它只作为临时系统运行,不覆盖原厂 boot_a。这台 3917JR 的 DSU 入口被厂商隐藏了,但后端仍能工作。

我最初试过 ArrowOS 的 Android 10 ARM64 A/B GSI。它能安装进 DSU,启动后却一直卡在开机动画,ADB 也显示未授权,没法在临时系统的屏幕上完成授权,最后放弃。这是因为这台设备上那条路径没有拿到可用 shell。

最终使用的是 phhusson 官方 Android 10 v222 的 system-quack-arm64-ab-vanilla.img.xz

下载后先解压 .xz。得到的 .img 是 Android sparse image,不能直接交给 DSU;要先转换成非 sparse 的 raw ext4 镜像,例如输出为 system.raw。本机离线检查这个 raw 镜像,确认其中存在 phh-su/system/xbin/su,并且构建属性是 userdebug。这一步很重要,不能只因为文件名里有 GSI 就默认它能 root。

预先写入 ADB 公钥#

前一次 DSU 虽然已经跑到 ADB,但主机无法授权。处理方法是在电脑上复制一份 system.rawsystem-patched.raw,只改副本。随后在 Linux 的 Docker 容器中用 debugfs 将本机 adbkey.pub 写入镜像文件系统根目录的 /adb_keys

/adb_keys 要设置为 UID/GID 0/0、权限 0644,SELinux xattr 为 u:object_r:adb_keys_file:s0;写入后执行 e2fsck -fn 只读检查。核心命令如下,/work 是容器中挂载的工作目录:

Terminal window
cp /work/system.raw /work/system-patched.raw
debugfs -w /work/system-patched.raw <<'EOF'
rm /adb_keys
write /work/adbkey.pub /adb_keys
set_inode_field /adb_keys uid 0
set_inode_field /adb_keys gid 0
set_inode_field /adb_keys mode 0100644
ea_set /adb_keys security.selinux u:object_r:adb_keys_file:s0
close -a
EOF
e2fsck -fn /work/system-patched.raw

目的是让临时 GSI 启动后,电脑能直接使用这把已有的 ADB key,不会卡在临时系统的授权弹窗。这里必须放在运行时根目录 /adb_keys,不要放到 /system/etc/security/adb_keys;后一个位置不是这条 Android 10 adbd 读取授权 key 的可靠目标。

确认镜像没有问题后,把 system-patched.raw gzip 压缩为 system-patched.raw.gzKEY_SYSTEM_SIZE 必须是未压缩 raw 的字节数。

安装并启动 DSU#

这台手机需要先打开被隐藏的 DSU 功能旗标:

Terminal window
adb shell setprop persist.sys.fflag.override.settings_dynamic_system=true

把压缩后的镜像推送到手机:

Terminal window
adb push .\system-patched.raw.gz /sdcard/Download/system-patched.raw.gz

然后通过 Android 10 的 VerificationActivity 发起安装。本机 raw system 的准确大小是 1774190592 bytes,userdata 使用 8 GiB,即 8589934592 bytes:

Terminal window
adb shell am start-activity --user 0 `
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity `
-a android.os.image.action.START_INSTALL `
-d file:///storage/emulated/0/Download/system-patched.raw.gz `
--el KEY_SYSTEM_SIZE 1774190592 `
--el KEY_USERDATA_SIZE 8589934592

按手机上的锁屏凭据确认,等待安装完成。通知出现 Restart 后,必须由用户在通知里手动点 Restart;不要假定 ADB 命令可以替代这一步。

重启进入临时 GSI 后,先确认 ADB 已连接,再检查 root:

Terminal window
adb get-state
adb shell "su 0 id"

本机正是通过这一步得到可用的 su 0,才继续读取原厂 boot_a。其他 Android 版本和机型的 DSU 支持、GSI 信任校验、启动结果和 ADB 行为都可能不同;DSU、GSI root 和 ADB key 注入不能直接照搬。

先确认连接、root 和分区#

先看当前槽位,:

Terminal window
adb shell getprop ro.boot.slot_suffix

本机返回 _a,所以后面读取 boot_a。接着依次检查 ADB、root 权限、分区实际路径和大小:

Terminal window
adb get-state
adb shell "su 0 id"
adb shell "su 0 sh -c 'readlink -f /dev/block/by-name/boot_a; blockdev --getsize64 /dev/block/by-name/boot_a'"

本机的实际结果是:

/dev/block/sde11
100663296

也就是 boot_a 实际指向 /dev/block/sde11,大小为 100663296 bytes,也就是 96 MiB。其他固件和机型应当以自己的查询结果为准。

两条走不通的路#

我先在 fastbootd 中试过直接读取分区:

Terminal window
fastboot fetch boot_a boot_a.img

实测失败,fastbootd 和原厂 Recovery 都不能用来读取这个分区。

进入临时 rooted Android 环境后,我最初假设 /sdcard/Download 存在,直接执行 dd,结果同样失败:

dd: /sdcard/Download/boot_a-stock.img: No such file or directory

继续检查后发现,这个临时环境里连 /storage/emulated/0/data/local/tmp 都没有。与其继续猜手机上哪个目录能写,不如完全绕过手机临时文件,直接把 boot_a 的二进制流写到 Windows。这样也避开了 PowerShell 5.1 文本重定向可能损坏二进制的问题。

用 Python 直接写入 Windows#

把下面的脚本保存为 pull-boot-a.py。输出文件固定为脚本同目录下的 boot_a-stock.img,传输过程中先写入 .part 临时文件,完成返回码、大小和 SHA256 校验后才改成正式文件名。已有同名正式文件或临时文件时,脚本会拒绝覆盖。

import hashlib
import os
import subprocess
from pathlib import Path
DEVICE = "/dev/block/by-name/boot_a"
OUTPUT = Path(__file__).with_name("boot_a-stock.img")
PART = OUTPUT.with_suffix(OUTPUT.suffix + ".part")
def adb_text(*args: str) -> str:
result = subprocess.run(
["adb", *args], capture_output=True, text=True, check=True
)
return result.stdout.strip()
state = adb_text("get-state")
if state != "device":
raise SystemExit(f"ADB device is not ready: {state!r}")
size_text = adb_text("exec-out", "su", "0", "blockdev", "--getsize64", DEVICE)
expected_size = int(size_text)
if expected_size != 100663296:
raise SystemExit(f"Unexpected boot_a size: {expected_size} bytes")
if OUTPUT.exists() or PART.exists():
raise SystemExit(f"Refusing to overwrite existing output: {OUTPUT} or {PART}")
hasher = hashlib.sha256()
written = 0
process = subprocess.Popen(
["adb", "exec-out", "su", "0", "cat", DEVICE],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
assert process.stdout is not None
with PART.open("xb") as destination:
while chunk := process.stdout.read(1024 * 1024):
destination.write(chunk)
hasher.update(chunk)
written += len(chunk)
stderr = process.stderr.read().decode(errors="replace") if process.stderr else ""
return_code = process.wait()
if return_code != 0:
raise SystemExit(f"adb failed with exit code {return_code}: {stderr}")
if written != expected_size:
raise SystemExit(f"Size mismatch: expected {expected_size}, received {written}")
os.replace(PART, OUTPUT)
print(f"Saved: {OUTPUT}")
print(f"Size: {written}")
print(f"SHA256: {hasher.hexdigest().upper()}")

脚本中的 100663296 是这台 3917JR 当前固件的设备专用断言。别的固件或机型要按实际查询结果修改,或者移除这条断言,不能照搬这个数字。

在 PowerShell 中进入脚本所在目录后执行:

Terminal window
python .\pull-boot-a.py

本机输出如下:

Saved: E:\Downloads\gsi-cloud-patch\boot_a-stock.img
Size: 100663296
SHA256: 626B5A3C44263A9E97778130A117C6525FEE502B7CEB6FF12BBBCA8BFA99A37B

导出的文件头是 ANDROID!,手机分区哈希与 Windows 本地文件哈希一致。Windows 上可以再检查文件大小和哈希:

Terminal window
Get-Item .\boot_a-stock.img
Get-FileHash .\boot_a-stock.img -Algorithm SHA256

这个大小和哈希只代表我手上这台手机当时的固件,不能拿来当作其他设备的标准。原厂 boot 必须从自己的设备和当前固件提取,不要分享或刷入别人的 boot。

解除 APN 新增限制#

这一部分的前提是原厂系统已经取得 Magisk root,本文不展开 Magisk 的安装。

我实测使用的是 Vodafone UK,受到手机的Vodafone限制,无法新增和编辑APN。

下面用到的MCC/MNC 为 23415carrierId28,当时的 subId3。固件中的文件:

/product/priv-app/CarrierConfig/CarrierConfig.apk

assets/carrier_config_carrierid_28_Vodafone.xml 把配置设成了:

read_only_apn_types_string_array=[*]

也就是所有 APN 类型都被设为只读,设置页面随之隐藏了“+”。实测原因在固件内置的运营商配置,不是 SIM 卡或企业策略。

先查当前 subId#

subId 不能写死。先查询 SIM 信息:

Terminal window
adb shell "su -c 'content query --uri content://telephony/siminfo --projection _id:sim_id'"

从活动 SIM 对应的行中读取 _id。本机当时类似:

Row: 2 _id=3, sim_id=0

这里要使用的是 _id=3,所以本机的 SUB_ID3。换卡或系统重新分配后,这个数字可能改变。

写入运行时覆盖#

把下面命令中的 SUB_ID 替换成刚才查到的实际数字,再执行:

Terminal window
adb shell "su -c 'service call carrier_config 2 i32 SUB_ID i32 1 i32 96 i32 1279544898 i32 1 s16 read_only_apn_types_string_array i32 14 i32 1 s16 dun'"

然后检查覆盖是否已经进入 mOverrideConfigs

Terminal window
adb shell "su -c 'dumpsys carrier_config'"

在输出的 mOverrideConfigs 下应当看到:

read_only_apn_types_string_array = [dun]

本机实测结果很直接:APN 页面里的“+”恢复,可以新增 APN。原来 user_editable=0 的 VOXI 行仍可能不能直接编辑,本文只解决新增限制,不承诺能够编辑原有行。

这个覆盖只存在于运行时。重启手机或者重启 com.android.phone 后会消失;换卡后 subId 也可能改变。因此失效后要重新查询 subId,再执行覆盖命令。

但是已经增加的APN选项会一直在手机上保留。

Magisk 自动模块#

已经把重新查询活动 subId、调用同一条运行时覆盖,以及监控 carrier_configcom.android.phone 重启后重新施加的逻辑做成 Magisk 模块。它不修改 CarrierConfig.apk、APN 数据库或 user_editable,只处理重启后 APN 页面“+”又消失的问题。

仓库链接#

仓库文件:

https://github.com/John10240/Rakuten-BIG-s-3917JR

以上。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Rakuten BIG s 提取原厂 boot_a 与解除 APN 新增限制
https://blog.qiui.net/posts/2026-08-13-rakuten-big-s-boot-apn/
作者
Qiui
发布于
2026-08-13
许可协议
CC BY-NC-SA 4.0

评论区

文章目录