4. 固件打包与重打包

任务说明

本案例以典型嵌入式设备固件(基于 CPIO 归档 + gzip 压缩的 Linux 内核 initramfs)为例,演示固件解包、修改、重打包的完整流程。目标是在修改固件内文件后,生成一个结构合法、可被设备引导加载程序接受的固件镜像。案例不涉及签名绕过或加密固件,仅针对未签名/明文固件的打包格式还原。

环境与依赖

  • Linux 环境(或 WSL),需具备 binwalk、cpio、gzip、file 工具
  • 安装:sudo apt install binwalk cpio gzip file
  • 示例固件:firmware.bin(由本案例脚本生成,用于验证流程)

固件结构识别

首先使用 binwalk 扫描固件,确认内部文件系统类型与偏移位置:

binwalk firmware.bin

典型输出会显示 gzip 压缩数据起始偏移(例如 0x100)。以下脚本自动完成从识别到重打包的全过程。

关键完整代码

将以下脚本保存为 repack.sh,并赋予执行权限(chmod +x repack.sh):

#!/bin/bash
# 固件解包-修改-重打包脚本(适用于 CPIO+gzip 格式)
set -e

FIRMWARE="firmware.bin"
WORKDIR="fw_work"
MODIFY_FILE="etc/config.txt"   # 要修改的文件路径(相对于根目录)
MODIFY_CONTENT="version=2.0"    # 新内容

# 1. 清理并创建工作目录
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"
cd "$WORKDIR"

# 2. 使用 binwalk 定位 gzip 偏移并解压
binwalk -e "../$FIRMWARE" > /dev/null 2>&1
GZ_DIR=$(find . -type d -name "_*" | head -1)
if [ -z "$GZ_DIR" ]; then
    echo "错误:未找到压缩段,请检查固件格式"
    exit 1
fi

# 3. 解压 gzip 得到 cpio 归档
cd "$GZ_DIR"
gzip -d -k *.gz 2>/dev/null || true
CPIO_FILE=$(ls | grep -v '\.gz$' | head -1)
if [ -z "$CPIO_FILE" ]; then
    echo "错误:未找到 CPIO 归档"
    exit 1
fi

# 4. 解包 cpio 到根目录
mkdir -p rootfs
cd rootfs
cpio -idmv < "../$CPIO_FILE" > /dev/null 2>&1

# 5. 修改目标文件
if [ -f "$MODIFY_FILE" ]; then
    echo "$MODIFY_CONTENT" > "$MODIFY_FILE"
    echo "已修改 $MODIFY_FILE"
else
    echo "警告:$MODIFY_FILE 不存在,创建新文件"
    mkdir -p "$(dirname "$MODIFY_FILE")"
    echo "$MODIFY_CONTENT" > "$MODIFY_FILE"
fi

# 6. 重新打包 cpio(使用 newc 格式,保持权限)
find . | cpio -o -H newc > "../new_initramfs.cpio" 2>/dev/null

# 7. 重新压缩为 gzip
cd ..
gzip -9 -c new_initramfs.cpio > new_initramfs.cpio.gz

# 8. 重组固件(保留原头部偏移)
cd ..
# 获取原固件头部长度(binwalk 显示的偏移值)
HEADER_LEN=$(binwalk "../$FIRMWARE" | grep gzip | head -1 | awk '{print $1}')
if [ -z "$HEADER_LEN" ]; then HEADER_LEN=0; fi

# 提取原头部并拼接新固件
dd if="../$FIRMWARE" of=header.bin bs=1 count=$HEADER_LEN 2>/dev/null
cat header.bin "$GZ_DIR/new_initramfs.cpio.gz" > "../firmware_repacked.bin"

echo "完成:firmware_repacked.bin 已生成"
echo "验证:file ../firmware_repacked.bin"

运行与验证

  1. 生成测试固件(仅用于验证流程):
    mkdir -p test_root/etc
    echo "version=1.0" > test_root/etc/config.txt
    cd test_root
    find . | cpio -o -H newc | gzip -9 > ../firmware.bin
    cd ..
  2. 执行重打包脚本:
    chmod +x repack.sh
    ./repack.sh
  3. 验证重打包结果:
    # 检查文件类型
    file firmware_repacked.bin
    # 应输出:gzip compressed data
    
    # 解包验证修改是否生效
    binwalk -e firmware_repacked.bin
    find _* -name "config.txt" -exec cat {} \;
    # 应输出:version=2.0

通过条件

  • file 命令识别新固件为 gzip 压缩数据,且头部偏移与原固件一致
  • 重新解包后,etc/config.txt 内容为 version=2.0,证明修改已正确写入
  • 固件整体结构完整,设备引导加载程序可正常解析(若设备支持未签名固件)

常见问题

  • binwalk 未识别压缩段:固件可能使用 LZMA/LZ4 等其他压缩,需调整解压命令
  • cpio 格式不匹配:部分固件使用旧版 binary 格式,将 -H newc 改为 -H bin 重试
  • 权限丢失:重打包前确认 cpio 命令在 rootfs 目录内执行,且文件属主为当前用户