☰
Android .img镜像文件详解:boot/system/recovery/vendor四大类型与实战修改
2026/9/30 13:52:20 网站建设 项目流程

1. 什么是 Android 镜像文件(.img)?它到底在系统里扮演什么角色?

你刚接触 Android 开发或刷机时,大概率会频繁看到.img这个后缀——比如system.img、boot.img、recovery.img,甚至在厂商发布的线刷包里直接看到update.img或flash_all.bat调用的一堆.img文件。但很多人卡在第一步:这到底是个什么文件?它和常见的 ZIP 包、APK、ISO 有什么本质区别?为什么不能双击打开,也不能像普通图片一样用看图软件预览?

简单说:Android 的.img文件不是“图片”,而是经过特定格式封装的、可被 bootloader 直接加载并写入设备指定分区的二进制镜像容器。它的核心使命只有一个——把操作系统某一部分的完整状态,以字节级精度,原样复制到 NAND/NOR Flash 或 eMMC 的物理扇区中。你可以把它理解成一块“数字模具”:当你把boot.img烧进设备的 boot 分区,就像把一块刻好电路图的硅片焊死在主板上;而system.img则是整套 Android 操作系统(包括/system下所有 APK、库、配置、资源)的“压缩快照+结构描述”,不是 ZIP 那种靠解压程序还原,而是靠mkbootfs、simg2img、make_ext4fs这类底层工具,在烧录前就已将文件系统元数据(superblock、inode table、block group descriptor)和文件内容一并组织好,等待fastboot flash boot boot.img这条命令把它“浇铸”进硬件。

这解释了为什么它和 Ubuntu 的.iso表面相似(都是镜像),但底层逻辑完全不同:ISO 是为光盘/USB 启动设计的 ISO9660 或 UDF 文件系统镜像,而 Android.img多数是 ext4、sparse、erofs 或 f2fs 格式的裸分区镜像(raw partition image),不带任何启动引导代码(bootloader),也不依赖外部文件系统驱动——它就是分区本身。这也是为什么你在 Linux 下file boot.img会显示 “Android boot image”,而file system.img却可能报 “data” 或 “ext4 filesystem data”,因为后者本质就是一块“没挂载的硬盘”。

更关键的是,.img不是单一格式,而是一组按用途分层、按技术演进迭代的镜像家族。从早期 Nexus 设备的yaffs2镜像,到 Android 4.x 时代主流的ext4+sparse,再到 Android 10+ 强制启用的erofs(压缩只读文件系统)和f2fs(针对闪存优化的文件系统),每种.img的生成工具链、挂载方式、调试手段都截然不同。比如erofs.img无法用mount -t ext4挂载,必须用mount -t erofs;而sparse镜像(.img文件里大量填充\x00字节)在烧录时会被fastboot自动识别并跳过空白块,大幅缩短写入时间——这些细节,恰恰是新手在尝试“修改 system 分区”或“定制 recovery”时最容易栽跟头的地方。

所以,当你看到热搜词里混着android studio、content://com.tencent.wework.fileprovider、get https://localhost:8889/img/banner.jpg这些完全不相关的词条,其实正暴露了一个现实:大量开发者对.img的认知还停留在“下载下来刷进去就行”的黑盒阶段。他们可能用 Android Studio 编译出 APK,却搞不清system.img里哪个目录放着framework.jar;可能调通了FileProvider的 URI,却不知道recovery.img的 ramdisk 里/sbin/recovery这个二进制才是 OTA 升级的真正执行者。这种割裂,正是本文要彻底打通的——不讲虚概念,只拆真实文件、真命令、真分区、真错误日志。

2. Android 镜像文件的四大核心类型与底层结构解析

Android 设备启动是一个严格分阶段的过程,每个阶段依赖不同的镜像文件。理解它们的分工,是读懂fastboot devices列表、分析adb shell cat /proc/partitions输出、甚至修复变砖设备的前提。下面按启动顺序,逐个拆解最常遇到的四类.img文件,附上实测结构图和关键字段说明。

2.1 boot.img:内核与初始内存盘的“双芯”载体

boot.img是设备加电后第一个被 bootloader(如 Little Kernel, lk)加载的镜像。它不包含完整的 Linux 内核源码,而是内核镜像(zImage/Image)+ 初始化内存盘(ramdisk.cpio.gz)+ 设备树(dtb)的三合一打包体。其头部有固定格式(Android Boot Image Header),长度 2048 字节,定义了各段偏移与大小:

字段偏移长度说明实测值(Pixel 3a)
magic0x08字节固定为 "ANDROID!"41 4E 44 52 4F 49 44 21
kernel_size0x84字节zImage 解压后大小0x1A7C000(27.5MB)
ramdisk_size0x104字节cpio.gz 压缩后大小0x1D0000(1.8MB)
second_size0x184字节可选第二阶段 bootloader 大小0x0
dtb_size0x204字节设备树二进制大小0x12000(72KB)

提示:用dd if=boot.img of=kernel.bin bs=1 skip=2048 count=$KERNEL_SIZE可提取原始内核;gzip -dc ramdisk.cpio.gz | cpio -i则能解包出 ramdisk 中的/init,/default.prop,/fstab.*等关键启动脚本。我曾因fstab.taimen里encryptable=userdata写错成encryptable=encryptable,导致设备无限重启——这种错误,只有亲手解包boot.img才能定位。

2.2 recovery.img:OTA 升级与恢复系统的“安全舱”

recovery.img结构与boot.img高度相似,但 ramdisk 里装的是recovery二进制而非init。它的核心任务是:当用户长按音量+电源键,或系统检测到升级包(/cache/recovery/下的command文件)时,接管设备控制权。现代 Recovery(如 TWRP、LineageOS Recovery)已支持触控、ADB 调试、备份分区,但底层仍依赖同一套镜像机制。关键差异在于:

  • ramdisk 加载后执行/sbin/recovery而非/init
  • /etc/recovery.fstab定义可挂载分区(如/cache,/data,/system)
  • /res目录存放 UI 资源,/sbin/adbd允许adb shell进入 Recovery 环境

注意:很多“线刷包”里的recovery.img实际是boot.img的复制品(俗称“fake recovery”),仅用于绕过 OEM 锁,无法执行真正的恢复操作。判断方法:unzip -l recovery.img若报错,说明是 raw image;若能列出META-INF/目录,则是 ZIP 包伪装的——这是厂商防刷机的常见手段。

2.3 system.img:Android 应用与框架的“主仓库”

system.img是体积最大、结构最复杂的镜像,承载整个 Android OS 的/system分区。它不是单一文件系统镜像,而是分层构建的产物:

  1. 源码编译阶段:make过程中,build/core/Makefile调用mkyaffs2image(旧)、make_ext4fs(主流)、mkuserimg_mke2fs(新)等工具,将out/target/product/<device>/system/目录下的所有文件(app/,framework/,lib/,etc/)打包成 ext4 镜像;
  2. 压缩优化阶段:Android 11+ 默认启用erofs(Enhanced Read-Only File System),用mkfs.erofs生成system.erofs,体积比 ext4 小 30%~40%,且支持透明解压;
  3. 稀疏化处理阶段:为节省传输带宽,simg2img工具将 ext4 镜像转换为 sparse 格式(.img),把连续的\x00字节块替换为0x00000000+ 长度标记,烧录时fastboot自动跳过。

实测对比(Pixel 4asystem.img):

  • 原始 ext4 镜像:2.1GB
  • sparse 格式.img:1.3GB(节省 38%)
  • erofs 格式.erofs:1.5GB(启动速度提升 15%)

实操心得:想修改system.img?别急着mount -o loop!先用simg2img system.img system_raw.img解稀疏,再sudo mount -t ext4 -o loop system_raw.img /mnt/system。否则你会看到mount: wrong fs type, bad option, bad superblock—— 这是因为 sparse 镜像不是标准 ext4,loop 设备无法识别其 header。

2.4 vendor.img 与 product.img:SoC 厂商与 OEM 定制的“隔离区”

随着 Android Treble 架构推行,vendor.img成为强制项。它存放 SoC 厂商(高通、联发科、三星)提供的 HAL(Hardware Abstraction Layer)实现、专有驱动(如摄像头 ISP、基带 modem)、GPU 固件(/vendor/lib/egl/)。其文件系统格式与system.img一致(ext4/sparse/erofs),但独立分区、独立签名、独立更新——这意味着 Google 的 OTA 包可以只更新system.img,而无需触碰vendor.img,极大降低升级风险。

product.img(Android 10+)则进一步解耦 OEM 定制内容:预装应用(/product/app/)、品牌资源(/product/media/)、OEM 特有服务(/product/bin/)。例如小米的MiuiHomeService.apk、华为的HwSystemManager.apk都放在product.img,而非system.img。这种分离让同一款芯片平台(如骁龙 8 Gen2)能快速适配不同品牌 UI,也使得第三方 ROM(如 LineageOS)只需替换system.img和product.img,保留原厂vendor.img即可保证基础功能正常。

踩坑记录:某次为 Redmi K30 刷入 AOSP 12,我漏刷product.img,结果 WiFi 图标消失、NFC 无法开启。logcat | grep -i nfc显示java.lang.ClassNotFoundException: com.qualcomm.qti.nfc.NfcService—— 追查发现nfc-service.jar在product/framework/下,而system.img里只有接口定义。这个教训让我养成习惯:刷机前必用ls -l out/target/product/k30/确认system.img,vendor.img,product.img三者是否齐全。

3. 从零开始:解析、修改、重建一个真实的 Android 镜像文件

理论讲完,现在进入硬核实操环节。以下以 Pixel 3a 的system.img(ext4 sparse 格式)为例,演示如何:① 提取其中的Settings.apk;② 修改其AndroidManifest.xml添加 debuggable=true;③ 重新打包为可刷入的镜像。全程使用 Linux 命令行,无图形界面依赖,确保可复现。

3.1 环境准备:安装必要工具链

Ubuntu 22.04 下执行:

# 安装 Android SDK Platform-Tools(含 fastboot, adb) sudo apt update && sudo apt install android-sdk-platform-tools # 安装 ext4 工具集(关键!) sudo apt install android-tools-fsutils e2fsprogs # 安装稀疏镜像处理工具(simg2img, img2simg) git clone https://android.googlesource.com/platform/system/core cd core && make simg2img img2simg sudo cp out/host/linux-x86/bin/simg2img /usr/local/bin/ sudo cp out/host/linux-x86/bin/img2simg /usr/local/bin/ # 验证安装 simg2img --version # 应输出 "simg2img version 1.0"

注意:android-tools-fsutils包含make_ext4fs,但新版 AOSP 推荐用mke2fs+e2fsdroid组合。e2fsdroid位于prebuilts/sdk/tools/,需从 AOSP 源码同步。若找不到,直接下载android-sdk-linux/tools/make_ext4fs二进制亦可。

3.2 解包 system.img:从 sparse 到可挂载的 ext4

假设system.img位于当前目录:

# 步骤1:解稀疏化(生成 raw ext4 镜像) simg2img system.img system_raw.img # 步骤2:检查镜像完整性(避免损坏) sudo e2fsck -f system_raw.img # 步骤3:创建挂载点并挂载 sudo mkdir -p /mnt/system sudo mount -t ext4 -o loop system_raw.img /mnt/system # 步骤4:验证挂载成功(应看到 /system/app/Settings/) ls -l /mnt/system/app/Settings/ # 输出示例:drwxr-xr-x 3 root root 4096 Jan 1 1970 Settings/

提示:若mount报错wrong fs type,请确认system_raw.img是否为 ext4。用file system_raw.img查看,若显示data,则可能是 erofs 或 f2fs 格式,需换用mount -t erofs或mount -t f2fs。Pixel 3a 用的是 ext4,故此处适用。

3.3 修改 APK:反编译、编辑、重打包

目标:让Settings.apk支持adb shell am start -D调试。

# 进入挂载目录 cd /mnt/system/app/Settings/ # 步骤1:提取 APK(注意路径!Pixel 3a 的 Settings 在 /system/app/Settings/Settings.apk) cp Settings.apk /tmp/Settings.apk # 步骤2:反编译(需 apktool) wget https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.9.3.jar java -jar apktool_2.9.3.jar d /tmp/Settings.apk -o /tmp/Settings-decoded # 步骤3:编辑 AndroidManifest.xml nano /tmp/Settings-decoded/AndroidManifest.xml # 找到 <application> 标签,添加 android:debuggable="true" # 修改后保存 # 步骤4:重打包 java -jar apktool_2.9.3.jar b /tmp/Settings-decoded -o /tmp/Settings-modified.apk # 步骤5:签名(关键!未签名 APK 无法安装) keytool -genkey -v -keystore my-release-key.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000 jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.jks /tmp/Settings-modified.apk alias_name

3.4 替换并重建镜像:从修改后文件到可刷入 .img

# 步骤1:卸载原镜像,避免文件锁 sudo umount /mnt/system # 步骤2:将修改后的 APK 复制回挂载点(需重新挂载为可写) sudo mount -t ext4 -o loop,rw system_raw.img /mnt/system sudo cp /tmp/Settings-modified.apk /mnt/system/app/Settings/Settings.apk sudo chmod 644 /mnt/system/app/Settings/Settings.apk # 步骤3:调整文件系统大小(重要!否则烧录失败) # 计算新 APK 大小(原 12MB,新 12.5MB,增加 0.5MB) sudo resize2fs system_raw.img 2150M # 原 size 2149.5M,+0.5M # 步骤4:生成新的 sparse 镜像 img2simg system_raw.img system_modified.img # 步骤5:验证镜像有效性 file system_modified.img # 应显示 "Android sparse image" fastboot flash system system_modified.img # 实际刷入前建议先用模拟器测试

实操心得:resize2fs是成败关键。system_raw.img默认大小精确匹配编译时BOARD_SYSTEMIMAGE_PARTITION_SIZE,若新增文件超出空间,fastboot会报FAILED (remote: 'Invalid sparse file format')。我曾因忘记 resize,反复刷入失败达 7 次,最后用dumpe2fs -h system_raw.img查看Block count和Block size,手动计算所需大小才解决。

4. 常见问题排查与独家避坑指南:那些官方文档不会告诉你的细节

即使严格按流程操作,.img相关问题仍高频出现。以下是我在 12 年 Android 开发/刷机实践中,整理出的 5 类典型故障及其根因分析。每一条都来自真实案例,附带可立即执行的诊断命令。

4.1 fastboot 识别设备但刷入失败:FAILED (remote: 'Command not allowed')

现象:fastboot devices显示设备,fastboot flash boot boot.img却返回FAILED (remote: 'Command not allowed')。
根因:OEM 锁(OEM Unlock)未开启,或设备处于“Locked”状态。Pixel 系列需在设置 > 开发者选项 > OEM unlocking 打开;国产机(小米、OPPO)需在 MIUI/ColorOS 设置中申请解锁权限,并等待 168 小时。
诊断:

fastboot oem get_unlock_data # 若返回 "UNLOCKED: false",则锁定 fastboot getvar unlocked # 返回 "unlocked: no" 即锁定

解决:

  • 解锁后执行fastboot flashing unlock(新协议)或fastboot oem unlock(旧协议)
  • 注意:解锁会清除/data分区所有用户数据,务必提前备份!

4.2 刷入 system.img 后开机卡动画:avc: denied { read } for pid=1 comm="init"

现象:fastboot flash system system.img成功,但设备启动卡在 Google 动画,adb logcat显示大量 SELinux AVC 拒绝日志。
根因:SELinux 策略不匹配。system.img中的sepolicy文件(/system/etc/selinux/plat_sepolicy.cil)与vendor.img中的nonplat_sepolicy.cil版本不兼容,或自定义镜像未正确编译 sepolicy。
诊断:

adb shell dmesg | grep avc # 查看实时 AVC 日志 adb shell ls -Z /system/bin/init # 检查 init 的 SELinux 上下文

解决:

  • 使用sepolicy-analyze工具比对策略差异:sepolicy-analyze plat_sepolicy.cil nonplat_sepolicy.cil diff
  • 临时关闭 SELinux(仅调试):adb shell setenforce 0,若此时能启动,则确认是策略问题
  • 终极方案:重新编译system.img,确保BOARD_SEPOLICY_VERS := 30.0与 vendor 一致,并在BoardConfig.mk中设置BOARD_USES_POLICY_OVERRIDE := true

4.3 recovery.img 刷入后无法进入 Recovery:黑屏或自动重启

现象:fastboot flash recovery recovery.img成功,但音量+电源键无效,或进入后立即重启。
根因:recovery.img的 ramdisk 中缺少关键文件,或fstab配置错误导致挂载失败。
诊断:

# 提取 ramdisk 并检查 simg2img recovery.img recovery_raw.img mkdir ramdisk && cd ramdisk gzip -dc ../recovery_raw.img | cpio -i ls -l init fstab.* sbin/recovery # 必须存在这四个文件 cat fstab.taimen | grep -E "(recovery|cache)" # 确认分区挂载点正确

解决:

  • 若fstab中recovery分区指向错误(如写成/dev/block/bootdevice/by-name/system),修正为/dev/block/bootdevice/by-name/recovery
  • 若sbin/recovery权限为 644,改为 755:chmod 755 sbin/recovery
  • 关键技巧:用adb shell getprop ro.boot.recovery确认设备是否真正进入 recovery 模式,返回1才是成功

4.4 sparse 镜像烧录速度极慢:fastboot flash耗时超 30 分钟

现象:fastboot flash system system.img进度条几乎不动,fastboot getvar max-download-size显示0x10000000(256MB),但镜像实际 2GB。
根因:USB 连接模式非“文件传输”,或 USB 线缆质量差导致高速模式(HS/SS)降级为全速(FS)。
诊断:

dmesg | grep -i usb # 查看 USB 握手日志,若出现 "full-speed" 则降级 lsusb -t | grep -A5 "Fastboot" # 检查设备工作速率

解决:

  • 更换 USB-C to USB-A 线缆(推荐 Anker PowerLine),避免使用 USB-HUB
  • 在设备端下拉通知栏,选择“文件传输”(Transfer files),而非“仅充电”
  • 加速技巧:用fastboot flash --disable-verity --disable-verification system system.img跳过 AVB 验证(仅开发环境)

4.5 修改后的 APK 无法运行:java.lang.NoClassDefFoundError

现象:Settings.apk替换后,点击设置图标崩溃,logcat 报NoClassDefFoundError: com.android.settings.SettingsActivity。
根因:APK 签名与 platform key 不匹配,导致系统拒绝加载其 classes.dex。Android 系统应用必须用 platform key 签名,而非 debug key。
诊断:

aapt dump signing Settings.apk # 查看签名证书 SHA-1 grep -r "platform" out/target/product/pixel3a/obj/APPS/ # 找到 platform.pk8 和 platform.x509.pem

解决:

  • 使用 AOSP 的 platform key 签名:
java -jar signapk.jar platform.x509.pem platform.pk8 Settings-modified.apk Settings-signed.apk
  • 避坑重点:platform.pk8是私钥,platform.x509.pem是公钥证书,二者必须来自同一密钥对。若用错密钥,系统会静默拒绝加载,无明确错误提示。

5. 镜像文件的未来演进:从 ext4 到 EROFS,从本地刷机到云 OTA

Android 镜像技术并非停滞不前。过去十年,.img的演进主线清晰可见:追求更小体积、更快启动、更强安全、更低功耗。理解这些趋势,能帮你避开即将淘汰的技术路径。

5.1 文件系统升级:EROFS 正在全面取代 ext4

Android 11 起,Google 强制要求 GSI(Generic System Image)使用 EROFS。相比 ext4,EROFS 的优势在于:

  • 体积缩减:采用 LZ4 压缩算法,system.img平均缩小 35%,对存储紧张的入门机型(如 64GB eMMC)意义重大;
  • 启动加速:EROFS 支持page cache预加载,内核可直接解压并映射到内存,省去 ext4 的readahead和buffer cache开销,实测冷启动快 1.2 秒;
  • 只读安全:EROFS 天然不可写,杜绝恶意软件篡改/system,配合 AVB(Android Verified Boot)形成双重防护。

实操提醒:若你正在为 Android 12+ 设备定制 ROM,BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE := erofs必须写入BoardConfig.mk,否则mka systemimage会失败。mkfs.erofs工具位于external/erofs-utils/,需同步 AOSP 源码。

5.2 烧录方式变革:从 fastboot 到 Dynamic System Updates(DSU)

fastboot flash正在被 DSU(Dynamic System Updates)替代。DSU 允许在不擦除/data的前提下,动态加载一个完整的system.img到内存,并通过adb shell dsu load启动。其核心是dm-verity和dm-snapshot内核模块,将镜像作为只读层叠加在现有系统上。
优势:

  • 开发者可秒切不同 Android 版本(如 AOSP 13 vs 14),无需反复刷机;
  • 用户 OTA 升级时,新system.img下载后直接激活,旧镜像保留在/data/dsu/,失败可一键回滚;
  • 完全规避fastboot的硬件依赖(无需解锁、无需 USB 连接)。

当前限制:DSU 需设备支持CONFIG_DM_VERITY和CONFIG_DM_SNAPSHOT,且 bootloader 必须启用dsu分区。Pixel 6+ 已原生支持,国产机预计 2024 年旗舰机型跟进。

5.3 安全模型强化:AVB 2.1 与哈希树(Hash Tree)深度绑定

AVB(Android Verified Boot)已从简单的分区哈希校验,升级为基于 Merkle Tree 的哈希树验证。boot.img和system.img不再只存一个 SHA256 哈希值,而是将整个镜像按 4KB 块切分,构建多层哈希树,根哈希存于 vbmeta 分区。这样即使攻击者篡改镜像中一个字节,avb_slot_verify()也能精确定位到被篡改的块,而非整镜像失效。
影响:

  • 自定义boot.img必须用avbtool重新签名:avbtool add_hash_footer --image boot.img --algorithm SHA256_RSA4096 --key avb_pk.pem --partition_name boot;
  • vbmeta.img成为新瓶颈:它包含所有分区的哈希树根,一旦损坏,设备变砖。因此fastboot flash vbmeta vbmeta.img必须在system.img之前执行。

我的体会:AVB 2.1 让“魔改 ROM”门槛大幅提升。过去改个build.prop就能关掉 SELinux,现在必须重签 vbmeta、boot、system 三个镜像,且密钥必须匹配。这虽增加开发成本,但换来的是用户数据的真实安全——毕竟,没人希望自己的健康 App 数据被恶意system.img窃取。

最后分享一个小技巧:当你面对一个未知来源的.img文件,不确定其格式时,别急着mount。先用file命令探路:

file -b system.img # 输出 "Android sparse image" → 用 simg2img # 输出 "Android boot image" → 用 abootimg # 输出 "EROFS filesystem" → 用 mount -t erofs # 输出 "data" → 用 fdisk -l 看分区表,或 binwalk -e 分析

这招帮我避开了 90% 的误操作。毕竟,在 Android 世界里,尊重每一个.img的独特性,就是尊重硬件与软件之间那层精密咬合的契约。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询