简介:一套适用于安卓第三方ROM制作全流程的集成工具包,面向ROM移植、精简与定制爱好者及开发者。内置一键解包打包脚本,支持boot/recovery、system/vendor/odm、payload.bin、super等常见分区格式的解包与重建,并集成高通机型镜像合并、QSB/OZIP/OFP/华为官方固件解包、开机第一屏logo制作、APK与ZIP签名加密、镜像格式互转等实用功能,菜单化设计便于上手,选项丰富,可从入门用到进阶。压缩包共338个文件,整体约201MB,以130个dll、97个exe核心程序为主,其中dll/exe负责核心逻辑,jar/bat提供脚本扩展,另有properties等配置文件辅助运行。已有4018人学习下载,适合系统化ROM制作练习。工具内含大量可执行模块与批处理入口,操作时注意路径不要包含中文字符;对希望自行编译第三方ROM的用户而言,能显著降低镜像处理门槛,快速完成分区解包、内容修改、重新打包及签名验证。
1. 做第三方 ROM,最快的路是解包而不是编译
做第三方 ROM,不必一上来就啃几个小时的源码编译。把官方全量包或 CM 系包拆开,改完再打回去,这条路对绝大多数想定制系统的人更快:解包、修改、打包、签名,走通一遍基本功也就一天。标题里那个 CM,指 CyanogenMod,也就是后来被 LineageOS 接棒的那一脉第三方 ROM 体系,它代表了“把一套干净系统做成可刷包”的思路,而我们要做的事,是把这类包当成原料再加工。适合谁?手里有旧手机想去掉全家桶、想往设备里塞进一个自己可控系统的工程师,或者单纯想弄明白 Android 镜像到底长什么样的动手派。同样一套流程,从手机到电视盒子都一样能落地。
2. 解开 boot.img:CM 包的启动镜像先搞清片段在哪
boot.img 是刷机包里最值得先拆的一个镜像,也是官方包、CM 包结构最统一的部分。它不是一个文件系统,而是内核、ramdisk、可选 dtb 和一堆头部字段拼在一起的文件。所谓解包,本质就是把这几段按头部描述切出来,而不是像解压 zip 那样整体解开。这一章我会先讲怎么识别镜像,再用常见工具拆一遍,最后用一个 Python 脚本把头部字段读出来,让你知道工具背后到底做了什么。
2.1 先识别镜像格式:为什么 file 命令会骗你
拿到一个 ROM 包,解压出 boot.img,先别急着跑工具。file boot.img的结果往往是data,因为 file 的 magic 数据库不一定认识 Android bootimg。判断标准是文件开头 8 个字节是不是ANDROID!,用 xxd 看一眼最直接:
file boot.img xxd -l 32 boot.imgxxd输出第一行如果以ANDROID!开头,就是标准 Android bootimg。后面紧接的字节是 kernel 大小、ramdisk 大小这类字段的小端表示。如果显示gzip compressed data,说明这是一个把内容整体压缩过的老式 boot 包,处理逻辑不同,而 CM 系和 LineageOS 的包基本都是标准格式,可以放心进入下一步。识别这一步的价值在于,很多新手拿着一个data类型的文件就开始解包,工具报错后完全不知道为什么。
另一点要注意的是,新机型除了 boot.img 还有 init_boot.img、dtbo.img、vbmeta.img 这些伙伴。CM 时代只有一个 boot.img 就能说明一切,现在的动态分区设备文件各自独立。新手做第三方 ROM,建议从 boot + system 两个镜像就能跑通的老设备开始,一上来就碰 payload.bin 的 A/B 设备,后面每一步都要乘两倍复杂度。
2.2 用 Android Image Kitchen 把 ramdisk 拆出来
社区里最常见的解包打包工具是 Android Image Kitchen,通常写作 AIK。它做的事很纯粹:按 header 里的字段把 boot.img 切出 kernel、ramdisk、dtb,然后把 ramdisk 部分解开成目录。常用命令就两条:
./unpackimg.sh boot.img ./repackimg.sh跑完unpackimg.sh后,工作目录会多出split_img/和ramdisk/。split_img/里是还原出来的 kernel、dtb、cmdline 等半成品,文件名带-kernel、-dtb、-cmdline后缀;ramdisk/就是解开后的 ramdisk 文件系统,可以直接改 fstab、default.prop 这些文件。改完以后执行repackimg.sh,它会把ramdisk/重新压回原压缩格式,再按原参数拼回 boot.img。
AIK 在我眼里是个黑匣子,它自动判断 header version、page size、ramdisk 压缩格式,省掉了大量底层细节。但正因为它太自动,很多使用者从来没意识到它还从原镜像里保存了--base、--pagesize这类关键参数。等你某天想手动调 cmdline,却发现repackimg.sh不提供自定义入口时,就需要下面这个技能。
2.3 只用 Python 读一遍 boot header:字段偏移一次看透
为了不黑盒到底,我习惯用一个小脚本把 boot header 的关键字段读出来。这里不用完整解析所有版本,只读几个公共字段,足够理解结构:
import struct import sys def read_boot_header(path): with open(path, "rb") as f: h = f.read(0x500) if h[0:8] != b"ANDROID!": raise ValueError("not an android bootimg") # 这几个字段从 Android 9 到现在偏移一直稳定 kernel_size = struct.unpack_from("<I", h, 0x08)[0] ramdisk_size = struct.unpack_from("<I", h, 0x10)[0] page_size = struct.unpack_from("<I", h, 0x24)[0] header_version = struct.unpack_from("<I", h, 0x28)[0] cmdline = h[0x40:0x240].split(b"\0")[0].decode("latin1") return { "kernel_size": kernel_size, "ramdisk_size": ramdisk_size, "page_size": page_size, "header_version": header_version, "cmdline": cmdline, } if __name__ == "__main__": info = read_boot_header(sys.argv[1]) for k, v in info.items(): print(f"{k}: {v}")这段代码只读 boot header 最前面的通用字段:kernel_size在偏移 0x08,ramdisk_size在 0x10,page_size在 0x24,header_version在 0x28。cmdline从 0x40 开始占 512 字节。组合起来就能推断镜像布局:kernel 从第一个 page 开始,ramdisk 从 kernel 数据结束后的对齐位置开始。理解了它,你就知道为什么改完 ramdisk 后压缩方式不能乱换,因为 bootloader 是按头部声明的大小去指定内存地址读取的。
header_version从 Android 9 以后普遍是 1、2,再新一点是 3、4,每个版本的字段长度和含义都不一样。这个脚本只读公共字段,想要完整解析高版本,建议直接看 AIK 在split_img/里生成的文件,而不必自己重写一套解析器。
2.4 这一层能改什么:fstab、init、cmdline 与 Magisk
解开 boot.img 之后,最常见定制点有三个。第一个是 ramdisk 里的 fstab,把 system、vendor 分区那行的verify去掉,否则刷完第三方 system.img 会被 dm-verity 拒之门外。第二个是default.prop或prop.default,里面ro.secure、ro.debuggable决定系统以什么身份起来,老设备改这里很方便,新设备这些值很多被 bootloader 或 vendor 属性覆盖,改了也不生效。第三个是 cmdline,在split_img/boot.img-cmdline里,可以加androidboot.selinux=permissive之类的调试参数,但要注意这种全局宽松在正式包里别留着。
如果你想给 ROM 加 root,我劝你克制住手工往 init.rc 里塞 su 的冲动。常见且可靠的方案是把 boot.img 交给 Magisk patch,它会处理好 ramdisk 里的 magiskinit 和 overlay,比手工改 init.rc 省掉大量 SELinux 排错。这条建议是我摔过跟头后总结的:手工 root 改完十个包,九个在半路死于权限上下文。
3. 解开 system 分区:sparse 镜像、挂载、去全家桶与特权应用注入
system 分区与 boot 分区完全不同,它是一个真正的文件系统。官方全量包和 CM 包里的system.img通常是 sparse 格式,不能直接挂载,要先转成 raw。转完以后就是一个大的 ext4 目录,整个系统应用、框架、配置全在里面。这一章的内容就是:把 system.img 变成可修改目录,在目录里做减法,再注入你自己的东西。
3.1 官方全量包里的 system.img 常常是 sparse 格式
从官方全量包解压出来的 system.img,直接用file看会显示Android sparse image。sparse 是一种分块镜像格式,为了减少文件体积和写入时间而设计。它内部按块存储数据,还带 CRC,不能直接mount -o loop。
file system.img simg2img system.img system.raw.img file system.raw.imgsimg2img是 Android 工具链里的标准转换工具,把 sparse 还原成 raw ext4。file system.raw.img的输出应该是Linux rev 1.0 ext4 filesystem data。如果输出不是 ext4 而是 erofs 或 f2fs,说明这是新设备的只读压缩分区,处理工具要换,这个边界后面再说。老设备和 CM 时代的包基本都是 ext4,可以继续往下走。
一个常见误区是拿到 sparse 镜像后直接mount -o loop system.img,报错后怀疑包有问题。这不是包坏了,是 sparse 格式本身不是完整文件系统。转换这一步没有捷径,也不要试图用dd去绕,simg2img会按 sparse header 里的块数重建完整镜像,缺它不行。
3.2 挂载并做减法:去全家桶的删包脚本与保留清单
转成 raw 之后就可以挂载了。系统镜像建议先只读挂载,确认没问题后改用挂载目录操作,避免宿主机的 mount 配置干扰镜像:
sudo mkdir -p /tmp/sys sudo mount -o loop,ro system.raw.img /tmp/sys拿到目录结构后,app/和priv-app/就是预装应用的存放位置。删包时别用 find 全盘乱搜,按包名在两个目录里分别删,写成一个脚本方便复用:
# 按包名目录删除,app 和 priv-app 都要检查 for app in BaiduNews DuokuGame RomUpdateService AssistantKit; do rm -rf /tmp/sys/app/$app 2>/dev/null rm -rf /tmp/sys/priv-app/$app 2>/dev/null rm -rf /tmp/sys/product/app/$app 2>/dev/null rm -rf /tmp/sys/vendor/app/$app 2>/dev/null done删包的第一个坑:目录名不一定等于包名。有些应用目录带了 cpu 架构后缀或版本后缀,先ls看清楚再写进脚本。第二个坑:有些应用本体在app/,数据依赖在framework/或lib64/,删应用没问题,删共享库就是另一回事。第三个坑:overlay/目录不要动,删了可能导致分辨率、语言资源全部回退。删完以后,用grep -rn "包名" /tmp/sys/etc/检查还有没有残留的权限配置或意图过滤声明,这些残留不会让包复活,但会让系统日志里反复出现找不到类的报错。
经验原则:第一版精简只删你确定没用的互联网全家桶和自带商店,framework/、media/、fonts/、overlay/一个都别碰。先把整个流程跑通,再逐步加大删减范围。
3.3 注入一个特权应用:priv-app 布局和权限白名单
想在第三方 ROM 里预置一个自己的应用,常见做法是放进priv-app/目录。priv-app 是系统特权应用目录,比普通应用有更高权限,但 Android 8 以后收紧了一个关键机制:priv-app 里的应用必须在/system/etc/permissions/privapp-permissions-*.xml里声明所需权限,否则开机后权限会被静默收回。
<!-- /tmp/sys/etc/permissions/privapp-permissions-custom.xml --> <permissions> <privapp-permissions package="com.example.remote"> <permission name="android.permission.REBOOT"/> <permission name="android.permission.SHUTDOWN"/> </privapp-permissions> </permissions>把 APK 本体放到/tmp/sys/priv-app/com.example.remote/com.example.remote.apk,再放一份这个 XML,就完成了特权应用注入。权限名必须和 APK 的 AndroidManifest.xml 里声明的完全一致,少一个功能受限,多写一个不存在的权限名,可能导致系统服务解析失败,严重的会开机不进桌面。谨慎起见,只声明你确定用到的权限,宁可少声明,不要乱声明。
这个机制的理解很重要:priv-app 不是 root,是系统签名级的特权运行环境。如果你的应用需要控制设备重启、静默安装、修改系统设置,走 priv-app 是比 root 更可控的方案,因为权限边界是系统强制约束的,不会像 su 那样一把梭。
3.4 改 build.prop:型号、语言、时区这些开机属性
system 根目录下的 build.prop 是系统属性的主要来源。第三方 ROM 的常见需求是改设备型号、默认语言、默认时区。直接编辑这个文件,在末尾追加或替换对应行:
# 原文件里同样 key 的行会被覆盖 ro.product.model=MyCustomPhone ro.product.locale=zh-CN persist.sys.timezone=Asia/Shanghai persist.sys.language=zh属性加载有优先级:boot ramdisk 里的 default.prop 最早,然后是 system/build.prop,再是 vendor/build.prop,最后是/data里的持久化属性。所以遇到改了不生效,先查是不是被后续优先级覆盖。persist.*属性是个例外,它一旦在/data里落了地,优先级高于 build.prop,你改了 build.prop 里的时区,系统读到的还是/data里的旧值。这也是为什么很多旧 CM 包换时区就出问题的根源之一,后面避坑章再展开。
我的习惯是:时区、语言这类持久化属性放在 boot ramdisk 的 default.prop 里,只在 build.prop 里改型号、版本号这些纯粹的系统展示字段,这样开机早期就生效,后面不会被 data 分区覆盖。
4. 重新打包:ext4、boot 镜像、ZIP 组装与签名顺序
改完 system 目录和 boot 的 ramdisk 之后,最关键的环节来了:把这些改动还原成可刷入的镜像。这一章的坑密度极高,很多包刷进去卡 LOGO、挂载失败,问题都出在打包参数和原包不一致。前半章讲两个镜像怎么复原,后半章讲刷机包结构和签名顺序。
4.1 把 system 目录打回 ext4 镜像:make_ext4fs 的三个关键参数
system 目录改完以后,需要打回 raw ext4,再转成 sparse。老设备上最常见的工具是make_ext4fs,它比纯mkfs.ext4多做了 Android 的 fs_config 处理,也就是文件权限、capabilities 的写入。三条关键命令:
SIZE=$(dumpe2fs -h system.raw.img 2>/dev/null | awk '/Block count:/{print $3}') BLOCK=$(dumpe2fs -h system.raw.img 2>/dev/null | awk '/Block size:/{print $3}') make_ext4fs -s -l $((SIZE * BLOCK)) -a system system.new.img /tmp/sys第一个关键参数是-l,指定镜像字节数,必须从原镜像的 superblock 读出来,而不是按文件大小猜。写小了镜像装不下,写大了刷进去超出分区实际容量,重启后文件系统挂载失败,这是血泪经验。第二个是-s,生成 sparse 格式,刷机包里的镜像几乎都用 sparse 形式,体积小、写入稳。第三个是-a system,告诉 make_ext4fs 这是 system 分区的镜像,写入正确的 mount point 和各目录的 fs_config,缺了这个参数做出来的包会有诡异权限问题。
这里要划一道边界:Android 10 以后的 system 镜像使用 file_contexts 记录 SELinux 标签,make_ext4fs 不会写标签,做出来的包 SELinux 全部不通过。如果你在做新设备,请改用 target_files 流程或 e2fsdroid 这类能读 file_contexts 的工具。本文的流程最适合 CM 时代和 Android 9 以下的包,这也是为什么只盯 boot + system 的老设备更适合入门。
4.2 把 kernel 和 ramdisk 拼回 boot:base、pagesize、header_version
boot 镜像打包最稳妥的方式是用 AIK 的repackimg.sh,它会自动读split_img/里的原参数。但如果你想手动加 cmdline 或换 dtb,就需要直接调 mkbootimg:
mkbootimg \ --kernel split_img/boot.img-kernel \ --ramdisk ramdisk.cpio.gz \ --cmdline "$(cat split_img/boot.img-cmdline)" \ --base 0x10000000 \ --pagesize 2048 \ --header_version 2 \ --dtb split_img/boot.img-dtb \ -o boot.new.img--base、--pagesize、--header_version这三个参数必须和原包完全一致,否则 bootloader 会按错误的地址加载镜像,卡 LOGO 是轻的。AIK 在解包时已经把这些存进了split_img/,你不需要自己猜。--ramdisk参数传的是 ramdisk 压缩文件本身,mkbootimg 不感知内部压缩格式,它原样塞进 payload 区,所以如果你想自己重建 ramdisk,必须用和原包相同的压缩方式:
cd ramdisk find . | cpio -o -H newc | gzip > ../ramdisk.cpio.gz cd ..原包如果是 lz4,就压回 lz4,不要图省事换成 gzip。bootloader 能不能解是一回事,解出来的内存布局对不对又是另一回事。这一层最常见的翻车就是改了压缩格式或者 pagesize 不匹配,表现都一样:刷完停在开机第一屏。
4.3 组装可刷入的 ZIP:updater-script 背后在做什么
镜像打包完成后,要组装成第三方 recovery 能刷入的 zip。包结构是固定的:
myrom/ ├── boot.img ├── system.img └── META-INF/com/google/android/ ├── update-binary └── updater-script核心在 updater-script,它由 Edify 语言写成,第三方 recovery 会逐行执行。一个经典的老设备示例:
ui_print("Installing boot and system ..."); package_extract_file("boot.img", "/dev/block/boot"); package_extract_file("system.img", "/dev/block/system");这段脚本把 boot.img 直接写到块设备,把 system.img 也直接写到块设备。优点是快、不依赖 recovery 的挂载能力,缺点是块设备路径随设备变化,换机型就失效。新设备的常见写法是先挂载 system 再解包:
ui_print("Mounting system ..."); mount("ext4", "EMMC", "/dev/block/by-name/system", "/system"); package_extract_dir("system", "/system"); unmount("/system");这种写法把 zip 里的 system/ 目录直接解到已挂载的 system 分区,不需要写整个镜像,但要求 recovery 能识别你的分区表。两种写法都能用,区别在于你手上的包是整镜像替换还是增量覆盖。新手做第三方 ROM,整镜像写块设备更容易排查问题,因为只要分区大小和镜像一致,结果就可预期。
updater-script 末尾记得加ui_print("Done.");这类提示,刷机时能直观看到脚本执行到哪一步。出问题时 TWRP 的/tmp/recovery.log会记录每一行执行结果,后面避坑章会具体讲。
4.4 签名和 AVB:哪些校验可以关,哪些校验不能省
第三方 ROM 与官方系统在签名上天然对立。zip 签名用 AOSP 的 signapk 执行:
java -jar signapk.jar platform.x509.pem platform.pk8 myrom.zip myrom-signed.zipTWRP 默认不校验 zip 签名,你签不签都能刷,只有官方 recovery 才强制校验 OTA key。但 boot.img 和 system.img 的验证是另一套东西,Android 7 以后是 dm-verity,Android 8 以后是 AVB 2.0。第三方包没有官方 key,验证天然失败,常见做法是禁用验证而不是伪造签名。
最简单的方式是刷入一个关闭验证的 vbmeta:
fastboot --disable-verity --disable-verification flash vbmeta vbmeta.img--disable-verity关闭 dm-verity 的哈希树校验,--disable-verification关闭 bootloader 对 boot 镜像的 AVB 校验。这两个参数是 fastboot 官方提供的,前提是你的 bootloader 已解锁。如果你不想单独刷 vbmeta,也可以在 ramdisk 的 fstab 里去掉 system 分区的verify标志,效果一样,但不如 vbmeta 干净。
这里想强调一个认知:AVB 校验的关闭是针对你自己设备的行为,不是破解,也不是伪造官方签名。伪造签名在算力上不现实,也没必要,解锁后的设备完全可以按官方支持的途径关闭验证。做第三方 ROM,这条链路本身就是这么走的。
5. 避坑指南:卡 LOGO、挂载失败、删完 apk 闪退怎么查
解包打包的流程说穿了就那么几步,但每一步都有对应的坑。这一章我按现象、原因、解决三个维度,写五个最常见的问题,后面照着排查能省一半时间。
5.1 现象:刷完卡在开机 LOGO,永远进不去桌面
原因:ramdisk 压缩方式被换了,或者 boot 打包参数不一致。最常见的是原包 ramdisk 是 lz4,你用 cpio + gzip 压回去;其次是 mkbootimg 时--pagesize没按原值填,bootloader 在错误的位置找 ramdisk。另一种常见原因是 header_version 填错,版本 3 的 boot 镜像头部结构完全重构,用 v2 的布局去包 v3 的镜像,开机直接白屏。
解决:解包后先记录split_img/里的原始参数,特别是--pagesize、--header_version和 cmdline 内容。ramdisk 重建时用file看一眼原来的压缩格式,原包是 lz4 就压回 lz4,原包是 gzip 就压回 gzip。如果自己手动 mkbootimg 老出问题,直接运行 AIK 的repackimg.sh,它会自动读原参数,少碰一个变量就少踩一个坑。
5.2 现象:刷完 system 后 Recovery 或系统报Invalid argument
原因:system.img 打包时-l参数填得比实际分区大。make_ext4fs 生成的镜像体积超过分区容量,写入时被分区层截断,重启后文件系统 superblock 里的块数比实际分区能提供的多,内核挂载时直接拒绝。另一个原因是用错了工具打包,生成的镜像没有 Android 的 fs_config,目录权限全乱,但这类问题通常表现为权限报错而不是Invalid argument。
解决:打包前用dumpe2fs -h读原镜像的Block count和Block size,两者相乘得到精确字节数,不要直接拿原文件大小或分区标称大小凑数。如果分区实际大小小于镜像大小,用resize2fs把新镜像缩进去再刷。记住一个习惯:所有参数从原镜像读,不靠自己记。
5.3 现象:TWRP 刷 zip 报Error 6或者刷到一半退出
原因:updater-script 语法错误。常见问题包括:mount函数参数写错、package_extract_file的源文件名和 zip 里实际文件名不一致、Edify 语句少了分号。status 6是脚本解析层面的错误,不是分区写入失败,所以不用怀疑镜像文件本身。
解决:刷完看 TWRP 里的/tmp/recovery.log,日志会定位到具体出错的行号。把 updater-script 改成极简版本,只留一个 ui_print 和一个 package_extract_file,刷一次跑通,再加内容。这样逐步加长,能精确定位到是哪一行出问题,而不是对着十行脚本猜。
5.4 现象:精简包刷完后应用大面积闪退,系统设置都进不去
原因:删了共享依赖。很多预装应用本体在 app/ 目录,但代码依赖位于 framework/、lib64/、overlay/ 里,删包时顺手删了这些共享文件,整个系统服务链就断了。另一个原因是删了某个应用但没删干净的残留,系统每次扫描 sysconfig 配置时找不到对应类,反复抛异常。
解决:删包前用grep -rn "包名" /tmp/sys/etc/查引用,看有没有 sysconfig、permissions 里的关联配置。删完以后不要急着打包,先在目录里对比一遍 framework/ 和 lib64/ 有没有被误伤。精简的第一版保守一点,只删 app/ 和 priv-app/ 里的完整目录,那些目录里不带 lib/ 子目录的尤其谨慎,说明它的代码可能全在系统共享库里。
5.5 现象:移植包能开机,一改时区或语言就无限软重启
原因:framework-res.apk 的 locale 资源被裁了。有些第三方包为了减小体积,删掉了 framework 里非默认语言资源,改时区或语言后 SystemUI 拿不到完整的资源配置,反复崩溃,表现为软重启。这个锅不全在解包打包流程,而是源包本身就不完整。
解决:刷完包先别急着改语言,观察默认状态是否稳定。改 build.prop 里的ro.product.locale=zh-CN和persist.sys.language=zh,让它开机就是目标语言,避免运行期切换触发资源重载。崩溃已经发生的话,先清 data 分区让持久化属性归零,再尝试进系统。如果你只是换时区就重启,大概率是源包裁剪过度,换一个完整度高的 CM/LineageOS 包更省事。
6. 把整条流水线压成一条命令,并学会先验证再烧写
前面几章都是手动操作,最后一章把这些步骤收敛成一个骨架脚本,再加一个重要的验证习惯。脚本的目的不是代替你思考,而是把定制动作之外的所有机械环节固定下来,减少人为失误。
#!/bin/bash set -euo pipefail BOOT=stock/boot.img SYSTEM=stock/system.img WORK=build mkdir -p $WORK/mnt # 1) 解包 ./unpackimg.sh $BOOT simg2img $SYSTEM $WORK/system.raw.img sudo mount -o loop $WORK/system.raw.img $WORK/mnt # 2) 定制动作写在这两行下面 # 删包、改 build.prop、注入 priv-app 等 # 3) 打回镜像 sudo umount $WORK/mnt SIZE=$(dumpe2fs -h $WORK/system.raw.img | awk '/Block count:/{print $3}') BLOCK=$(dumpe2fs -h $WORK/system.raw.img | awk '/Block size:/{print $3}') make_ext4fs -s -l $((SIZE * BLOCK)) -a system $WORK/system.img $WORK/mnt ./repackimg.sh脚本里的set -euo pipefail让任何一步失败就中断,避免打包到一半继续往下跑,最终产出一个坏包。定制动作是唯一需要你每次想清楚的部分,这一步不要脚本化,因为每个包要改的东西不同。打包参数全部从原镜像读取,这是前面避坑章总结出的最重要的纪律。
验证方式比刷机更聪明一步:用fastboot boot boot.new.img临时引导 boot 镜像。这个命令把镜像加载到内存启动,不写入 boot 分区,起不来就重启回到原系统,比直接烧写多一道后悔药。确认临时引导能进系统,再考虑整包刷入。system 分区的验证没有这样的临时机制,那就先在 recovery 里只刷 system 不刷 boot,保留原 kernel,至少能排除内核侧的问题。
我早期做第三方包时吃过一次大亏:手动算 system 分区大小,少算了几百 MB,连续刷坏三块 userdata 分区才意识到问题根源。后来定了两条规矩,一是所有大小参数从原镜像 superblock 读,二是每步操作前先在本地做个文件快照。解包打包这个方向门槛不高但耐心要求很高,你愿意按这套流程多验证一轮,翻车概率会直线下降。希望帮到你。
本文还有配套的精品资源,点击获取