[Linux系统工具] 剖析Android super.img:从稀疏镜像到分区解包
1. 理解Android super.img的基本概念
第一次接触super.img是在去年给一台Pixel设备刷机的时候。当时按照老习惯去找system.img文件,结果发现整个刷机包里压根没有这个文件——取而代之的是一个体积巨大的super.img。这让我意识到,Android的动态分区机制已经彻底改变了传统的分区结构。
super.img本质上是一个稀疏镜像容器,它把原本独立的system、vendor、product等分区打包成一个整体。这种设计从Android 10开始引入,到Android 11已经成为标配。我后来在AOSP的文档里查到,谷歌这么做的初衷是为了解决OTA更新时的分区大小限制问题。想象一下你的手机就像一栋公寓楼,以前每个住户(分区)都有自己的固定房间,现在改成了共享办公空间,可以根据需要动态调整各分区的大小。
稀疏镜像(sparse image)是Android特有的镜像格式,它最大的特点就是会自动跳过全零的数据块。我做过一个测试:一个1GB的raw镜像转换成sparse格式后,实际文件大小可能只有300MB左右。这种压缩方式在传输和存储时特别有用,但代价就是我们不能直接用mount命令挂载查看内容。
2. 准备工作:工具获取与环境搭建
2.1 必备工具清单
在我的工具链里,处理super.img主要依赖两个核心工具:
- simg2img:负责将稀疏镜像转换为可挂载的raw镜像
- lpunpack:专门用于解包super分区
在Ubuntu 20.04上安装这些工具特别简单:
sudo apt update sudo apt install android-sdk-libsparse-utils android-sdk-ext4-utils如果apt源里没有(比如某些CentOS服务器),可以直接从AOSP源码编译:
git clone https://android.googlesource.com/platform/system/extras cd extras/libsparse make -j82.2 常见环境问题排查
第一次在阿里云的ECS上操作时,遇到了lpunpack: command not found的报错。后来发现这个工具其实藏在AOSP的输出目录里:
# 典型路径示例 out/host/linux-x86/bin/lpunpack如果手头没有完整的编译环境,也可以直接下载预编译好的二进制文件。我在GitHub上找到过一个现成的工具包,包含Windows和Linux版本的所有必要工具。不过要注意检查文件的SHA256校验值,避免安全问题。
3. 镜像格式转换实战
3.1 识别镜像类型
拿到一个super.img文件,首先要确认它的实际格式。我常用的方法是:
file super.img典型的输出会有两种:
- Android sparse image:需要先用simg2img转换
- Linux rev 1.0 ext4 filesystem data:已经是raw格式,可以直接处理
最近遇到一个坑是某些厂商会自定义镜像头信息,导致file命令识别错误。这时候可以用hexdump看文件头:
hexdump -C -n 16 super.img真正的sparse镜像开头应该是0x3AFF26ED这个魔数。
3.2 转换稀疏镜像
转换命令本身很简单:
simg2img super.img super_raw.img但有几个实际使用中的细节需要注意:
- 磁盘空间:转换后的raw镜像可能是原文件的3-4倍大
- 权限问题:在Windows子系统(WSL)里操作时要注意文件权限
- 进度显示:大文件转换时可以用pv命令显示进度:
pv super.img | simg2img > super_raw.img
我曾经处理过一个32GB的super.img,转换过程花了近20分钟。这时候建议用nohup放到后台运行,避免SSH超时中断。
4. 解包super分区全流程
4.1 基础解包操作
解包命令的语法很直观:
lpunpack super_raw.img output_dir/但实际操作中我发现几个关键点:
- 输出目录必须为空:否则会报"failed to create ext4 image"错误
- 需要root权限:特别是在操作/dev/loop设备时
- 版本兼容性:Android 10和11的super.img结构有差异
4.2 解包结果分析
以Android 11为例,典型的解包结果是这样的:
-rw-r--r-- 1 user user 1.2G system_a.img -rw-r--r-- 1 user user 0B system_b.img -rw-r--r-- 1 user user 450M vendor_a.img -rw-r--r-- 1 user user 220M product_a.img -rw-r--r-- 1 user user 120M odm_a.img这里有个有趣的现象:所有带_b后缀的分区都是空文件。这是因为当前设备使用的是A/B无缝更新方案中的A槽。我在小米的设备上还见到过system_ext这样的新分区,这是Android 11新增的。
4.3 挂载查看分区内容
解包得到的img文件基本都是标准的ext4格式:
mkdir system_mount sudo mount system_a.img system_mount -o loop不过要注意,某些厂商可能会用erofs替代ext4。这种情况可以用:
fuse.erofs system_a.img system_mount5. 版本差异与厂商定制
5.1 Android 10 vs 11对比
通过对比多个版本,我发现这些关键变化:
- 分区数量:Android 10通常只有system/vendor/product,11增加了system_ext和odm
- 大小分配:11的system分区明显变小,部分内容移到了system_ext
- 文件系统:11开始推广erofs只读文件系统
5.2 厂商魔改问题
处理过一加和小米的ROM后,我总结出这些经验:
- 一加:喜欢在super.img里放大量无用填充数据
- 小米:经常修改分区命名规则,比如用
cust代替product - 三星:会在镜像里加入额外的签名区块
有个取巧的方法是用strings命令查找隐藏信息:
strings super.img | grep -i "partition"6. 高级技巧与自动化
6.1 批量处理脚本
我写了个简单的shell脚本来自动化整个流程:
#!/bin/bash set -e INPUT=$1 OUTPUT_DIR=${2:-./output} mkdir -p "$OUTPUT_DIR" raw_img="${OUTPUT_DIR}/super_raw.img" echo "Converting sparse image..." simg2img "$INPUT" "$raw_img" echo "Unpacking partitions..." lpunpack "$raw_img" "$OUTPUT_DIR" echo "Done! Output in $OUTPUT_DIR"6.2 直接提取特定文件
有时候我们只需要分区里的某个文件,可以不用完整解包:
debugfs -R "cat /system/build.prop" system_a.img > build.prop对于erofs格式,可以用:
erofsfuse system_a.img mount_point cp mount_point/build.prop ./ fusermount -u mount_point7. 常见问题解决方案
7.1 解包失败排查
遇到lpunpack报错时,我通常会检查:
- 镜像完整性:
md5sum super_raw.img - 文件系统类型:
blkid super_raw.img - 工具版本:
lpunpack --help看是否支持当前Android版本
7.2 空间不足处理
对于大镜像,可以尝试:
- 使用tmpfs作为临时目录:
mount -t tmpfs tmpfs ./temp - 直接挂载而不解包:
sudo mount -o loop super_raw.img /mnt
7.3 厂商加密处理
某些厂商会加密super.img,这时候需要:
- 找对应的firmware密钥
- 使用
imgdecrypt工具先解密 - 然后用常规流程处理
8. 实际应用场景
上周帮同事分析一个启动问题时,就是通过解包super.img发现vendor分区里多了个冲突的驱动模块。这种案例让我总结出一个排查流程:
- 解包当前系统和上一个正常版本的super.img
- 用diff对比关键分区:
diff -qr system_a/ system_a_old/ - 重点关注
/vendor/lib/modules和/system/etc目录
另一个实用技巧是修改分区内容后重新打包。虽然AOSP官方不推荐,但在开发阶段可以这样操作:
make_ext4fs -l 2G new_system.img system/ lpmake --metadata-size 65536 --super-name super --metadata-slots 2 ...