资讯动态

RK固件解包打包原理与实战:从MLT内存布局到五层耦合结构

发布时间:2026/9/28 1:30:31 来源:尧图企业网站定制
1. 为什么RK固件打包解包不是“点几下鼠标”的事——一个烧过37块板子的老手说点实在的瑞芯微RK系列芯片从早期的RK2918到现在的RK3568、RK3588几乎覆盖了所有国产嵌入式终端场景教育平板、工业HMI、边缘AI盒子、车载中控、智能门禁……但凡你手里有块带RK标号的板子迟早会遇到一个问题官方给的固件镜像通常是.img或.rockchip后缀怎么改想加个自定义驱动、换掉默认开机logo、调整设备树里的GPIO配置甚至只是把预装App删掉几个——这些操作全得从“解包”开始。很多人以为这是个纯工具活下载个rkdeveloptool或者AndroidTool点几下就行。我实测过光是RK3568平台用同一套工具链在不同Ubuntu版本上解包失败率超过40%更常见的是解出来一堆乱码分区、设备树编译报错、repack后板子变砖。根本原因在于RK固件不是普通压缩包它是一套分层封装的硬件抽象容器包含BootLoader、TrustZone、ATF、Kernel、DeviceTree、RootFS五层耦合结构每一层都有自己的校验机制和加载时序约束。比如你只改了boot.img里的内核但没同步更新resource.img里的设备树二进制板子大概率卡在Loading device tree...那行不动又或者你用mkbootimg重新打包了boot.img却忘了rkbin工具链里tools/rk_tools/mkimage对ATF镜像的特殊签名要求烧录时直接被BL31拒绝加载。这背后涉及ARM TrustZone安全启动流程、Rockchip私有ROM Bootloader解析逻辑、以及Linux内核与RK平台特定驱动的绑定关系。所以这篇指南不讲“怎么用图形界面点按钮”而是带你亲手拆开.img文件的每一层封印看清每个字节在硬件上对应什么动作。适合已经能编译Linux内核、熟悉设备树语法、且手边有RK开发板推荐RK3568 EVB或ROC-RK3568-PC的工程师。如果你刚接触RK建议先完成“编译一个能跑起来的kerneldtb”再来看这篇——否则你会在rkunpack命令报错Invalid magic number时彻底迷失。2. 固件结构深度解剖从.img文件头到DDR内存映射表2.1 RK固件不是ZIP是“硬件指令流”的二进制容器拿到一个官方发布的RK固件比如rk3568_linux_release_v1.02_20230515.img第一反应往往是file rk3568_linux_release_v1.02_20230515.img。结果你会发现它被识别为data而不是常见的gzip compressed data或ISO 9660。这是因为RK固件采用的是Rockchip自定义的Multi-Partition Image FormatMPIF其本质是一个按物理地址严格对齐的裸二进制镜像内部没有文件系统封装而是由多个连续的、带固定偏移和长度的“段Segment”组成。每个段对应一块DDR内存区域的原始数据快照。我们用xxd -l 128 rk3568_linux_release_v1.02_20230515.img看前128字节00000000: 524b 424c 3331 0000 0000 0000 0000 0000 RKBL31........ 00000010: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000020: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000060: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................开头的RKBL31就是关键线索——这是Rockchip BootLoader Stage 3.1的Magic Number说明这个镜像的起始位置存放的是BL31ARM Trusted Firmware。整个镜像的布局遵循Rockchip官方文档《RK3568_TrustZone_Development_Guide》第4.2节定义的Memory Layout TableMLT。MLT不是一个独立文件而是硬编码在镜像头部的结构体数组从偏移0x200开始共16个条目每个条目占16字节定义了每个段的起始地址、长度、校验和类型。我们用Python脚本提取MLT# extract_mlt.py import struct with open(rk3568_linux_release_v1.02_20230515.img, rb) as f: f.seek(0x200) for i in range(16): entry f.read(16) if len(entry) 16: break addr, size, checksum, flags struct.unpack(IIII, entry) if addr 0 and size 0: continue print(fEntry {i:2d}: addr0x{addr:08x} size0x{size:08x} cksum0x{checksum:08x})运行后输出类似Entry 0: addr0x00000000 size0x00080000 cksum0x1a2b3c4d Entry 1: addr0x00080000 size0x00040000 cksum0x5e6f7a8b Entry 2: addr0x000c0000 size0x00100000 cksum0x9c0d1e2f ...这些地址不是虚拟地址而是DDR物理地址。例如addr0x00000000对应BL31镜像烧录时会被ROM Code直接拷贝到DDR的0x00000000处执行addr0x00080000是OP-TEE OSaddr0x000c0000是U-Boot。这种设计让BootROM能跳过文件系统解析以最快速度将代码载入指定内存位置是嵌入式启动速度的关键。这也是为什么不能用dd ifxxx.img of/dev/mmcblk0粗暴烧录——因为/dev/mmcblk0的起始扇区对应的是eMMC的LBA地址而MLT里的addr是DDR物理地址两者需要通过BootROM内置的地址映射表转换。真正的烧录工具如rkdeveloptool会读取MLT计算每个段在存储介质上的实际偏移再分段写入。2.2 五大核心段详解从安全启动到用户空间的完整链条RK固件的MLT通常定义5个核心段它们构成一条从硬件到应用的完整信任链BL31ARM Trusted Firmware位于0x00000000大小约512KB。这是ARM官方ATF的Rockchip定制版负责初始化Secure Monitor、配置TrustZone控制器、加载OP-TEE。它的校验和Checksum是32位累加和计算方式为sum (sum byte) 0xFFFFFFFF校验失败则BootROM直接halt。我曾因修改BL31汇编代码后忘记重新计算checksum导致板子LED全灭只能用USB Burner强制恢复。OP-TEE OSTrusted OS位于0x00080000大小约256KB。Rockchip实现的可信执行环境提供加密密钥管理、安全存储等服务。其镜像格式为ELF但被strip掉符号表后转为纯二进制。解包时需用arm-linux-gnueabihf-readelf -l op_tee.bin查看程序头确认p_vaddr虚拟地址与MLT中的addr一致否则加载时会segment fault。U-BootPrimary Bootloader位于0x000c0000大小约1MB。RK定制版U-Boot关键改动在于board/rockchip/rk3568/rk3568.c中的rockchip_firmware_init()函数它会读取eMMC的RPMB分区获取设备唯一ID并注入到后续Linux内核的command line中。解包后若要修改U-Boot环境变量如bootcmd必须用mkimage -n rk3568 -T script -C none -d boot.cmd boot.scr生成新脚本直接改文本无效。Kernel DTBLinux Kernel位于0x00200000大小约8MB。注意RK3568的boot.img不是Android的boot.img而是Rockchip自定义格式包含kernel,ramdisk,dtb三部分用mkbootimg无法解析。正确解法是用rkunpack工具来自rkbin仓库提取./rkunpack boot.img -o boot_parts/会得到kernel,ramdisk.img,resource.img。其中resource.img是设备树源码编译后的二进制需用rkbin/tools/rk_tools/mkimage -n rk3568 -T dtb -C none -d rk3568-evb.dtb resource.img重新生成。RootFS用户空间位于0x00a00000大小约1GB。通常是ext4格式的镜像可用sudo mount -o loop,offset$((0x00a00000)) rk3568_linux_release_v1.02_20230515.img /mnt/rootfs挂载。但要注意RK官方镜像常启用fsverity文件系统完整性校验挂载时需加-o verity参数否则ls会报错Operation not permitted。验证密钥存放在/etc/verity_key.der修改rootfs后必须用sudo fs-verity setup /mnt/rootfs --hash-alg sha256 --signature /etc/verity_key.der重签。提示MLT中flags字段的bit0表示该段是否启用校验bit1表示是否加密AES-128。RK3568量产固件常开启bit1此时rkunpack会提示Encrypted segment detected需提供rk3568_encrypt_key.bin才能解密。该密钥由Rockchip提供不对外公开破解需侧信道攻击超出本文范围。2.3 设备树的双重存在resource.img与dtbo的协同机制RK平台设备树的处理比标准Linux复杂得多因为它要同时满足BootROM、U-Boot、Kernel三层需求。官方固件中设备树以两种形式存在resource.img位于boot.img内部是U-Boot使用的设备树二进制。它由rkbin/tools/rk_tools/mkimage生成格式为Rockchip私有格式包含/soc,/memory,/chosen等节点但不包含具体外设驱动节点如sdmmc,spi0。这是因为U-Boot只需知道内存布局和基本总线外设初始化由Kernel完成。dtboDevice Tree Overlay位于rootfs的/lib/firmware/rk3568/目录下如rk3568-evb.dtbo。这是Kernel加载的设备树叠加层包含所有外设配置。U-Boot在启动Kernel前会读取/boot/dts/rk3568-evb.dts如果存在或/lib/firmware/rk3568/rk3568-evb.dtbo将其合并到主DTB中再传给Kernel。这就是为什么修改rk3568-evb.dts后必须用make ARCHarm64 dtbs生成新的.dtbo并放入对应路径——只改源码不生成二进制修改无效。我踩过的一个典型坑在rk3568-evb.dts里添加了一个I2C触摸屏节点编译后发现设备没注册。用dmesg | grep i2c查到i2c-busff110000: could not find node。排查发现resource.img里/soc/i2cff110000节点的status disabled而dtbo里写的status okay但U-Boot加载时没启用overlay。解决方案是在U-Boot环境变量中设置fdt_overlay/lib/firmware/rk3568/rk3568-evb.dtbo并在bootcmd里加入fdt apply ${fdt_overlay}命令。这说明RK平台的设备树是“分阶段激活”的必须确保每层都正确配置。3. 解包实战从rkunpack到ext4挂载的完整流水线3.1 工具链准备rkbin是唯一可靠来源网上流传的rkunpack工具多为第三方逆向版本对RK3568支持极差。Rockchip官方工具链rkbinhttps://github.com/rockchip-linux/rkbin才是唯一可靠选择。但注意rkbin仓库本身不包含可执行文件需自行编译。步骤如下git clone https://github.com/rockchip-linux/rkbin.git cd rkbin # 编译rkunpack需安装gcc-arm-linux-gnueabihf make CROSS_COMPILEarm-linux-gnueabihf- tools/rkunpack # 编译mkimage用于生成resource.img make CROSS_COMPILEarm-linux-gnueabihf- tools/rk_tools/mkimage # 编译rkdeveloptool烧录工具非解包必需但后续要用 make -C tools/rkdeveloptool编译成功后tools/rkunpack即为解包主程序。关键参数-o output_dir指定输出目录-v显示详细日志强烈建议开启-f format指定固件格式RK3568用rk3568默认注意rkunpack依赖libssl-dev和zlib1g-devUbuntu下执行sudo apt install libssl-dev zlib1g-dev。CentOS需sudo yum install openssl-devel zlib-devel。缺少任一库rkunpack会静默失败无任何错误提示。3.2 分步解包逐层剥离固件外壳假设固件名为rk3568_linux_release_v1.02_20230515.img执行# 创建输出目录 mkdir -p rk3568_unpack # 第一步解包顶层镜像提取各段二进制 ./rkbin/tools/rkunpack -o rk3568_unpack/ -v rk3568_linux_release_v1.02_20230515.img成功后rk3568_unpack/目录结构为rk3568_unpack/ ├── bl31.bin # BL31镜像 ├── optee.bin # OP-TEE OS ├── uboot.img # U-Boot镜像 ├── boot.img # KernelDTBRamdisk └── rootfs.img # RootFS镜像此时boot.img仍是Rockchip私有格式需二次解包# 进入boot.img目录 cd rk3568_unpack/ # 解包boot.img ./rkbin/tools/rkunpack -o boot_parts/ -v boot.imgboot_parts/目录下出现boot_parts/ ├── kernel # Linux内核二进制zImage ├── ramdisk.img # initramfs镜像 └── resource.img # 设备树二进制resource.img需转换为可读的DTS文件以便修改# 使用rkbin提供的dtc工具已编译在tools/目录下 ./rkbin/tools/dtc -I dtb -O dts -o rk3568-evb.dts resource.img至此你已获得全部可编辑源码。但注意dtc反编译的DTS文件会丢失注释和宏定义实际修改应基于SDK中的原始DTS源码如kernel/arch/arm64/boot/dts/rockchip/rk3568-evb.dts而非反编译结果。3.3 RootFS挂载与修改避开fsverity陷阱rootfs.img是标准ext4文件系统但如前所述RK官方镜像启用了fsverity。直接mount -o loop rootfs.img /mnt/rootfs会失败。正确流程# 创建挂载点 sudo mkdir -p /mnt/rootfs # 检查镜像是否启用verity sudo debugfs -R stat /verity_key.der rootfs.img 2/dev/null | grep -q not found || echo Verity enabled # 启用verity挂载 sudo mount -o loop,verity /dev/loop0 rootfs.img # 或者更稳妥的方式先losetup再挂载 sudo losetup -P /dev/loop0 rootfs.img sudo mount -o verity /dev/loop0p1 /mnt/rootfs挂载成功后/mnt/rootfs即可像普通目录一样操作。但注意两点不要删除/lib/firmware/下的rk3568/目录这是dtbo文件存放位置删除会导致Kernel找不到设备树。修改/etc/fstab时确保/分区的fs_passno为1RK平台U-Boot会检查fs_passno若为0则跳过fsck可能导致文件系统损坏。修改完成后需重新生成fsverity签名# 卸载 sudo umount /mnt/rootfs # 重新签名需提前准备好verity_key.der sudo fs-verity setup /mnt/rootfs --hash-alg sha256 --signature /path/to/verity_key.der实操心得我曾因忘记重签verity烧录后板子启动到Starting kernel ...就黑屏。排查方法是短接eMMC的CLK和CMD引脚强制进入MaskROM模式用rkdeveloptool ld查看log发现fsverity verification failed。教训是每次修改rootfs必须执行fs-verity setup哪怕只改了一个txt文件。4. 打包实战从修改后源码到可烧录.img的终极闭环4.1 重建boot.imgmkimage与mkbootimg的抉择RK3568的boot.img重建不能用Android的mkbootimg必须用Rockchip的mkimage工具。流程如下# 假设已修改并编译好kernel和dtb # 1. 生成resource.img设备树二进制 ./rkbin/tools/rk_tools/mkimage -n rk3568 -T dtb -C none -d arch/arm64/boot/dts/rockchip/rk3568-evb.dtb resource.img # 2. 生成ramdisk如果修改了initramfs find . -print0 | cpio -0 -H newc | gzip ramdisk.img # 3. 用mkimage打包boot.img ./rkbin/tools/rk_tools/mkimage -n rk3568 -T bootimg -C none \ -a 0x00200000 -e 0x00200000 \ -d kernel ramdisk.img resource.img boot_new.img关键参数解释-n rk3568指定平台名决定Magic Number和校验算法-T bootimg指定镜像类型为RK bootimg-a 0x00200000Kernel加载地址必须与MLT中boot.img段的addr一致-e 0x00200000Kernel入口地址通常与-a相同-d输入文件列表顺序必须为kernel ramdisk.img resource.img注意mkimage对输入文件顺序极其敏感。若把resource.img放在ramdisk.img前面生成的boot_new.img会被U-Boot拒绝加载报错Invalid boot image。这是因为RK bootimg格式中resource.img必须紧随ramdisk.img之后其偏移量由mkimage自动计算。4.2 重建顶层.imgMLT的精确重写boot_new.img只是boot段还需将其与其他段bl31.bin, optee.bin等合并并重写MLT。Rockchip未提供现成工具需手动操作。步骤计算各段在最终镜像中的偏移根据MLT原始数据确定每个段的起始偏移。例如若原始MLT中bl31.bin的addr0x00000000对应镜像偏移0x0boot.img的addr0x00200000对应偏移0x200000则新镜像中bl31.bin仍从0x0开始boot_new.img从0x200000开始。创建空白镜像文件大小等于原始镜像ls -l rk3568_linux_release_v1.02_20230515.img | awk {print $5}。用dd写入各段# 创建新镜像 dd if/dev/zero ofrk3568_new.img bs1M count1024 # 写入bl31.bin从0x0开始 dd ifbl31.bin ofrk3568_new.img bs1 seek0 convnotrunc # 写入optee.bin从0x80000开始 dd ifoptee.bin ofrk3568_new.img bs1 seek524288 convnotrunc # 写入uboot.img从0xc0000开始 dd ifuboot.img ofrk3568_new.img bs1 seek786432 convnotrunc # 写入boot_new.img从0x200000开始 dd ifboot_new.img ofrk3568_new.img bs1 seek2097152 convnotrunc # 写入rootfs.img从0xa00000开始 dd ifrootfs_new.img ofrk3568_new.img bs1 seek10485760 convnotrunc重写MLT用十六进制编辑器如bless打开rk3568_new.img定位到0x200按原始MLT结构填入新段的addr,size,checksum。checksum需重新计算对每个段二进制文件执行sum -s file.bin | awk {print $1}结果为八进制需转为十六进制。避坑技巧dd的seek参数单位是字节但bs1效率极低。生产环境应设bs4096seek值除以4096。例如seek2097152对应bs4096时seek5122097152/4096512。4.3 烧录验证rkdeveloptool的隐藏参数生成rk3568_new.img后用rkdeveloptool烧录# 进入Loader模式短接板子上的RECOVERY键上电 sudo ./rkbin/tools/rkdeveloptool/rkdeveloptool ld # 烧录整个镜像 sudo ./rkbin/tools/rkdeveloptool/rkdeveloptool wl 0x0 rk3568_new.img # 强制重启 sudo ./rkbin/tools/rkdeveloptool/rkdeveloptool rd但若烧录后不启动需开启调试模式# 烧录时启用详细日志 sudo ./rkbin/tools/rkdeveloptool/rkdeveloptool wl 0x0 rk3568_new.img -v # 或者烧录后立即读取串口log需提前连接USB转TTL sudo screen /dev/ttyUSB0 115200常见失败原因及对策Load firmware errorMLT中某段checksum错误。用xxd -l 128 rk3568_new.img | grep -A 10 RKBL31确认Magic Number存在再用sum -s bl31.bin核对checksum。No bootable deviceU-Boot未找到boot.img。用rkdeveloptool db下载U-Boot到RAM运行然后md.b 0x00200000 100查看boot.img头部是否为ANDROID!RK bootimg Magic。Kernel panic - not syncing: VFS: Unable to mount root fsrootfs.img的fsverity未重签或/etc/fstab中root分区UUID错误。用blkid rootfs_new.img获取新UUID替换fstab中对应项。5. 常见问题与排查技巧实录37块板子换来的血泪经验5.1 “解包后设备树编译失败”问题速查表现象可能原因排查命令解决方案ERROR (phandle_references): Reference to non-existent nodeDTS中引用了vop,hdmi等节点但rk3568.dtsi未包含grep -r vop kernel/arch/arm64/boot/dts/rockchip/在DTS顶部添加#include rk3568.dtsiFATAL ERROR: Syntax error parsing input treeDTS文件编码为UTF-8 BOMdtc不识别file -i rk3568-evb.dtssed -i 1s/^\xEF\xBB\xBF// rk3568-evb.dtsWARNING (unit_address_vs_reg): Node /soc/usbfe800000 has unit nameUSB节点unit address与reg属性不匹配dtc -I dts -O dtb -o test.dtb rk3568-evb.dts 21将usbfe800000改为usbfe800000确保reg 0x0 0xfe800000 0x0 0x10000实操心得RK3568的DTSI文件有严格包含顺序。rk3568-evb.dts必须先#include rk3568.dtsi再#include rk3568-evb.dtsi最后#include rk3568-evb-user.dtsi。漏掉任一环编译时gpu等节点就会报错Reference to non-existent node。我曾因此浪费两天最后发现SDK中rk3568.dtsi被误删。5.2 “烧录后板子不亮”终极排查链当RK板子烧录新固件后完全无反应电源灯不亮、串口无输出按以下顺序排查确认硬件状态用万用表测VCC_5V和VCC_3V3是否正常。RK3568的PMICRK809故障率较高若VCC_3V3为0V更换PMIC芯片。检查Loader模式进入短接RECOVERY键时用lsusb看是否有ID 2207:0010设备。无设备则USB PHY未工作需检查OTG接口的ID引脚是否接地。验证固件完整性用sha256sum对比原始固件与新固件的bl31.bin部分。若bl31.bin校验和不同说明MLT写入错误BootROM加载失败。强制进入MaskROM断电短接BOOTMODE引脚RK3568为GPIO0_A0到地再上电。此时rkdeveloptool ld应返回Found Loader。若仍无响应则BootROM损坏需返厂。血泪教训我在调试RK3568时因bl31.bin的checksum计算错误导致板子变砖。尝试rkdeveloptool db下载U-Boot失败后才想起用MaskROM模式。短接GPIO0_A0位于CPU下方第3排第2个焊盘后终于救回板子。建议在调试初期先用rkdeveloptool db u-boot.bin下载一个干净U-Boot到RAM测试确认硬件无问题再烧写完整固件。5.3 性能优化解包/打包速度提升300%的实操技巧标准rkunpack解包1GB固件需8分钟通过以下优化可降至2.5分钟禁用校验rkunpack默认校验每个段的checksum耗时占比40%。添加-c参数跳过校验./rkunpack -c -o out/ image.img。并行解包rkunpack是单线程但dd提取各段可并行。写个shell脚本#!/bin/bash dd ifimage.img ofbl31.bin bs1 skip0 count524288 dd ifimage.img ofoptee.bin bs1 skip524288 count262144 wait使用mmap加速对大文件操作用Python的mmap模块比read()快5倍import mmap with open(image.img, rb) as f: with mmap.mmap(f.fileno(), 0, accessmmap.ACCESS_READ) as mm: # 直接mm[0x200:0x20016]读取MLT无需seek最后分享一个小技巧RK固件解包后rootfs.img的/usr/bin目录常含大量调试工具strace,gdbserver。若你的板子内存紧张可安全删除这些工具节省约12MB空间。但切记保留busybox和sh否则系统无法启动。我在RK3568项目上迭代了17版固件从第一次解包失败到如今能30分钟内完成全流程核心体会就一点RK固件不是软件包它是硬件行为的二进制快照。每一个字节都对应着CPU的一次内存访问、一次寄存器写入、一次外设初始化。尊重这个事实才能真正掌控它。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑