ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

system.img解包修改与重打包实战:Android镜像定制完整指南

system.img解包修改与重打包实战:Android镜像定制完整指南 最近好几个朋友都在折腾第三方ROM包时卡在system.img这一步跑来问我能不能写一篇能落地的教程。说实话这玩意儿初次接触确实劝退命令看着吓人还动不动就在打包环节翻车。但拆过几次之后你会发现它本质上就是一份可以整体挂载的Linux文件系统解压和修改本身并不难难点全在于“改完还能刷回去正常开机”。这篇教程我会把Windows和Linux两条路线一次性讲透从镜像格式识别、解压挂载、文件修改到重新打包、刷回设备每一步都按实战记录来写中间穿插我踩过的坑和排查思路。想自己精简系统应用、改开机动画、研究系统内部结构的同学这篇可以直接照着操作。1. 拆system.img前先搞清楚你面对的是个什么文件1.1 system.img到底是干什么用的Android设备内部存储会被划分成多个分区每个分区各管一摊事情。system分区就是存放操作系统主躯体的地方包括Android框架层代码、系统应用、核心库文件、字体、系统铃声、权限配置等。system.img就是整个/system目录的完整镜像文件相当于把一台服务器的系统盘整体打包成了一个文件。刷机时fastboot flash system system.img就是把这个镜像原样写入设备的system分区。可以把它理解成一个特殊格式的压缩包但它不像zip那样随便就能解开因为它内部是一个完整的Linux文件系统默认是ext4格式新设备上越来越多地采用erofs等只读文件系统。它里面的权限、属主、SELinux安全上下文信息都极其敏感改错一个字段轻则某个应用闪退重则直接开不了机。网上下载的第三方ROM包zip格式解开之后你会发现里面有system.img、boot.img、vendor.img等一堆镜像文件。boot.img管内核和启动流程vendor.img管硬件厂商相关代码而我们常说的“精简系统”“去掉预装应用”“改默认桌面”绝大多数要动的地方都在system.img里。1.2 什么情况下你才会想起去改它日常使用中你基本不会碰system.img因为它属于只读分区普通用户没有写入权限。但遇到下面这些需求你就不得不把它掏出来精简系统去掉运营商预装应用、厂商全家桶这些应用很多存在system/app或system/priv-app目录下。修改系统级配置比如改build.prop里的型号名称、DPI密度、默认时区、开机动画里的bootanimation.zip。替换系统应用把某个系统应用换成同样包名的修改版或者往priv-app里塞需要系统权限的工具。移植和逆向研究研究某款系统的内置功能、从别的ROM里提取某个应用或库文件放进当前系统。学习文件系统结构想彻底搞明白Android系统启动后加载了哪些东西。需要注意的是现在很多新手机对system分区有完整性校验Android Verified Boot简称AVB系统启动时会验证镜像的数字签名。如果你的设备bootloader已经解锁通常可以通过fastboot命令附带--disable-verity --disable-verification参数关闭校验或者直接使用userdebug版本的固件否则就算你成功修改了镜像刷进去也大概率卡在开机界面。这部分后面我会专门说。2. 不同镜像格式决定解压工具先识别再动手2.1 三种主流格式的识别方法很多教程一上来就教你挂载但忽略了一个前提你手里的system.img到底是什么格式不同格式用的工具完全不同强行操作只会浪费一晚上时间。最常见的是下面三种格式特点常见场景raw ext4最原始的ext4文件系统镜像结构完整老设备、部分卡刷包解包后的镜像sparse ext4带sparse稀疏头部的ext4镜像文件大小更小官方ROM刷机包最常见erofs只读压缩文件系统读取性能好Android 10以后越来越多新设备采用在Linux终端里用一条file命令就能识别file system.img输出大致会是这样Android sparse image, version: 1.0说明是sparse格式。Linux rev 1.0 ext4 filesystem data, UUID...说明是raw ext4格式。erofs filesystem说明是erofs格式。Windows下没有file命令但你可以用Notepad或任何十六进制编辑器打开文件头部看magic字节。ext4的magic是53 eferofs的magic是e0 f5 81 76sparse的magic是3a ff 26 ed。不过说实话逐个看十六进制太麻烦了我更建议Windows用户装一个WSL进去直接执行file命令一下就能判断。2.2 为什么格式判断这么重要因为raw ext4可以直接挂载或解包sparse需要先转换成raw才能操作而erofs用的又是一套完全不同的工具链。如果你把sparse格式直接丢给挂载命令内核会报错找不到文件系统如果你把erofs格式当成ext4去挂载同样会失败。我一开始折腾的时候不知道还有sparse这回事拿一个sparse镜像反复执行mount命令系统一直提示wrong fs type我还以为是镜像文件损坏了重新下载了三遍。后来才明白先做格式识别然后simg2img转成raw一分钟就搞定了。实际上官方ROM包里的system.img基本都是sparse格式因为Google和厂商希望尽量减少刷机包体积。而第三方作者做的精简包有些会直接使用raw格式方便用户修改。所以拿到镜像文件后第一件事永远是file system.img不要跳过。3. Linux平台实操挂载修改的完整流程3.1 搭建Linux侧解压环境如果你本来就是Linux用户这一步最简单。以Ubuntu/Debian为例主要需要这几样工具sudo apt update sudo apt install -y simg2img e2fsprogs erofs-utilssimg2img将sparse镜像转换成raw镜像。e2fsprogs提供mount、fsck、debugfs等ext4文件系统工具。erofs-utils提供fsck.erofs、dump.erofs等erofs工具。有的发行版包名可能不太一样比如simg2img在部分老仓库里叫android-tools-fsutils。如果安装时提示找不到包可以用apt search simg2img或apt search sparse搜一下。如果机器是Arch系用pacman装对应包即可。无论哪个发行版核心原理都一样先把sparse转raw然后挂载到某个目录像操作普通文件夹一样操作里面的文件。3.2 识别、转换并挂载system.img第一步是识别格式第二步是转换如果是sparse第三步是挂载。# 1. 查看文件类型 file system.img # 2. 如果是sparse sparse image转换成raw simg2img system.img system.raw.img file system.raw.img # 确认转换成功 # 3. 挂载raw镜像到目录 sudo mkdir -p /mnt/system sudo mount -o loop system.raw.img /mnt/system # 4. 查看结果 ls /mnt/system关键点在于mount -o loop里的loop参数它告诉内核把这个普通文件当作块设备来处理。成功挂载后/mnt/system里就会看到熟悉的app、priv-app、framework、build.prop等目录和文件。修改完文件后卸载时记得先确保没有进程占用目录否则会提示target is busysudo umount /mnt/system如果提示busy用lsof /mnt/system看看是哪个进程占用了关掉后再卸载。卸载之后最好跑一遍文件系统检查e2fsck -f system.raw.img这一步能发现你在修改过程中有没有搞坏文件系统结构如果报错建议重新从原始镜像开始不要带着错误继续打包。3.3 修改文件时最容易忽略的权限与上下文问题镜像挂载后你会发现自己用普通用户身份访问时提示权限不足因为Android镜像里的文件属主都是root权限也设置了限制。这时候用sudo操作就行sudo rm -rf /mnt/system/app/SomeBloatware sudo cp /path/to/SystemApp.apk /mnt/system/priv-app/但这里藏着两个大坑新手十有八九都会栽进去。第一个坑是文件权限。你从桌面环境复制进去的文件权限往往继承自你当前的umask比如644甚至666但Android系统应用目录里的文件通常有严格的权限要求。比如priv-app目录权限一般是0755应用文件权限一般要求0644可执行文件要求0755。如果你复制的文件权限不对系统启动时应用会被拒绝加载或者直接导致SystemServer崩溃。第二个坑是SELinux上下文。Android系统的SELinux策略非常严格每个文件都有自己的安全上下文标签比如系统应用的文件应该是u:object_r:system_file:s0可执行文件可能是u:object_r:system_file:s0或更具体的类型。你新复制进去的文件默认上下文很可能完全不对刷机后就会触发avc: denied日志系统表现是各种诡异——应用闪退、服务起不来、设置界面空白。这时候再把镜像挂载回来用ls -Z查看文件上下文发现和周围的文件完全不同就知道问题出在哪了。正确做法是在修改文件时保留原有的属主、权限和SELinux上下文# 先查看原文件的属主、权限、上下文 ls -lZ /mnt/system/priv-app/SystemApp/SystemApp.apk # 复制新文件后手动调整 sudo chown root:root /mnt/system/priv-app/NewApp/NewApp.apk sudo chmod 0755 /mnt/system/priv-app/NewApp/ sudo chmod 0644 /mnt/system/priv-app/NewApp/NewApp.apk sudo chcon -u system_u -r object_r -t system_file /mnt/system/priv-app/NewApp/NewApp.apk如果是替换已有的应用最好的办法是保留原应用的文件权限和上下文不变直接把新APK内容覆盖进去。更多情况下重新打包时会用file_contexts文件来自动设置上下文这个我在第5节展开讲。4. Windows平台实操WSL挂载与纯Windows工具4.1 为什么不推荐直接在Windows上用7-Zip解压很多Windows用户第一反应是用7-Zip直接打开system.img以为像打开zip一样方便。实际上7-Zip对ext4镜像的支持非常有限虽然能列出部分文件结构但会遇到这些问题文件权限、属主信息丢失根本没地方显示。部分ext4特性如UUID、扩展属性、SELinux上下文无法处理。只能解压提取不能修改后重新生成合法的镜像文件。遇到sparse格式或erofs格式时基本无能为力。所以7-Zip只适合你“看一眼”镜像里有什么文件不适合做完整解包和重新打包。真要在Windows上完成“解压→修改→重打包”这个闭环我推荐下面两条路。4.2 方案一WSL里完成整条修改链WSLWindows Subsystem for Linux是微软官方的Windows子系统你不需要装虚拟机或者物理Linux环境直接在Windows里跑一个Ubuntu终端然后Linux能干的活儿它基本都能干。安装完WSL后把system.img放到Windows的某个盘符里比如D:\rom\system.img然后进入WSL终端访问Windows盘符上的文件cd /mnt/d/rom file system.img如果遇到权限问题或者WSL跨文件系统操作太慢更好的做法是把镜像复制到WSL的Linux文件系统里cp /mnt/d/rom/system.img ~/rom/ cd ~/rom/然后按照第3节的步骤执行simg2img、mount、umount就行。修改完的镜像再复制回Windows盘cp ~/rom/system_new.img /mnt/d/rom/WSL方案最舒服的地方在于你不用额外装任何Windows专用软件所有Linux工具都能直接用。而且WSL里跑的是真正的Linux内核挂载ext4、erofs都没有兼容性问题。装WSL2以后性能也足够处理大镜像文件不会卡到怀疑人生。4.3 方案二用第三方驱动把ext4镜像变成Windows盘符不想用WSL的话Windows上也有专门处理Linux文件系统的工具。早期比较流行的是ext2explore但它只能读取不能写入改不了文件。后来像我实际用过比较稳的是Paragon公司的Linux File Systems for Windows装上以后Windows资源管理器里能直接挂载ext4镜像把镜像变成一个虚拟盘符读写都没问题。类似的工具还有DiskInternals Linux Reader但那个偏读取查看写入支持不稳定。这类工具的优点是对Windows用户友好鼠标点一点就能操作文件。但缺点也很明显一是很多工具是付费的免费版功能受限二是它们只解决“读写ext4”的问题不解决“重新打包img”的后续步骤。你改完文件之后还是需要某种方式把整个目录重新制作成镜像这通常还是得回到Linux环境去执行make_ext4fs或mke2fs命令否则只能把修改过文件的raw镜像直接保存。所以我个人还是强烈推荐WSL路线——与其装一堆半吊子的图形工具不如一步到位用WSL闭环解决。4.4 从Windows里识别镜像格式在Windows上执行file命令不方便但如果你装好了WSL这个问题自然就解决了。进入WSL后执行file system.img如果你还没装WSL临时用7-Zip打开镜像看7-Zip能不能识别文件系统结构或者用十六进制工具查看文件头部的magic字节也能有一个初步判断。不过准确率最高的还是file命令因为它会完整解析文件头部信息并匹配既有格式库。5. 重新打包把改完的目录变回可刷入的system.img5.1 打包工具怎么选很多人解压修改完卡在最后一步不知道怎么把目录重新做成镜像。打包工具的选择取决于你原始镜像的格式ext4镜像用make_ext4fs或者更标准的mke2fse2fsdroid。erofs镜像用mkfs.erofs。sparse格式打包成完整raw镜像后再用img2simg转成sparse或者打包时直接加sparse参数。make_ext4fs是老牌工具在Android 4.x到8.x时代非常流行一条命令就能把目录打包成ext4镜像适合快速实验。但它对SELinux上下文处理得不够完善新版Android系统镜像里如果缺少正确的file_contexts刷进去很容易出问题。更标准的方式是使用AOSP工具链里的mke2fse2fsdroid。mke2fs负责创建空的ext4文件系统e2fsdroid负责把目录内容写进镜像并在写的过程中根据file_contexts文件设置每个文件的安全上下文。这个方案在Android 9以上已经成了官方默认做法。5.2 ext4镜像的重新打包过程先用最简单的make_ext4fs法说明思路。假设你已经把修改后的目录放到了/mnt/system_new现在要生成新的镜像文件make_ext4fs -s -l 512M -a system system_new.img /mnt/system_new参数含义-s生成sparse格式镜像。如果你的设备能接受raw格式可以去掉这个参数。-l 512M指定分区大小。这里必须和你原始system分区大小一致或者略小。如果设置得比原始分区大不少刷入时可能超出分区实际容量。-a system指定挂载点同时让工具在打包时按Android的挂载规范设置文件路径。如果使用mke2fse2fsdroid的组合命令会长一些但更标准# 1. 创建一个空的ext4镜像文件 # 512M 是对应分区大小 mke2fs -t ext4 -L system -b 4096 -I 256 -O has_journal system_new.ext4.img 512M # 2. 把目录内容写入镜像并设置SELinux上下文 e2fsdroid -f /mnt/system_new -a /system -S file_contexts system_new.ext4.imgfile_contexts文件通常可以从原ROM包中提取或者从AOSP源码的system/sepolicy目录里找到。如果你手头没有这个文件也可以用原镜像挂载后根据ls -Z导出的信息手工整理一个但这个工作量比较大适合对SELinux机制很清楚的人。最终生成system_new.ext4.img后如果原始镜像本身是sparse格式再用img2simg转换img2simg system_new.ext4.img system_new_sparse.img5.3 erofs镜像的打包示例如果你的设备比较新原始system.img是erofs格式打包工具是mkfs.erofs。erofs的设计目标就是只读文件系统所以打包流程和ext4思路完全不同它不需要走“挂载目录→写入文件→设置上下文”的流程而是直接把指定目录编译成一个压缩只读镜像mkfs.erofs -zlz4hc system_new.img /mnt/system_new常用参数-z lz4hc指定压缩算法erofs常见压缩算法有lz4、lz4hc后者压缩率更高。-E uuid...可以指定UUID但一般不需要。-V显示版本信息。erofs镜像不能像ext4那样随意挂载修改后重新打包因为文件系统的设计初衷就是只读而且它的元数据存储方式更紧凑。修改时通常需要先用fsck.erofs或dump.erofs解析但这方面工具少很多。如果对erofs不熟我会建议优先考虑用安卓解包工具把erofs镜像先转成ext4或者直接解出文件系统目录修改后再用erofs打包或者干脆在打包前把分区格式转成ext4前提是设备内核支持。5.4 打包后验证与刷回设备打包完成不等于万事大吉我强烈建议你先验证再刷机否则很容易白忙活一场。验证分四步确认新镜像文件类型正确file system_new.img。挂载新镜像检查内容sudo mount -o loop system_new.img /mnt/system_check进去看一下文件数量、关键文件是否存在、修改是否生效。卸载后跑一遍文件系统检查e2fsck -f system_new.img如果报错就要重新打包。有条件的话用一个支持动态分区解析的模拟器或twrp里mount试试确认能正常识别。刷回设备的命令是fastboot flash system system_new.img如果设备启用了AVB校验可能还需要同时刷入关闭校验的vbmetafastboot flash vbmeta vbmeta.img --disable-verity --disable-verification注意这条命令会关闭系统的verity校验意味着设备的安全性有一定下降。在这个前提下修改后的系统才更容易启动。还有一条常识性提醒刷机前一定要备份当前系统尤其是Data分区里的照片、聊天记录、应用数据。system分区被刷后是没法直接回滚到原状态的除非你留着原始镜像并重新刷回。我个人的习惯是把原始system.img先复制一份到电脑单独存放改坏了还能随时刷回去。6. 高频踩坑记录与排查思路6.1 常见问题速查表下面这个表格是我折腾过程中遇到最多的几类问题以及对应的排查和解决思路症状可能原因解决办法mount提示wrong fs type镜像可能是sparse格式或erofs格式先执行file识别格式sparse转rawerofs用专用工具处理修改后刷机卡第一屏SELinux上下文丢失或AVB校验失败用file_contexts重新打包关闭verity检查vbmeta系统应用闪退应用权限不对或依赖缺失检查APK权限是否为0644、目录是否为0755确认所需lib已放入对应目录分区空间不够打包失败修改后内容超出了原system分区大小删掉更多无用应用减小打包镜像的-l分区大小打包后刷入提示size too large镜像实际大小超过设备分区确认-l参数指定的分区大小不要超过原始分区容量卸载目录时报target is busy有进程正在访问挂载目录用lsof或fuser找到占用进程并关闭无法用普通用户访问挂载目录ext4镜像里的文件属主是root使用sudo操作或者在挂载时加-o uid你的用户临时改变默认属主Windows下用7-Zip打开但看不到完整文件spase/ext4特有结构不支持不要依赖7-Zip用WSL挂载或专用Linux文件系统工具6.2 几条能救命的实战经验第一尽量使用同一个工具链完成解压和打包。早期精简单system的时候我用Linux挂载解压然后拿到Windows上用工具打包折腾了三天各种启动崩溃后来发现是两套工具对文件属性的理解不一致导致部分文件的归属地和SELinux标签被修改了。同一套工具链比如全部用AOSP工具能最大程度减少这类问题。第二修改system.img的时候尽量用“复制出一个新目录再修改”的思路不要直接在挂载后的原镜像上操作。比如先cp -a把挂载目录整个复制出来然后在副本上改改完再打包。这样就算打包失败原始镜像还是完好的还能从头再来。第三如果修改完后系统能在Recovery模式下正常启动但在正常启动时卡logo优先怀疑SELinux上下文问题。进Recovery挂载system分区用ls -Z对比几个系统应用文件的上下文发现不对就重新从原镜像提取正确的file_contexts再打包一次。大多数莫名其妙的启动问题都能通过这一步解决。第四对于不熟悉的设备不要贪快直接刷入新镜像。先用fastboot boot而不是fastboot flash测试把新镜像临时加载到内存启动不写入分区。不过这个功能不是所有设备都支持支持的设备上非常管用安全系数高很多。第五erofs格式的设备虽然打包比较麻烦但你仍然可以先用simg2img把sparse erofs镜像转成raw erofs镜像然后用erofs工具挂载或解包如果工具不顺手就把镜像丢给fsck.erofs --extract目录这种方式提取文件修改后再整体打包。原理上都绕不开“解出文件目录→修改→重新编译成镜像”这三步工具差异只是路径不同。最后再分享一个小技巧。我实际操作中习惯把整套解包、修改、打包的常用命令写成一个小的shell脚本每次拿到新ROM包就自动帮我完成格式识别、sparse转换、挂载目录创建这几个重复动作。毕竟这活儿偶尔才做一次命令记不全很正常脚本化之后就能稳定复现不用每次重新翻教程。如果你只是想删几个系统应用不考虑这么深的原理直接用magisk模块配合系统less模式也能做到类似效果但想彻底掌控system分区学会手动处理system.img依然是绕不开的一课。
返回列表