天猫魔盒2(TMB300A)逆向分析实录

2026-10-20
16分钟阅读时长
7598字
Featured Image

设备型号:MagicBox2
系统版本:YunOS 3.0.6
固件版本:3.0.6-RS-20160831.0951
芯片平台:晶晨 S802-H(M8)
内存:1 GiB DDR3
存储:8G eMMC
U-Boot:U-boot-00000-g667d5c3(m8_mango_v1@667d5c32) (Jul 15 2015)
内核:Linux 3.10.33(uImage,lzo 压缩)
WiFi:Broadcom SDIO(dhd / bcmsdh_sdmmc)


🔒 脱敏声明:本文涉及设备唯一标识(MAC 地址、设备序列号)、内网 IP 配置等内容,均已替换为占位符或做模糊化处理。
env.img 本身含这些信息,绝对不能公开分享。
读者在自己设备上操作时,也请注意不要将原始 env.img 或含敏感值的串口日志直接公开。


写在前面

上一篇 《天猫魔盒2(TMB300A)固件提取与备份》 记录了怎么把原厂固件完整备份出来。备份是手段,不是目的。真正想做的事,是在这台老盒子上做点魔改:精简系统、去掉不需要的服务、尝试第三方固件,或者至少让它在一个可控的环境里跑起来。

但魔改之前,得先搞清楚它内部是怎么运转的。启动链怎么走、安全校验卡在哪、system 里哪些组件是厂商定制、哪些服务可以安全移除——这些不弄清楚,刷机就是碰运气。

这篇文章就是逆向分析阶段的记录。和上一篇一样,它会随分析进度逐步补充,不会一次写完。

最终实测结果: 签名校验拦截机制 不好处理, 就不进一步深究了 (浪费时间)

前置条件:

  • 完整的分区备份
  • TTL 串口访问
  • root shell

这三样在上一篇里已经齐了。

本文目标:

  • 还原完整启动链,确认每一级的输入输出;
  • 分析安全机制(Secure Boot、系统完整性校验)对魔改的实际约束;
  • 拆解 system 分区,定位厂商定制组件和可精简项;
  • 确认硬件驱动与内核模块的依赖关系,为第三方固件适配做准备。

边界:本文只做分析和记录,不涉及具体刷机操作。魔改实操留到下一篇。


一、设备与固件概览

1.1 硬件规格

项目 规格
SoC 晶晨 S802-H(M8),四核 Cortex-A9
内存 1 GiB DDR3,DDR clock 768MHz,1T mode
存储 8G eMMC(SDIO Port C)
WiFi Broadcom SDIO,dhd 驱动
电源管理 RN5T618
电池管理 AML1218(平台复用,盒子无电池)

1.2 软件版本

项目 版本
系统 YunOS 3.0.6
固件 3.0.6-RS-20160831.0951
内核 Linux 3.10.33
U-Boot m8_mango_v1,2015-07-15
安全方案 M8-RSA-1024

1.3 已备份分区

分区 大小 用途
bootloader 4 MB 引导程序
reserved 64 MB 厂商保留
cache 640 MB 缓存
env 8 MB U-Boot 环境变量
logo / logo1 16 MB × 2 开机画面
recovery 32 MB Recovery
boot 32 MB 内核 + ramdisk
system 800 MB 系统主体
data 约 5.67 GB 用户数据
mmcblk0boot0/1 4 MB × 2 eMMC 硬件 Boot 分区

二、启动链分析

flowchart TD A["上电 / BootROM"] --> B["BL1 / TPL
M8-RSA-1024 校验
TPL secure check pass"] B --> C["U-Boot
m8_mango_v1
2015-07-15"] C --> D["读取 boot 分区
imgread kernel boot"] D --> E{"Amlogic 私有签名校验"} E -->|通过| F["加载 kernel
Linux 3.10.33
uImage, lzo"] E -->|失败| X["Image secure check fail
show error logo
boot_hang=2"] F --> G["DTB
m8_mang_1g"] G --> H["ramdisk
解压挂载"] H --> I["init
/init"] I --> J["解析 init.rc
及 import 的 rc"] J --> K["YunOS 服务启动"] X --> L["Enter v2 usbburning mode
等待 USB 烧录恢复"] style E fill:#f9c74f,stroke:#333,stroke-width:2px style X fill:#e63946,color:#fff,stroke:#333,stroke-width:2px style L fill:#457b9d,color:#fff
图 2-1:TMB300A 启动链,标注了各级校验点。

2.1 从串口日志还原的完整启动链

根据恢复出厂后抓取的完整串口日志,启动链如下:

BL1 / TPL(M8-RSA-1024 校验)
  → U-Boot(m8_mango_v1,2015-07-15)
    → kernel Linux 3.10.33(uImage,lzo)
      → DTB(m8_mang_1g)
        → ramdisk → init → YunOS 服务

2.2 各阶段关键日志

BL1 / TPL 阶段

Boot From SDIO C
Aml log : M8-RSA-1024
Aml log : TPL secure check pass!
ucl decompress...pass
Boot from internal device 1st SDIO C
  • 启动介质是 SDIO Port C 上的 eMMC;
  • TPL 阶段有 M8-RSA-1024 安全校验,日志显示通过;
  • ucl decompress 说明 TPL 是压缩存储的,启动时解压。

U-Boot 阶段

U-boot-00000-g667d5c3(m8_mango_v1@667d5c32) (Jul 15 2015 - 17:28:52)
clr h-ram
DRAM:  1 GiB
relocation Offset is: 2fec8000
  • U-Boot 编译于 2015-07-15,代码标识 m8_mango_v1;
  • 内存 1 GiB,U-Boot 自身重定位偏移 0x2fec8000。

内核引导阶段

## Booting kernel from Legacy Image at 12000000 ...
   Image Name:   Linux-3.10.33
   Image Type:   ARM Linux Kernel Image (lzo compressed)
   Data Size:    7813246 Bytes = 7.5 MiB
   Load Address: 00208000
   Entry Point:  00208000
   Verifying Checksum ... OK
    Ramdisk start addr = 0x12774800, len = 0x27bc87
      Multi dtb detected, support 2 dtbs.
        0 dtb: ... "m8_mang_1g"
        1 dtb: ... "m8_mang_2g"
      Find match dtb: 0
    Flat device tree start addr = 0x129f1000, len = 0x4bda magic=0xedfe0dd0
   Uncompressing Kernel Image ... OK
  • 内核 Linux 3.10.33,uImage 格式,lzo 压缩;
  • 加载地址和入口地址均为 0x00208000;
  • DTB 支持 1G 和 2G 两个变体,本机匹配 m8_mang_1g;
  • ramdisk 起始 0x12774800,长度 0x27bc87;
  • DTB 起始 0x129f1000,长度 0x4bda。

init 阶段(节选)

init: mount none to target failed
init: Before e2fsck_main...
init: After e2fsck_main...
SysFileCheck: do_alisec|Total time passed: 135ms||
init: data_integrity_guard main!
init: is_support_system_bak sup_sys_bak:0
init: cannot find '/system/bin/zram_mount.sh', disabling 'zram_mem'
init: cannot find '/system/bin/nvwa', disabling 'nvwa'
init: process 'aliyun_uuid', pid 1387 exited
  • SysFileCheck / data_integrity_guard 是 YunOS 的系统文件完整性校验;
  • zram_mount.sh、nvwa 在 system 中缺失,相关服务被禁用;
  • aliyun_uuid 与阿里云账号 UUID 相关。

2.3 启动链分析要点

阶段 关键点 对魔改的影响
BL1/TPL M8-RSA-1024 校验 需确认校验范围,是否覆盖 bootloader 之后的镜像
U-Boot 分区表、环境变量、bootcmd 可修改 env 调整启动参数,但需注意校验
Kernel 3.10.33,uImage 第三方内核需匹配此版本或确认兼容性
DTB 双变体,1G/2G 魔改时需保持 DTB 与硬件匹配
init SysFileCheck、alisec check-core-file 修改 system 可能触发校验,需分析绕过或关闭方式

三、init 脚本加载树

flowchart TD A["init.rc"] --> B["init.environ.rc
环境变量"] A --> C["init.usb.rc
USB 通用配置"] A --> D["init.amlogic.rc
Amlogic 平台配置"] A --> E["init.trace.rc
内核 tracing"] A --> F["init.yunos.rc
YunOS 定制服务"] D --> G["init.amlogic.usb.rc
Amlogic USB"] D --> H["init.amlogic.board.rc
板级配置"] H --> I["mount /system rw
alisec check-core-file
remount /system ro"] style H fill:#f9c74f,stroke:#333,stroke-width:2px style I fill:#e63946,color:#fff,stroke:#333,stroke-width:2px
图 3-1:init 脚本加载树(init.rc 及各 import 关系)

3.1 完整加载顺序

init.rc 头部 import 顺序:

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.trace.rc
import /init.yunos.rc

ro.hardware 在这台机器上是 amlogic,所以实际加载 init.amlogic.rc。而 init.amlogic.rc 又 import:

import init.amlogic.usb.rc
import init.amlogic.board.rc

完整加载树:

init.rc
  ├─ init.environ.rc      环境变量(PATH、BOOTCLASSPATH 等)
  ├─ init.usb.rc          USB 通用配置
  ├─ init.amlogic.rc      Amlogic 平台配置
  │    ├─ init.amlogic.usb.rc    Amlogic USB 配置
  │    └─ init.amlogic.board.rc  Amlogic 板级配置(含 SysFileCheck)
  ├─ init.trace.rc        内核 tracing 权限
  └─ init.yunos.rc        YunOS 定制服务

3.2 各文件职责

文件 职责 关键内容
init.environ.rc 环境变量 PATH、LD_LIBRARY_PATH、BOOTCLASSPATH(含 libyunos.jar)
init.usb.rc USB 通用配置 adb、mass_storage、mtp、ptp 等 USB 模式切换
init.amlogic.rc Amlogic 平台配置 视频/音频/HDMI/CPU 调频等大量 sysfs 权限设置
init.amlogic.usb.rc Amlogic USB 配置 USB 序列号、mass_storage、mtp、ptp 模式
init.amlogic.board.rc 板级配置 /system 挂载 + alisec check-core-file + remount ro
init.trace.rc 内核 tracing trace buffer 权限
init.yunos.rc YunOS 定制服务 alitvsecureboot、aliyun_uuid、nvwa、ras-daemon 等

四、U-Boot 层分析

flowchart LR subgraph EMMC["eMMC (mmcblk0)"] P0["bootloader
offset 0x0
size 0x400000"] P1["reserved
offset 0x2400000
size 0x4000000"] P2["cache
offset 0x6400000
size 0x28000000"] P3["env
offset 0x2e400000
size 0x800000"] P4["logo
0x2ec00000 / 0x1000000"] P5["logo1
0x2fc00000 / 0x1000000"] P6["recovery
0x30c00000 / 0x2000000"] P7["boot
0x32c00000 / 0x2000000"] P8["system
0x34c00000 / 0x32000000"] P9["data
0x66c00000 / 0x16b400000"] P0 --> P1 --> P2 --> P3 --> P4 --> P5 --> P6 --> P7 --> P8 --> P9 end style P7 fill:#f9c74f,stroke:#333,stroke-width:2px style P8 fill:#2a9d8f,color:#fff
图 4-1:U-Boot 分区表与地址对照

4.1 分区表(U-Boot 视角)

Partition table get from SPL is :
        name                        offset              size              flag
   0: bootloader                         0            400000                  0
   1: reserved                     2400000           4000000                  0
   2: cache                        6400000          28000000                  2
   3: env                         2e400000            800000                  0
   4: logo                        2ec00000           1000000                  1
   5: logo1                       2fc00000           1000000                  1
   6: recovery                    30c00000           2000000                  1
   7: boot                        32c00000           2000000                  1
   8: system                      34c00000          32000000                  1
   9: data                        66c00000         16b400000                  4

与 Linux 下 /proc/partitions 的 mmcblk0p1~p10 完全对应,U-Boot 视角多了 offset 和 flag:

  • offset 是分区起始地址(十六进制);
  • flag 可能与分区属性有关(如是否可启动、是否加密),具体含义需结合晶晨平台文档确认;
  • bootloader 之后到 reserved 之间有 32 MB 空隙,与 /proc/partitions 中总块数和分区块数合计的差值吻合。

4.2 环境变量分析

env.img 通过 strings 提取后,得到完整的 U-Boot 环境变量。以下是与魔改和安全相关的关键项。

💡 完整查看方法:strings env.img | head -100 只能看到前 100 行,可能截断长变量。建议用 strings env.img > env_full.txt 保存完整输出,再用 grep 按关键词筛选。

启动参数(bootargs)

将 initargs 与 storeargs 拼接,实际传给内核的 bootargs 为:

init=/init console=ttyS0,115200n8 no_console_suspend vmalloc=300m quiet
ramoops.mem_address=0x04e00000 ramoops.mem_size=0x100000
ramoops.record_size=0x8000 ramoops.console_size=0x10000 storage=2
vdaccfg=... logo=osd1,loaded,... hdmimode=1080p50hz cvbsmode=576cvbs
androidboot.firstboot=0 hdmitx=...

关键结论:

  • ❌ 没有 dm-verity / verify= 参数:内核未启用块设备级完整性校验;
  • ❌ 没有 androidboot.selinux=enforcing:SELinux 状态不由 bootargs 强制指定;
  • ❌ 没有 androidboot.verifiedbootstate=:未传递 Verified Boot 状态。

这意味着修改 system 分区后,内核层大概率不会因完整性校验而拒绝挂载。SysFileCheck 是用户空间的点名校验,不是 dm-verity。

启动流程(bootcmd / storeboot)

bootcmd=run storeboot
storeboot=echo enter storeboot...; echo Booting...; if unifykey get usid; then setenv bootargs ${initargs} androidboot.serialno=${usid}; fi; imgread kernel boot ${loadaddr}; setenv bootm_mode boot; bootm; run recovery1;
  • unifykey get usid 读取设备序列号,拼进 bootargs 的 androidboot.serialno=(原始值不展示);
  • imgread kernel boot 读取 boot 分区并启动;
  • 启动失败时执行 recovery1(显示错误 logo,挂起)。

恢复与烧录通道

recovery_key=saradc open 0; if saradc get_in_range 0x0 0x50; then msleep 50; if saradc get_in_range 0x0 0x50; then setenv user_factoryreset true; save; run recovery; fi; fi
switch_bootmode=... if test ${reboot_mode} = factory_reset; then run recovery; ... usb_burning ... safe_mode ...
  • recovery_key 通过 SAR ADC 检测某个通道电压,连续两次命中触发恢复出厂;
  • reboot_mode 决定启动路径:正常、恢复出厂、OTA 更新、USB 烧录、安全模式;
  • usb_burning 和 sdc_burning 是两条救砖通道,具体触发方式待确认。

设备唯一标识与网络配置(隐私相关)

🔒 脱敏声明:以下环境变量值已做脱敏处理,ethaddr、ipaddr、serverip、gatewayip、usid 等均为设备唯一标识或内部网络配置,原始值不公开。env.img 本身含这些信息,绝对不能公开分享。

ethaddr=xx:xx:xx:xx:xx:xx
ipaddr=x.x.x.x
serverip=x.x.x.x
gatewayip=x.x.x.x
netmask=255.255.255.0
  • ethaddr 是以太网 MAC 地址,属于设备唯一标识;
  • ipaddr / serverip / gatewayip 是产线或内网调试配置,暴露内部网络结构;
  • storeboot 中的 usid 是设备序列号,同样属于设备唯一标识,原始值不展示。

⚠️ 结论:env.img 含设备唯一标识和网络配置,绝对不能公开分享。之前文章中将 env.img 列为“可公开”是不准确的,已修正。

其他关键变量

变量 值 说明
aml_dt m8_mango_1g DTB 名称,与串口日志一致
bootdelay 0 启动不等待按键,正常上电无法中断 U-Boot
firstboot 0 首次启动标志
store 2 存储类型(eMMC)
hdmimode 1080p50hz 默认 HDMI 输出模式
outputmode 1080p50hz 默认输出模式
cvbsmode 576cvbs CVBS 输出模式

4.3 安全状态

Aml log : M8-RSA-1024
Aml log : Image secure check pass!
aml_keys: version 0 can not be init 3ff7368c
ERR(v2_common/optimus_download_key.c)L268:failed to query key state, rc 0, keyIsBurned=0
  • 镜像通过了 RSA-1024 安全校验;
  • 但 keyIsBurned=0 说明 efuse 中没有烧录设备密钥。

关键结论(已由实测修正):keyIsBurned=0 只说明 efuse 中没有烧录设备密钥,不代表 boot 分区的签名校验被关闭。后续刷入 boot_new.img 的实测证明,U-Boot 仍然使用内置公钥校验 boot 分区内 kernel 的 Amlogic 私有签名。原“efuse 未烧录密钥可能允许重签名镜像”的推测被证伪。


五、内核与 boot.img 分析

5.1 内核信息

项目 值
版本 Linux 3.10.33
格式 uImage(Legacy Image)
压缩 lzo
加载地址 0x00208000
入口地址 0x00208000
镜像大小 7,813,246 字节(约 7.5 MiB)
ramdisk 起始 0x12774800,长度 0x27bc87
DTB 起始 0x129f1000,长度 0x4bda

5.2 boot.img 格式

flowchart LR H["header
2048 字节
page size=2048"] K["kernel
7,813,310 字节
按 2048 对齐"] R["ramdisk
2,604,167 字节
按 2048 对齐"] S["second
43,008 字节
按 2048 对齐"] H --> K --> R --> S style H fill:#457b9d,color:#fff style K fill:#2a9d8f,color:#fff style R fill:#f9c74f,stroke:#333,stroke-width:2px style S fill:#e76f51,color:#fff
图 5-1:boot.img 结构图

file boot.img:

Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048

binwalk boot.img:

Android bootimg, kernel size: 7813310 bytes, kernel addr: 0x10008000, ramdisk size: 2604167 bytes, ramdisk addr: 0x11000000
项目 值
格式 Android bootimg
page size 2048
kernel 大小 7,813,310 字节(约 7.45 MB)
kernel 头地址 0x10008000
ramdisk 大小 2,604,167 字节(约 2.48 MB)
ramdisk 头地址 0x11000000
second stage 存在,地址 0x10f00000

5.3 头地址与串口日志加载地址的差异

串口日志里:

Load Address: 00208000
Entry Point:  00208000
Ramdisk start addr = 0x12774800

而 bootimg 头里 kernel addr 是 0x10008000,ramdisk addr 是 0x11000000。

解释:U-Boot 读取 bootimg 后,会把 kernel 和 ramdisk 重定位到实际运行地址。头里的地址是 bootimg 打包时的约定地址,U-Boot 根据内存布局重新搬运。所以 0x10008000 → 0x00208000、0x11000000 → 0x12774800 是 U-Boot 的行为,不是镜像损坏。

5.4 binwalk 扫出的内容

binwalk 在 boot.img 中扫出大量:

  • ELF 32-bit LSB shared object, ARM:ramdisk 里的共享库和可执行文件;
  • Copyright string:大量 Android Open Source Project 和 IBM 的版权字符串;
  • WidevineMediaKit 路径:/var/lib/jenkins/workspace/Android_Classic_Widevine/...,说明 ramdisk 里包含 Widevine DRM 组件;
  • 蓝牙配置:/etc/bluetooth/bt_vendor.conf;
  • 证书:/etc/ssl/certs/ca-certificates.crt;
  • /usr/bin/ntlm_auth:网络认证工具。

💡 bix header、mcrypt 2.2 encrypted data 等大概率是误报或加密数据,不必过度解读。

结论:ramdisk 里不只有 init 脚本,还塞了不少厂商预置的二进制和库。

5.5 boot.img 内核与 ramdisk 数据疑似加密

abootimg -x 能正常解出 zImage、initrd.img、bootimg.cfg,但 zImage 前 512 字节全是随机字节,没有任何可读字符串:

00000000: 4d7a 6919 3461 6608 5ba2 114d c758 ee1b  Mzi.4af.[..M.X..
00000010: 0a56 3522 4c6f 5ddf f6e1 8d89 fda9 5565  .V5"Lo].......Ue
...

标准 uImage magic 是 27 05 19 56,gzip 是 1f 8b,ELF 是 7f 45 4c 46,前 512 字节里都没有出现。initrd.img 同样全是随机字节,file 返回 data,binwalk 无识别结果。

判断:boot.img 里的 kernel 和 ramdisk 数据很可能被 Amlogic 私有加密或加扰。bootimg.cfg 中的地址和大小与串口日志一致,说明 abootimg 切分偏移没错,只是内容不是明文。

keyIsBurned=0 只说明 efuse 中未烧录设备密钥,不代表镜像本身未加密——加密密钥可能硬编码在 BL1/U-Boot 中。

直接解包尝试结果:

  • Customization Tool 10.1.0 在部分 Windows 环境下启动闪退,无法使用。论坛中也有其他用户报告类似问题,换机器或换版本不一定能解决。
  • mkbootimg_tools 的 mkboot 脚本成功识别了 bootimg 头部(kernel 大小 7813310、ramdisk 大小 2604167、page size 2048 等),但最后报错 ramdisk is unknown format, can't unpack ramdisk。
  • 公开工具 abootimg、unpackbootimg、mkboot 均无法解包此 ramdisk,确认 Amlogic 对 ramdisk 数据做了私有封装或加密处理。

对魔改的影响:

  • 不能直接用标准 abootimg / unpackbootimg / mkboot 解包和重打包;
  • 不建议在逆向加密算法上投入过多时间,目标不是“解析原厂 ramdisk”,而是“让修改后的系统能启动”;
  • 更实际的路线是从运行系统重建 ramdisk。

5.6 second stage 实测结论与文件取舍

abootimg -i 报告的 second stage size = 2604167 与 ramdisk size 完全相同,这个值不可靠。实际提取结果:

工具 文件 大小 md5
abootimg -x stage2.img 42 KB ffa699b7bc23daec92da4765db819d90
mkboot second.img 42 KB ca930f1535689e9aada383cc2ec5372e
Python 按 42 KB 偏移切分 second_42k.bin 43008 字节 ca930f1535689e9aada383cc2ec5372e

mkboot 和 Python 切分结果完全一致,互相印证,说明 second 的真实大小是 43008 字节(42 KB),内容以这两个为准。abootimg 虽然也切出 42 KB,但内容不同,说明它对 Amlogic 私有 bootimg 的 second 偏移解析有偏差,stage2.img 不可用。

关于 mkboot 报错的说明:mkboot 解包时报告 ramdisk is unknown format, can't unpack ramdisk,这个错误发生在“尝试解压 ramdisk”阶段,不影响前面的“按 header 偏移切分 kernel、ramdisk、second”步骤。second.img 在报错之前就已经切好,且与 Python 按标准 bootimg 布局独立切出的结果 md5 完全一致,说明 mkboot 的 second 切分是正确的。

关于 second.img.tmp:mkboot 解包后会同时留下 second.img 和 second.img.tmp。.tmp 是工具“先写临时文件再改名”的中间产物,second.img 已正常生成,说明 second 切分完成。重打包用 second.img,不要用 .tmp。

second stage 只能从 boot.img 里切:second 区域不像 ramdisk 那样会在运行时解压挂载,内核启动时也没有使用它(/proc/cmdline 无相关参数,dmesg 只有 Freeing initrd memory),所以运行时无法导出 second,只能从原厂 boot.img 里按偏移切出。

5.7 从运行系统重建 ramdisk

原始 boot.img 中的 kernel 和 ramdisk 数据被 Amlogic 私有封装或加密,标准工具无法直接解包。但魔盒能正常启动,说明内核已经成功解压了 ramdisk,运行系统里的根文件系统就是 ramdisk 的内容。

重建 ramdisk 的完整流程如下。

前置确认

id
ls -la /init
ls -la /sbin
mount | grep -E '/system|/sbin|rootfs'

确认 root 权限、/init 是 ELF、/sbin 内容正常、rootfs 已挂载。

准备临时根目录

U 盘通常是 fuseblk(NTFS/exFAT),不支持符号链接,直接在 U 盘上 cp -a /sbin 会报 cp: symlink: ../init: Function not implemented。因此改为在 /data(ext4)下操作,最后只把生成的 cpio 文件复制到 U 盘:

rm -rf /data/local/rd_root
mkdir -p /data/local/rd_root
cd /data/local/rd_root

cp -f /init /init.rc /init.amlogic.rc /init.amlogic.board.rc /init.amlogic.usb.rc \
      /init.environ.rc /init.trace.rc /init.usb.rc /init.yunos.rc \
      /default.prop /file_contexts /file_manifest /file_whitelist \
      /property_contexts /restart_info.png /restore_info.rle . 2>&1

mkdir -p sbin
cd /sbin
find . \( -type f -o -type l \) | while read f; do
  mkdir -p "/data/local/rd_root/sbin/$(dirname "$f")"
  cp -a "$f" "/data/local/rd_root/sbin/$f" 2>/dev/null
done
ls -la /data/local/rd_root/sbin/

预期看到 adbd、alitvsecureboot、healthd,以及 ueventd -> ../init、watchdogd -> ../init 两个软链接。

一次性 cpio 打包

cd /data/local/rd_root
find . | cpio -o -H newc 2>/dev/null | gzip > /data/local/ramdisk_new.cpio.gz
ls -lh /data/local/ramdisk_new.cpio.gz

cp /data/local/ramdisk_new.cpio.gz /storage/external_storage/sda1/
ls -lh /storage/external_storage/sda1/ramdisk_new.cpio.gz

预期文件大小在 1–3 MB 量级,接近原厂 ramdisk 的 2.48 MB。

校验归档内容

gunzip -c /data/local/ramdisk_new.cpio.gz | cpio -it 2>/dev/null | grep sbin

预期看到 ./sbin/adbd、./sbin/alitvsecureboot、./sbin/healthd、./sbin/ueventd、./sbin/watchdogd。

拉到电脑

adb pull /storage/external_storage/sda1/ramdisk_new.cpio.gz

5.8 重打包 boot.img

用系统 apt 安装的 mkbootimg 重打包。注意 Debian 源里的 mkbootimg(29.0.6)使用 --base + --kernel_offset 风格,不支持直接指定 --kernel_addr 这类绝对地址:

mkbootimg \
  --kernel ~/boot_unpacked/zImage \
  --ramdisk /path/to/ramdisk_new.cpio.gz \
  --second ~/boot_mkboot/second.img \
  --base 0x10000000 \
  --kernel_offset 0x00008000 \
  --ramdisk_offset 0x01000000 \
  --second_offset 0x00f00000 \
  --tags_offset 0x00000100 \
  --pagesize 2048 \
  -o boot_new.img

生成后校验:

file boot_new.img
abootimg -i boot_new.img

file 应显示 Android bootimg,abootimg -i 里 kernel size 应为 7813310,ramdisk size 应与 ramdisk_new.cpio.gz 大小一致。

打包结果确认:boot_new.img 生成后,file 和 abootimg -i 输出如下:

boot_new.img: Android bootimg, kernel (0x10008000), ramdisk (0x11000000), second stage (0x10f00000), page size: 2048
  • kernel size 7813310,与原厂一致;
  • ramdisk size 1334740(约 1.27 MB),对应新生成的 ramdisk_new.cpio.gz;
  • abootimg -i 报告的 second stage size 1334740 是已知误读(把 ramdisk size 读成了 second size),实际 second 数据是打包时传入的 42 KB second.img。以 file 输出为准。

ramdisk 从原厂 2.48 MB 缩小到 1.27 MB,是正常现象:原厂 ramdisk 是加密/私有封装格式且包含 Widevine、蓝牙、证书等组件,新 ramdisk 只包含 init 脚本和 /sbin,标准 gzip cpio 压缩后体积更小。

工具职责区分:Debian apt 安装的 mkbootimg 只负责打包,不包含可用的解包功能。解包 boot.img 仍需使用 mkboot(mkbootimg_tools)或 abootimg。虽然 mkboot 解压 ramdisk 会报错,但它切分 kernel、second 的功能是正常的,且与 Python 独立切分结果一致。流程是:mkboot 解包 → 替换 ramdisk → mkbootimg 打包。

mkbootimg_tools 的兼容性:该工具目录下的 mkbootimg 是 2016 年编译的 32 位 x86 ELF 二进制,依赖 /lib/ld-linux.so.2。在 64 位 Debian 12 上直接执行会报“无法执行:找不到需要的文件”。如果确实要用,需先 sudo dpkg --add-architecture i386 && sudo apt install libc6:i386,但旧二进制对新版 glibc 的兼容性不保证。推荐直接使用系统 apt 安装的 mkbootimg(29.0.6)。

5.9 刷入 boot_new.img 的实测结果:签名校验拦截

通过 update.exe(Amlogic USB 烧录工具的命令行版)将 boot_new.img 写入 boot 分区。烧录过程成功:

update.exe partition boot boot_new.img
file size is 0x8c5000
Downloading....
[update]:mwrite success

串口同步显示 Burn complete。但设备重启后,U-Boot 拒绝加载新 boot:

enter storeboot...
Booting...
 error: image data is not a fdt
key_unify_init:362,unify key config table parse fail
Err key(L48):fail in key_drv_init
Aml log : M8-RSA-1024
Aml log : ERROR! Image secure check fail!
Error! Illegal kernel image, please check!
handle_error_proc, bootm_mode="boot"
show error logo...
show_booterror_logo, boot_hang="2"

sequenceDiagram participant U as U-Boot participant B as boot 分区 participant S as 签名校验模块 U->>B: imgread kernel boot B-->>U: 返回 bootimg 数据 U->>S: aml_sig_check_buffer S->>S: 查找 Amlogic 私有签名块 alt 签名块存在且有效 S-->>U: 校验通过 U->>U: 加载 kernel 并 bootm else 签名块缺失或无效 S-->>U: ERROR! Image secure check fail! U->>U: show error logo / boot_hang=2 U->>U: 进入 usbburning mode end
图 5-2:签名校验拦截示意

关键结论:

  • Image secure check fail 表明 U-Boot 对 boot 分区内的 kernel 镜像执行了 Amlogic RSA-1024 签名校验,mkbootimg 生成的标准 Android bootimg 没有 Amlogic 私有签名块,被拒绝;
  • keyIsBurned=0 只说明 efuse 中未烧录设备密钥,不代表签名校验被关闭。U-Boot 仍然使用内置公钥校验镜像签名;
  • 之前“efuse 未烧录密钥可能允许

flowchart LR A["TTL 串口
root shell dd"] --> C["boot.img 备份 A"] B["update.exe mread
USB 烧录模式"] --> D["boot.img 备份 B"] C --> E{"哈希比对"} D --> E E -->|一致| F["交叉验证通过
备份可信"] E -->|不一致| G["排查偏移或分区"] style F fill:#2a9d8f,color:#fff style E fill:#f9c74f,stroke:#333,stroke-width:2px
图 5-3:两条备份路径交叉验证

Avatar
zfkun 喜欢游戏、热爱技术、追求艺术、崇尚自由、渴望精彩、最爱唠叨