👁 46

这台小米盒子的 eMMC 已经整卡读出来了。型号 MDZ-26-AA——也就是 小米盒子 4S Pro(固件内部型号 MiTV-AZFP0),平台代号 frozen。SoC 是 晶晨 S905X3(Amlogic SM1),4 核 Cortex-A55 + Mali-G31;存储是 16 GB eMMC;系统停在 Android 9、内核 4.9.113。用户区一共切出 20 个分区,其中 18 个全挤在最前面 1.4 GiB 里。下面把硬件和固件分区摊开看一遍。

小米盒子 4S Pro(MDZ-26-AA,Amlogic S905X3)eMMC 分区地图:20 个分区与地址空间占比
小米盒子 4S Pro(MDZ-26-AA)的 20 个分区地图 · 顶部色带按真实字节比例绘制

一、转储包与设备身份

转储是离线读的,包里就是整齐的几件东西:

xiaomiIPTVmaz-26-16G/
├── BOOT1.BIN       4,194,304 B   eMMC 硬件启动区 1
├── BOOT2.BIN       4,194,304 B   eMMC 硬件启动区 2(与 BOOT1 逐字节相同)
├── ECSD_CSD.BIN          544 B   eMMC 扩展寄存器快照(ECSD)
├── RPMB.BIN        4,194,304 B   RPMB 认证区备份
├── UserData.BIN   15,634,268,160 B   用户区整镜像(14.56 GiB)
├── EMMC_Ghost.mpj        14,098 B   编程工程文件(含完整分区清单)
├── FileStatus.cfg        25,664 B   校验 / 状态元数据
└── P_log-sanxing            629 B   编程器运行日志(GBK 编码)

最有用的是 UserData.BIN——15,634,268,160 字节的用户区完整镜像。这个长度和 ECSD_CSD.BIN 里 SEC_C 寄存器读出的 30,535,680 × 512 B 完全一致,说明这份转储是完整、没有截断的。

项目 值
型号 MDZ-26-AA(小米盒子 4S Pro)
固件内部型号 MiTV-AZFP0(ro.product.model)
平台代号 frozen(ro.product.device / board / platform 三处一致)
品牌 / 类型 Xiaomi · stb(机顶盒)
系统 Android 9(SDK 28)· 构建号 23.5.10.1035
内核 Linux 4.9.113(32 位 armv7l)
构建时间 2023-05-10(安全补丁日期 2022-01-01)

二、硬件

2.1 硬件清单

项目 值
SoC Amlogic S905X3(SM1 平台,RevB)
CPU 4 × Cortex-A55(MIDR 0x411fd050),32 位
GPU Mali-G31 MP2
内存 2 GB(aml_dt=sm1_ac214_2g,内核可见 2,097,152 K)
存储 16 GB eMMC —— 三星 KLMAG1JETD-B041(产品代号 AJTD4R),eMMC 5.1,可用 14.6 GiB
Wi-Fi RTL8821CU(USB 免驱方案)
蓝牙 Realtek rtk_btusb(USB),Core 2.22
HDMI 输出 hdmitx Ver 20190815,默认 1080p60 / RGB444 8bit,支持 HDCP 2.x
HDMI 输入 hdmirx 驱动存在但 no hdmirx regs(未布线)
显示 framebuffer 1920×1080 / 32bpp,另叠 CVBS 输出
USB xhci-hcd(含 pegasus USB 网卡驱动)

Wi-Fi 和蓝牙都是 USB 方案,射频没做进 SoC 板内——这是 2018–2019 那批 S905X/X3 盒子(尤其运营商定制机)的典型做法。

顺带一个外部交叉验证:2020 年 11 月社区拆解 4S Pro 的记录里,主板是 S905X3 + 2 GB DDR3 + 16 GB eMMC,Wi-Fi 模块标称 FN-LINK 6221S——6221S 正是 RTL8821CU 的模组型号,和这次转储里读到的 USB 免驱驱动完全对得上。

2.2 eMMC 与内存布局

eMMC 是三星的 KLMAG1JETD-B041(产品代号 AJTD4R),16 GB eMMC 5.1 规格。设备自己起机时就把这颗芯片打印了出来——mmcblk0: emmc:0001 AJTD4R 14.6 GiB,容量和型号都在这一行里。转储这边也对得上:BOOT1.BIN / BOOT2.BIN / RPMB.BIN 各 4,194,304 B(4 MiB),UserData.BIN 15,634,268,160 B,换算就是 14,910 MiB 用户区——16 GB 标称容量扣掉保留区之后的可用空间。

这颗芯片的 RPMB 是已启用状态:认证密钥已经烧写、写入受密钥保护,转储自带的编程器日志(P_log-sanxing)读到的是「已使用」而不是空白区。RPMB 不存用户数据,它是给 Keystore / DRM / 回滚计数用的防重放小存储,和「只写 userdata 能不能开机」直接相关,放到第六节说。

2 GB 内存里有约 704 MB 被划成 CMA(视频编解码零拷贝缓冲),常规可用约 1.28 GB。内核保留区长得是这样:

05000000-07400000   36 MiB   linux,secmon        ← 安全监视器常驻
07400000-07500000    1 MiB   ramoops
57C00000-59C00000   32 MiB   linux,vm0_cma
59C00000-5DC00000   64 MiB   linux,vdin1_cma
5DC00000-61C00000   64 MiB   linux,vdin0_cma
61C00000-75000000  308 MiB   linux,codec_mm_cma  ← 编解码 CMA(最大一块)
75000000-77800000   40 MiB   linux,di_cma
77800000-7F800000  128 MiB   linux,ion-dev
7F800000-80000000    8 MiB   linux,meson-fb      ← framebuffer

三、Amlogic MPT 分区表

Amlogic 不用 MBR,也不用 GPT,而是把分区表以私有格式写在 reserved 分区开头,叫 MPT(Media Partition Table)。在偏移 0x02400000 处能读到魔数 MPT\0 加版本号 01.00.00。把它解出来,20 个分区一目了然:

# 名称 起始偏移 大小 里面是什么
0 bootloader 0x00000000 4 MiB 引导镜像(AMLSECU! 加密内核)
1 reserved 0x02400000 64 MiB Amlogic 私有 MPT 分区表就写在这
2 cache 0x06C00000 512 MiB 缓存区
3 env 0x27400000 8 MiB U-Boot 环境变量
4 logo 0x28400000 8 MiB 开机图资源容器 AML_RES!(10 项)
5 recovery 0x29400000 24 MiB Recovery 内核 / 恢复镜像
6 misc 0x2B400000 8 MiB 引导控制块(BCB),空
7 dtbo 0x2C400000 8 MiB 设备树叠加层
8 cri_data 0x2D400000 8 MiB 加密 / 密钥数据
9 param 0x2E400000 16 MiB 引导参数
10 boot 0x2FC00000 16 MiB 内核 + ramdisk
11 rsv 0x31400000 16 MiB 保留,全空
12 metadata 0x32C00000 16 MiB 元数据(FDE / OTA)
13 vbmeta 0x34400000 2 MiB AVB 校验元数据(空壳)
14 tee 0x34E00000 32 MiB OP-TEE 可信执行环境
15 factorydata 0x37600000 10 MiB 出厂数据
16 vendor 0x38800000 512 MiB 厂商只读卷 ext4(卷标 vendor)
17 system 0x59000000 1 GiB Android 系统卷 ext4(卷标 /)
18 product 0x99800000 32 MiB 产品定制卷
19 data 0x9C000000 12414 MiB(12.1 GiB) 用户数据 ext4(全盘加密 FDE)

注:分区表里的 product 偏移原始报告写成 0x09980000,多了一个 0;按后一个分区 data 的偏移反推,正确值是 0x99800000。

3.1 布局规律:每个分区后面固定留 8 MiB

除了 bootloader → reserved 之间那个 32 MiB 空洞,其余每个分区之后都固定留 8 MiB 间隙。这些间隙不是浪费——它们是分区级的磨损均衡预留,保证擦除某个分区时不会波及相邻分区。整个镜像里 10.1% 的已擦除区域,绝大多数就落在这些间隙上。

3.2 三重自洽性验证

这份分区表不是猜的,有三条互相独立的证据能对上:

验证项 结果
data.offset + data.size = 15,634,268,160 = UserData.BIN 文件大小 ✔
ECSD_CSD.BIN 的 SEC_C = 30,535,680 × 512 = 15,634,268,160 ✔
内核 meson-mmc: 注册表(20 个 mmcblk0pNN) 偏移 / 大小与 MPT 逐项吻合 ✔

四、三个 ext4 卷

20 个分区里只有三个是真正的文件系统(超级块在分区起点 +1024,magic 0xEF53):system、vendor、data。它们的空间占用情况是直接从超级块里读出来的:

卷 偏移 大小 总块数 空闲块 已用 卷标
system 0x59000000 1 GiB 262,144 43,596 83.4% /(根)
vendor 0x38800000 512 MiB 131,072 23,646 82.0% vendor
data 0x9C000000 12.1 GiB 3,177,980 934,609 70.6% (无卷标)

三个卷都开了一组共同的 ext4 特性(CSUM_SEED、EXTENTS、64BIT、FLEX_BG、INLINE_DATA、DEFRAG);data 额外开了 EXT_ATTR、GDT_CSUM 和 QUOTA。SELinux 是启用的,system 里能直接看到 u:object_r:rootfs:s0 上下文串。

另外 system 和 vendor 的 UUID 生成方式不一样:vendor 的 UUID 第三段是 5901,属于 Android 用卷名 + 路径哈希出来的确定性 UUID;system 那边则是真随机 UUID。同一台机器上两种做法并存,是 mkfs 时机和参数不同造成的,属正常。

五、系统版本

系统身份直接从 /vendor 和 /system 的构建属性里读:

属性 值
ro.build.fingerprint Xiaomi/frozen/frozen:9/PPR1.180610.011/1035:user/release-keys
ro.build.version.release 9(Android 9 Pie)
ro.build.version.sdk 28
ro.build.display.id 23.5.10.1035(MiTV 商业版本号)
ro.build.version.security_patch 2022-01-01
ro.build.date Wed May 10 17:57:03 CST 2023(构建机 c5-mitv-cm-build06.bj)
ro.product.cpu.abi armeabi-v7a —— 只有 32 位
ro.build.type / tags user / release-keys

内核是 Linux version 4.9.113-gc1ad0ff2cfbd-dirty,GCC 6.3.1 编的 32 位 SMP 内核(armv7l)。同一分区里还躺着另外两个内核构建变体(4.9.113-g0ba0888、4.9.113-g1ee05ea),来自另一台构建机——说明这个 BSP 分支同时挂着好几条并行构建流。

六、RPMB 与「只写 userdata 能不能开机」

这是整卡备份最常被问到的一句,答案可以直接给:同一台机器上、只把 UserData.BIN(用户区整镜像)写回去,能正常开机,数据也还在。前提有几条,先把这台机器的实际情况钉住:

项目 本机实况
RPMB 区 4 MiB,已启用:认证密钥已烧写、写入受密钥保护
它的作用 给 Keystore / DRM / 回滚保护提供防重放计数,是安全小存储,不存用户数据
userdata 加密 FDE 全盘加密:ro.crypto.type = block: FDE device,AES-256-CTS
密钥链位置 data 分区尾部的 crypto footer(被包起来的主密钥)+ TEE/Keymaster 的硬件密钥(tee / cri_data 分区)

为什么只写 userdata 不影响开机:

  • 启动链根本不碰用户区。bootloader / reserved / boot / system / vendor 等 19 个分区全挤在最前面 1.4 GiB 里,写 userdata 时它们一个字节都不会变,引导流程原样。
  • 解密密钥不在用户区里。FDE 的主密钥由 TEE/Keymaster 用硬件密钥包住,落在两条链上:data 尾部的 crypto footer + tee 里的 TEE。写回的是整段用户区镜像(footer 一起回来),而 tee / cri_data / metadata 都没动 —— 密钥链完整,开机照常解密挂载。
  • RPMB 这一层压根没参与。开机只用最前面那 1.4 GiB 里的引导分区和 tee,RPMB 里存的是防重放计数与 DRM / Keystore 凭据,既不参与引导也不参与解密。只写 userdata 完全不碰它,RPMB 保持原样就是最安全的状态。

反过来,四条容易踩的坑:

  • 别顺手把 RPMB.BIN 也写回去,尤其是别的机器、别的批次读出来的备份。这台机器的 RPMB 已经启用、带着密钥和单调写入计数:把别处的 RPMB 内容灌进去,计数一旦回退或者密钥对不上,RPMB 认证就不通过——表现通常是 Keystore 硬件密钥失效、DRM 掉级(比如 Widevine 掉出 L1)、个别应用要求重新登录甚至清数据,而不是简单回滚。既然只写 userdata 根本不碰它,就别多这一手。
  • 只能写回原来那台机器。硬件密钥跟芯片绑定,换机只写 userdata 会解不开,Android 走「无法解密 → 清除数据」这条路:能开机,但数据归零。
  • 必须写完整段。转储出来的是 14.56 GiB 的整块用户区,包含 data 分区尾部那段 crypto footer。如果只写「实际用到的 1884 MB」或者保存时截断,footer 一丢就再也解不开,只能格式化 data 重来(等于恢复出厂)。
  • 别在写入中途断电。data 是 ext4 + 加密,写一半中断会留下不一致的文件系统,开机 fsck 可能过不去;这时最省事的处理就是格式化 data 让它重建。

一句话:只写 userdata 是这套转储里最安全的操作 —— 启动链和硬件密钥都不在用户区里,要守的规矩就三条:同一台机器、写完整段、别碰 RPMB。

七、顺带发现的几点

  • 全盘加密是开着的:ro.crypto.type = block: FDE device,文件名加密用 aes-256-cts。也就是说 data 分区直接读出来是密文,转储本身不泄数据。
  • ADB over TCP 默认端口 5555:service.adb.tcp.port 和 persist.adb.tcp.port 都写死 5555,但 ro.secure=1、ro.adb.secure=1、ro.debuggable=0,属于生产安全模式,默认连不上。
  • 属性里有一处自相矛盾:system 里 ro.debuggable=0、ro.build.type=user,但 vendor 里同一组属性却是 ro.debuggable=1、userdebug/eng。多半是 vendor 镜像从 debug 分支裁出来忘了改回去,不影响使用,但在做完整性校验时是个明显的指纹。

需要提醒的是:这份转储的基线是 2018 年的 AOSP 9,安全补丁停在 2022 年 1 月。设备还在正常运行,但系统层面的漏洞是没人再补了——当存储载体或拆机研究没问题,别再拿它跑敏感业务。

数据来源:小米盒子 4S Pro(MDZ-26-AA,内部型号 MiTV-AZFP0)的 16 GB eMMC 整卡离线转储 (编程器读取,含 BOOT1/BOOT2/ECSD_CSD/RPMB/UserData 全套)· 分区表由 Amlogic MPT 结构解出,并与内核 meson-mmc 注册表、ECSD 的 SEC_C 寄存器交叉校验一致 · eMMC 型号取设备自身起机打印的 mmcblk0: emmc:0001 AJTD4R 14.6 GiB,RPMB 状态与容量取转储自带的编程器日志 P_log-sanxing(BOOT1/BOOT2/RPMB 各 4 MiB、用户区 14,910 MiB,与转储文件逐个吻合)。