ROM解包打包实战:从boot.img到system.img的完整流程
简介一套面向安卓ROM开发者与刷机爱好者的第三方ROM编译辅助工具合集围绕「一键解包/打包」核心能力整合了boot与recovery分解合成、system/ODM/vendor等分区镜像处理、payload.bin与super格式解包、高通机型镜像合并及华为Updata.app/OFP/OZIP格式转换等大量操作操作项高度集成尤其适合需要快速制作、定制或移植第三方ROM又不愿逐条记忆手工命令的中高级玩家使用。7z压缩包约201.29MB共338个文件以exe可执行程序与dll动态库为主另有bat批处理脚本、jar工具、txt说明、sys驱动、inf配置及少量签名密钥类文件功能模块划分清晰可直接调用对应脚本完成内核打包、Recovery打包、镜像格式互转等任务。目前已有4018人学习下载。工具覆盖常见品牌的固件解包与签名加密流程另附开机第一屏logo制作等附加功能建议在非中文路径下运行以避免脚本报错是一套上手即可用的ROM制作工具集。1. ROM解包打包这件事工具选型和使用边界ROM解包打包不是新鲜技能却是做第三方ROM绕不开的基本功。CMCyanogenMod或LineageOS系固件、各家官网放出的全量包、酷安上那些名称五花八门的优化包剥开看都是boot、system、vendor这些img分区按固定规则组成。做第三方ROM、精简系统、预置APK、改开机动画和屏幕密度本质就是把这个结构拆开改完再装回去最后封成别人能直接刷入的zip。但解包不是双击解压那么简单。卡刷包里那个system.img往往是稀疏镜像boot.img带内核、ramdisk和一圈header参数错一步或者动到不该动的权限和SELinux label刷进去就是不开机严重时连rec都要重刷。普通教程只讲“解压出来改完再压回去”不讲工具链选型、分区边界和权限保留这正是大量翻车的根源。这篇笔记写给两类人一是想把官方包改成自己顺手形态的ROM DIY新人二是已经动过手、但总在sparse镜像、签名校验、分区大小上反复折腾的从业者。前提是你手里有一台能进fastboot或TWRP的设备、一根不松动的数据线以及刷坏了能救回来的底气。后文按解包、打包、避坑、验证的顺序走每一步都尽量给到能直接复用的参数。2. 解包从线刷包到可编辑的镜像分区2.1 先分清包类型再选工具拿到包别急着解压先看扩展名和file输出。卡刷包是zip解压后是几个img和META-INF/update脚本线刷包是tar或md5img要fastboot或Odin刷写Android 10之后很多全量OTA是payload.bin直接unzip看不到分区。我就曾经花半天挂载一个sparse镜像报错报得人发懵后来才意识到simg2img跑漏了这就是没先识别格式的代价。工具选型上我的默认清单是unzip和tar干粗活simg2img把sparse转rawpayload-dumper-go处理payload.binunpack_bootimg/mkbootimg管理boot.imgmake_ext4fs重新生成system镜像signapk做卡刷包整体签名。其中unpack_bootimg和mkbootimg要配套使用解包版本和打包版本最好保持同一套避免新版本悄悄改过对齐方式。包类型外表特征预处理目标文件卡刷zipzip含META-INFunzip直接解boot.img / system.img / vendor.img线刷包tartar/md5含imgtar解包boot.img、modem等分区imgpayload.bin二进制头两字节为crpayload-dumper-go导出各分区imgsparse imgfile显示“Android sparse image”simg2img转raw后挂载注意如果file输出里带“Android sparse image”而你直接mount -o loop大概率报wrong fs type。这不是权限问题是镜像没转raw。这也是后面避坑章里排第一位的经典翻车。2.2 解包boot.img和system.img的完整步骤假设你从卡刷包开始。我会在本地建三个目录out放原包解压产物boot放boot.img拆出来的东西system放挂载出来的文件树。mkdir -p ~/rom/{out,boot,system} unzip ~/download/official_cm.zip -d ~/rom/out file ~/rom/out/system.img # 输出示例: Android sparse image, 51240960 bytes in 1000960 blocks ~/bin/simg2img ~/rom/out/system.img ~/rom/out/system_raw.img cd ~/rom/boot unpack_bootimg --boot_img ~/rom/out/boot.img --out . --formatmkbootimg ls -R ~/rom/boot # 预期看到 kernel、ramdisk目录、dtb以及header参数文件逻辑说明unzip只是解开卡刷包外壳img保持原格式simg2img把稀疏表展开成raw ext4展开后文件明显变大属正常unpack_bootimg会读取boot header里的base、pagesize、cmdline等并输出在同目录这些参数在打包时要原样填回。--formatmkbootimg是兼容老包时的常见参数如果你的boot.img来自很老的平台建议加上。挂载system镜像时我一般先只读挂载看结构确认后再重新rw挂载。因为root挂载ext4时即使不写文件atime也可能更新metadata虽然不影响核心功能但会让后续对比镜像时多出没必要的diff。sudo mount -o loop,ro ~/rom/out/system_raw.img ~/rom/system ls -lZ ~/rom/system/build.prop sudo umount ~/rom/system # 确认结构后以rw重新挂载 sudo mount -o loop ~/rom/out/system_raw.img ~/rom/system参数说明loop是使用回环设备挂载img文件ro代表只读避免atime被更新。如果umount时报target is busy通常是终端工作目录还停在该挂载点切到别的目录再umount即可。若你不想用sudo反复挂载也可以把raw镜像解到目录里直接处理但那样SELinux label更容易丢所以我一直保留loop挂载的方式。2.3 特殊包payload.bin和线刷包的处理Android 10之后的官方全量包很多是payload.bin里面按update_engine的格式存储分区解包用payload-dumper-go。完整命令如下。~/bin/payload-dumper-go -o ~/rom/payload_out ~/rom/out/payload.bin # 输出目录里会看到 boot.img、system.img、vendor.img、vbmeta.img 等这个目录下通常还有vbmeta、dtbo这些和启动验证相关的分区。它们一般不改但等会儿重新打包时不能漏漏了在新平台上可能被AVB校验拉去fastboot。这一条我们放在第4章的避坑里细说。老设备的线刷包解开就是tartar -xf ~/rom/smd.tar.md5解出来的img可以直接走boot/unpack流程没有特别差别。唯一要注意的是部分线刷包里的system.img已经是raw不需要simg2imgfile看清楚再动手。tar包里的分区比卡刷包更全radio、modem这些都在但做第三方ROM通常不需要动它们。2.4 权限、属主和SELinux context为什么不能动system分区里的每个文件除了内容还有三层元数据属主/组、mode、SELinux label。CM系包的system/build.prop通常是root:root、0644、u:object_r:system_file:s0。你用宿主机root随便cp -r进去属主可能变成host用户label直接丢失。现在Android默认强制SELinux缺少label的可执行文件和库在init阶段就被拒绝表现为开机动画转几圈后重启。我的规范做法解包后建一个改动日志记录每次chmod/chown/label变更能用sed、perl原地改的不要新建文件必须在目录里新增文件时从旧文件复制属性mkdir -p ~/rom/system/app/MyApp cp -p ~/rom/system/app/LegacyApp/LegacyApp.apk ~/rom/system/app/MyApp/MyApp.apk chmod 644 ~/rom/system/app/MyApp/MyApp.apk chown root:root ~/rom/system/app/MyApp/MyApp.apk ls -lZ ~/rom/system/app/MyApp/参数说明cp -p保留权限、属主、时间戳ls -lZ用来复核SELinux label。看到unknown或问号说明label已经丢别试着手动补回到原始镜像重新解包再跑一遍你的改动脚本这样才可控。一个提高成功率的手段是把改动写成bash脚本每次解包后整段重放。比如build.prop的sed、APK目录的创建、二进制替换都集中在一份脚本里就不容易因为操作顺序不同产生隐性的不一致。我在给旧机型维护包的时候脚本里还会顺手把file_contexts的路径打印出来提醒自己下一步打包要用到。提示解包后的第一件事是先记录原包字段再开始改文件。字段信息是后面打包和排错最大的参照系。3. 打包把改动写回镜像并生成可刷入的ZIP3.1 build.prop调整和预置应用第三方ROM定制里改build.prop的需求非常高频改屏幕密度、机型识别、开发者选项开关给某些应用伪造设备型号。CM系包常见路径是/system/build.prop部分厂商包把属性分开放在/vendor/build.prop两个都要检查。改之前先备份这步成本很低但救过我很多次。另一个隐蔽坑是行尾符。Android属性服务对行尾敏感CRLF会导致一行属性解析异常表现是设置里少一些选项某个属性读出来的值是乱码。我用cat -A来检查cp -p ~/rom/system/build.prop ~/rom/system/build.prop.bak sed -i s/ro.sf.lcd_density480/ro.sf.lcd_density420/ ~/rom/system/build.prop sed -i /^ro.debuggable/d ~/rom/system/build.prop echo ro.debuggable1 ~/rom/system/build.prop cat -A ~/rom/system/build.prop | head -5 # 正常结尾是 $出现 ^M$ 就是CRLF需要去\r sed -i s/\r$// ~/rom/system/build.prop逻辑说明第一条sed替换分辨率密度第二条先删除可能存在的旧ro.debuggable第三条再追加避免出现两个同名属性。cat -A看到$才是LF遇到^M就执行最后的清理。这个操作不会影响其他正常行。预置应用不只是把APK丢进去。经典布局是system/app/应用名/应用名.apk权限目录755、文件644、属主root:root。这个三元组错了包管理服务要么忽略应用要么开机扫描时刷一堆警告。mkdir -p ~/rom/system/app/MyApp cp ~/download/MyApp.apk ~/rom/system/app/MyApp/MyApp.apk chmod 755 ~/rom/system/app/MyApp chmod 644 ~/rom/system/app/MyApp/MyApp.apk chown -R root:root ~/rom/system/app/MyApp如果你要预置的是普通应用签名可以是自己的release签名如果应用声明了sharedUserId“android.uid.system”必须用系统签名重新签否则放进system一样会被kill。这个属于定制进阶预置前用apksigner看一圈manifest更稳。3.2 重新生成boot.img和system.img打包boot.img本质是复现boot header。unpack_bootimg解包时已经把kernel、ramdisk、dtb和其他字段放在目录里我习惯把header字段单独抄到记事本打包时逐项对着填而不是靠模糊记忆。cd ~/rom/boot mkbootimg --kernel kernel \ --ramdisk ramdisk \ --base 0x80000000 \ --pagesize 2048 \ --cmdline androidboot.hardwareqcom ... \ --output ../out/boot_new.img三个关键参数是base、pagesize和cmdline都必须与解包所得完全一致。pagesize最容易被忽略高通平台常见2048或4096写错后内核可以解压但无法正常进入系统而且看起来像随机重启。解包结果里有dtb就加--dtb没有就不加不要自己硬想一个出来。system.img打包我用make_ext4fs。核心是分区长度。从原包分区表拿到system分区合法大小在这个基础上留1%-3%余量别按实际文件大小去给。实际文件大小填进去会造出一个缩水的文件系统部分强验证机型刷完会报分区大小不匹配。du -sh ~/rom/system # 示例输出 1.1G但原分区约2GB make_ext4fs -s -l 2147483648 -a system \ -f ~/rom/file_contexts \ ~/rom/out/system_new.img ~/rom/system参数说明-s生成sparse镜像刷机脚本写起来更快-l指定生成镜像的字节大小-a system表示以/system做挂载锚点配合-f指定的file_contexts生成正确label。很多第三方教程忽略file_contexts导致镜像文件对但label全区缺失刷进去一样翻车。CM系的file_contexts通常出现在解包zip的根目录或vendor/etc目录下找不到就先搜。3.3 updater-script、ZIP结构和签名重打包ZIP时把原META-INF/MANIFEST.MF、CERT.RSA、CERT.SF删掉只保留update-binary和updater-script否则旧签名和新签名会互相打架。updater-script的顺序是format、mount、package_extract_file、unmount。以高通设备的by-name路径为例ui_print(Installing custom system...); format(ext4, EMMC, /dev/block/bootdevice/by-name/system, 0, /system); mount(ext4, EMMC, /dev/block/bootdevice/by-name/system, /system); package_extract_file(system_new.img, /dev/block/bootdevice/by-name/system); package_extract_file(boot_new.img, /dev/block/bootdevice/by-name/boot); unmount(/system);逻辑说明先format再mount保证写入前分区是干净状态package_extract_file把镜像直接写到块设备不走逐文件释放速度快且不易断点。部分rec版本要求不能同时mount和写同一个分区遇到“Device or resource busy”时把mount、package_extract_file顺序调成先写后mount也是可行解。by-name路径不同机型差异很大小米、三星、老MTK都各有一套写法。我在TWRP终端里会先用readlink确认ls -l /dev/block/bootdevice/by-name/system readlink -f /dev/block/bootdevice/by-name/system这条路径是物理分区的符号链接rec更新或内核变化后仍能保持稳定比裸的mmcblk0p不写死板。拿到真实路径后再回写updater-script。组装zip和签名命令cd ~/rom/out zip -r0 rom_unsigned.zip META-INF system_new.img boot_new.img java -jar ~/bin/signapk.jar -w \ ~/keys/testkey.x509.pem ~/keys/testkey.pk8 \ rom_unsigned.zip rom_signed.zip逻辑说明zip -r0里的0是store不压缩img本身已是压缩后的数据再压浪费CPU签名必须带-wwhole-file签名方式才是卡刷包要的用apksigner签APK那套在rec里会直接失败。发布给陌生人时我会在签名后再跑一遍java -jar signapk.jar -verify确认签名完整才拿出去。4. 避坑ROM解包打包最常见的5个翻车现场4.1 解包阶段挂载失败、权限污染、boot字段对不上翻车一直接挂载sparse img报wrong fs type。现象mount -o loop提示“wrong fs type, bad magic, superblock”看文件也确实像ext4但就是挂不上。原因得到的是Android sparse image不是裸ext4。file命令输出里写着sparse被大多数人忽略。解决先执行simg2img system.img system_raw.img再mount raw镜像。这个坑我踩过不止一次后来养成了解包后第一件事就是跑file命令的习惯。还有一个更隐蔽的情况是厂商改过镜像魔数file输出会显示成data这时用imjtool去看分区偏移按偏移用losetup挂载。但那个属于极少数普通包用simg2img足够。翻车二解包后随便改文件刷入开机应用全部force close。现象系统能启动桌面也有但Settings和一堆系统应用反复闪退logcat里大量Permission Denied。原因文件和目录的uid/gid/mode被宿主机污染。最常见的动作是用root在system目录里cp -r或touch把属主改成了host映射的uid目录权限也从755变成了700。解决回到原始包重新解包把改动脚本化。新加入文件一律chmod 644或755chown root:root。这一步虽然啰嗦但能根治问题。后续排查时用find找非root属主的文件find ~/rom/system -type f ! -uid 0 | head -20 find ~/rom/system -type d ! -perm -755 | head -20第一行查属主异常的文件第二行查权限过窄的目录。执行结果正常的话两条命令的输出应该是空的。翻车三boot.img打包后刷入黑屏手机只能进fastboot。现象解包后没改内核只是重新打包刷完就黑屏呼吸灯亮但屏幕没有任何输出。原因mkbootimg的base、pagesize、cmdline和原boot header不一致或ramdisk压缩格式不一样。最常见的是pagesize记成默认2048但原包是4096。解决解包完把unpack_bootimg输出的header参数存好按原值回填。如果原包ramdisk是gzip打包前别用lz4重新压缩保持和原包一致。我一般会在打包前先对比原boot.img和打包后boot_new.img里的字符串strings ~/rom/out/boot.img | grep -i androidboot | head -5 strings ~/rom/out/boot_new.img | grep -i androidboot | head -5两份输出的cmdline必须一致有一处不同都不进系统。4.2 刷入阶段updater脚本报错、签名失败翻车四TWRP报Error 7脚本没执行完。现象刷入一到三秒就报“Error 7”或“updater process ended with error”。原因updater-script里的getprop断言机型不匹配或者脚本是从Windows记事本拷贝来的CRLF行尾导致Edify解析失败。TWRP看日志时经常能看到“failed to parse”或“expected ; but got”这类提示。解决打开updater脚本检查第一行的getprop断言改成你机型的ro.product.device值同时执行sed -i s/\r$// updater-script。如果日志显示具体某条命令不认识比如format或package_extract_file拼错用正确的函数名重写。我可以给一个判断技巧把报错行的开头和官方LineageOS同机型包里的updater-script对比一下能省很多排查时间因为官方脚本的基本框架是经过大量设备验证的。翻车五签名后刷入显示signature verification failed。现象rec页面明确提示签名校验失败包根本不会被刷入。原因一是zip内残留原厂签名文件二是用了APK签名工具而不是whole-file签名。很多人拿到signapk直接按APK方式来生成的签名只在zip的META-INF里有rec端不认识。解决删除META-INF/MANIFEST.MF、CERT.RSA、CERT.SF重新打包用signapk -w做整包签名。调试期可以临时关rec签名校验但向别人发布时别关签名校验是降低误刷率的第一道门。新平台还有一个连带现象从payload.bin解出来的vbmeta只读分区没有带进新包AVB校验会拦到fastboot。处理方式是保留原vbmeta不修改若要彻底禁用验证用avbtool清掉vbmeta的flags而不是把分区从包里删除。5. 从CM到自定义固件刷入验证与调优技巧刷机不是刷进去就完了我每次发自己的包都会走三步验证。第一步是刷前校验在电脑上先md5sum签名包再在TWRP里对照一遍同时确认包体积小于目标分区剩余空间。第二步是刷完后的logcat过滤。很多问题不是不能开而是异常刷屏不抓日志很难定位。md5sum rom_signed.zip adb logcat -b all -d flash_log.txt grep -iE error|fatal|denied|failed flash_log.txt | head -50grep出来的denied大部分是SELinux拒绝这时候要区分是label问题还是策略问题label问题回到重建镜像前加file_contexts策略问题则提取logcat里的avc信息到policy里补allow。这一步在CM系和LineageOS系上都适用因为它们的SELinux策略相对完整手动包的问题几乎都出在label。一个我坚持了很久的习惯每次改动都在干净的原始包上重跑脚本并把中间镜像、签名产物、分区大小写进一个改动记录文件。某次发布前我图快在一个已经改过一堆东西的目录上继续改DPI结果把之前手动调整过的两个配置文件弄混整个包刷进去WiFi打不开排查了两个晚上。从那以后我每做一个版本都强制走一遍“干净环境重跑→校验镜像→签名→双清刷入→logcat过滤”确认无误才拿出去给身边人测试。这组步骤看着笨但确实是做第三方ROM最有效的后悔药。希望帮到你。本文还有配套的精品资源点击获取