From db8f25a518b625a70c0658560cb9c989224d6e28 Mon Sep 17 00:00:00 2001 From: Ching L Date: Fri, 12 Dec 2025 18:03:39 +0800 Subject: [PATCH] feat(cp02): add firmware processing tools - Add extract_firmware.sh for extracting firmware components from merged bins - Add merge_2323_firmware.sh for merging PA768 updater, firmware and IUM data - Add split_and_merge.sh for splitting firmware and creating 40W firmware - Add comprehensive README.md with usage documentation and examples --- CP02/README.md | 43 +++++++++ CP02/extract_firmware.sh | 129 +++++++++++++++++++++++++ CP02/merge_2323_firmware.sh | 188 ++++++++++++++++++++++++++++++++++++ CP02/split_and_merge.sh | 84 ++++++++++++++++ 4 files changed, 444 insertions(+) create mode 100644 CP02/README.md create mode 100755 CP02/extract_firmware.sh create mode 100755 CP02/merge_2323_firmware.sh create mode 100755 CP02/split_and_merge.sh diff --git a/CP02/README.md b/CP02/README.md new file mode 100644 index 0000000..ff8229c --- /dev/null +++ b/CP02/README.md @@ -0,0 +1,43 @@ +# CP02 固件处理工具 + +## 文件说明 + +### split_and_merge.sh +- **功能**: 切分固件文件并与 updater.bin 合并,用于制作 40W 固件 +- **用法**: `./split_and_merge.sh <待切分文件> <输出文件名>` +- **描述**: + - 将输入固件文件切分为前面部分和最后256字节 + - 调用合并脚本生成最终固件 + - 自动删除最后1字节并检查文件大小限制 + +### merge_2323_firmware.sh +- **功能**: 合并 PA768 updater、2323固件和IUM数据为单一固件文件 +- **用法**: `./merge_2323_firmware.sh -u -f -i -o ` +- **描述**: + - 创建基础0xFF填充文件 + - 在指定偏移位置写入各组件 + - 生成固件元数据 + - 输出完整内存布局信息 + +### extract_firmware.sh +- **功能**: 从合并的 bin 文件中提取 firmware、IUM 和 metadata +- **用法**: `./extract_firmware.sh [output_prefix]` +- **描述**: + - 从固定偏移位置提取 firmware (0x04000) + - 提取 IUM 数据 (0x1dc00, 256 bytes) + - 解析并生成 metadata.json (blocks 和 last_block_size) + - 如未指定前缀,自动使用原文件名 + +## 使用示例 + +```bash +# 切分并合并固件 +./split_and_merge.sh /Users/ching/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/looching_5217/msg/attach/61f8147fd472041d44f609e3618e827c/2025-12/Rec/ff6a18f4ab8db2a9/F/3/SW2303P_B_V1.0_00_D1F6_UFCS.bin ~/Downloads/固件/PA503_Updater.bin 40_ufcs2.bin + +# 直接合并固件组件 +./merge_2323_firmware.sh -u PA768_Updater.bin -f 2323_firmware.bin -i 2323_ium.bin -o merged.bin + +# 提取固件组件 +./extract_firmware.sh 40_ufcs2.bin firmware # 指定输出前缀 +./extract_firmware.sh merged.bin # 使用原文件名作为前缀 +``` \ No newline at end of file diff --git a/CP02/extract_firmware.sh b/CP02/extract_firmware.sh new file mode 100755 index 0000000..01cfc57 --- /dev/null +++ b/CP02/extract_firmware.sh @@ -0,0 +1,129 @@ +#!/bin/bash +# 用法: ./extract_firmware.sh [output_prefix] +# 从合并的 bin 文件中提取 head file (firmware), tail file (IUM), 以及生成 metadata.json +# +# 文件布局: +# 0x04000: firmware (head file) +# 0x1dc00: IUM (tail file, 256 bytes) +# 0x1dd00: metadata (blocks: uint16, last_block_size: uint8) + +INPUT="$1" +# 如果没有传 PREFIX,使用原文件名(去掉扩展名) +if [ -n "$2" ]; then + PREFIX="$2" +else + # 获取文件名(不含路径),然后去掉扩展名 + BASENAME=$(basename "$INPUT") + PREFIX="${BASENAME%.*}" +fi + +if [ -z "$INPUT" ]; then + echo "用法: $0 [output_prefix]" + echo "示例: $0 40_ufcs2.bin firmware" + echo " $0 40_ufcs2.bin # 使用原文件名作为前缀" + echo "" + echo "输出文件:" + echo " {prefix}.firmware - firmware 部分" + echo " {prefix}.ium - IUM 部分 (256 bytes)" + echo " {prefix}_metadata.json - 元数据" + echo "" + echo "注: 如果不指定 output_prefix,将使用原文件名(去掉扩展名)" + exit 1 +fi + +if [ ! -f "$INPUT" ]; then + echo "错误: 文件不存在: $INPUT" + exit 1 +fi + +# 常量定义 +FIRMWARE_OFFSET=$((0x4000)) # 16384 +IUM_OFFSET=$((0x1dc00)) # 121856 +METADATA_OFFSET=$((0x1dd00)) # 122112 +IUM_SIZE=256 + +# 获取文件大小 +if stat --version >/dev/null 2>&1; then + FILE_SIZE=$(stat -c%s "$INPUT") # Linux +else + FILE_SIZE=$(stat -f%z "$INPUT") # macOS +fi + +echo "输入文件: $INPUT" +echo "文件大小: $FILE_SIZE bytes (0x$(printf '%x' $FILE_SIZE))" +echo "" + +# 读取 metadata (位于 0x1dd00) +# 结构: uint16_t blocks (little-endian), uint8_t last_block_size +echo "读取 metadata (offset 0x1dd00)..." + +METADATA=$(dd if="$INPUT" bs=1 skip=$METADATA_OFFSET count=3 2>/dev/null | xxd -p) + +if [ ${#METADATA} -lt 6 ]; then + echo "错误: 无法读取 metadata" + exit 1 +fi + +# 解析 little-endian uint16 (blocks) 和 uint8 (last_block_size) +BLOCKS_LOW=$((16#${METADATA:0:2})) +BLOCKS_HIGH=$((16#${METADATA:2:2})) +BLOCKS=$(( BLOCKS_LOW + BLOCKS_HIGH * 256 )) +LAST_BLOCK_SIZE=$((16#${METADATA:4:2})) + +echo " blocks: $BLOCKS (0x$(printf '%04x' $BLOCKS))" +echo " last_block_size: $LAST_BLOCK_SIZE (0x$(printf '%02x' $LAST_BLOCK_SIZE))" + +# 计算 firmware 大小 +FIRMWARE_SIZE=$(( BLOCKS * 256 + LAST_BLOCK_SIZE )) +echo " 计算出 firmware 大小: $FIRMWARE_SIZE bytes" +echo "" + +# 提取 head file (firmware: 从 0x4000 开始) +HEAD_FILE="${PREFIX}.firmware" +echo "提取 head file (firmware)..." +dd if="$INPUT" of="$HEAD_FILE" bs=1 skip=$FIRMWARE_OFFSET count=$FIRMWARE_SIZE status=none + +if [ $? -eq 0 ]; then + ACTUAL_HEAD_SIZE=$(stat -f%z "$HEAD_FILE" 2>/dev/null || stat -c%s "$HEAD_FILE" 2>/dev/null) + echo " ✓ 已保存: $HEAD_FILE ($ACTUAL_HEAD_SIZE bytes)" +else + echo " ✗ 提取失败" + exit 1 +fi + +# 提取 tail file (IUM: 从 0x1dc00 开始, 256 bytes) +TAIL_FILE="${PREFIX}.ium" +echo "提取 tail file (IUM)..." +dd if="$INPUT" of="$TAIL_FILE" bs=1 skip=$IUM_OFFSET count=$IUM_SIZE status=none + +if [ $? -eq 0 ]; then + ACTUAL_TAIL_SIZE=$(stat -f%z "$TAIL_FILE" 2>/dev/null || stat -c%s "$TAIL_FILE" 2>/dev/null) + echo " ✓ 已保存: $TAIL_FILE ($ACTUAL_TAIL_SIZE bytes)" +else + echo " ✗ 提取失败" + exit 1 +fi + +# 生成 metadata.json +METADATA_FILE="${PREFIX}_metadata.json" +echo "生成 metadata.json..." + +cat > "$METADATA_FILE" << EOF +{"blocks": "0x$(printf '%04x' $BLOCKS)", "lastBlockSize": "0x$(printf '%02x' $LAST_BLOCK_SIZE)"} +EOF + +echo " ✓ 已保存: $METADATA_FILE" +echo "" + +# 显示结果 +echo "=========================================" +echo "提取完成!" +echo "" +echo "输出文件:" +echo " Head (firmware): $HEAD_FILE ($ACTUAL_HEAD_SIZE bytes)" +echo " Tail (IUM): $TAIL_FILE ($ACTUAL_TAIL_SIZE bytes)" +echo " Metadata: $METADATA_FILE" +echo "" +echo "Metadata 内容:" +cat "$METADATA_FILE" +echo "" \ No newline at end of file diff --git a/CP02/merge_2323_firmware.sh b/CP02/merge_2323_firmware.sh new file mode 100755 index 0000000..4eb1d6b --- /dev/null +++ b/CP02/merge_2323_firmware.sh @@ -0,0 +1,188 @@ +#!/bin/bash + +# Function to show usage +show_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " -o, --output FILE Output file (default: 40w.bin)" + echo " -u, --updater FILE PA768 Updater binary file" + echo " -f, --firmware FILE 2323 firmware binary file" + echo " -i, --ium FILE 2323 IUM binary file" + echo " -h, --help Show this help message" + echo "" + echo "Example:" + echo " $0 -u PA768_Updater.bin -f 2323_firmware.bin -i 2323_ium.bin -o output.bin" + echo "" + exit 1 +} + +# Default values +OUTPUT_FILE="40w.bin" +PA768_FILE="" +FIRMWARE_FILE="" +IUM_FILE="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -o | --output) + OUTPUT_FILE="$2" + shift 2 + ;; + -u | --updater) + PA768_FILE="$2" + shift 2 + ;; + -f | --firmware) + FIRMWARE_FILE="$2" + shift 2 + ;; + -i | --ium) + IUM_FILE="$2" + shift 2 + ;; + -h | --help) + show_usage + ;; + *) + echo "Unknown option: $1" + show_usage + ;; + esac +done + +# Configuration +MAX_LENGTH=0x1df00 # 122,624 bytes + +# Convert hex to decimal for dd +MAX_LENGTH_DEC=$((MAX_LENGTH)) # 122,624 bytes +IUM_OFFSET=$((0x1dc00)) # 121,856 bytes +METADATA_OFFSET=$((0x1dd00)) # 121,088 bytes + +echo "Creating merged binary file: $OUTPUT_FILE" +echo "Maximum file size: $MAX_LENGTH ($MAX_LENGTH_DEC bytes)" +echo "" +echo "Input files:" +echo " PA768 Updater: ${PA768_FILE:-"(not specified)"}" +echo " Firmware: ${FIRMWARE_FILE:-"(not specified)"}" +echo " IUM: ${IUM_FILE:-"(not specified)"}" +echo "" + +# Step 1: Create initial file filled with 0xFF bytes +echo "Step 1: Creating base file filled with 0xFF..." +dd if=/dev/zero bs=1 count=$MAX_LENGTH_DEC 2>/dev/null | LC_ALL=C tr "\000" "\377" >"$OUTPUT_FILE" + +if [ $? -ne 0 ]; then + echo "Error: Failed to create base file" + exit 1 +fi + +# Step 2: Write PA768_Updater.bin at offset 0 (entire file) +echo "Step 2: Writing PA768_Updater.bin at offset 0..." +if [ -n "$PA768_FILE" ] && [ -f "$PA768_FILE" ]; then + UPDATER_SIZE=$(stat -f%z "$PA768_FILE" 2>/dev/null || stat -c%s "$PA768_FILE" 2>/dev/null) + echo " Updater size: $UPDATER_SIZE bytes" + dd if="$PA768_FILE" of="$OUTPUT_FILE" bs=1 conv=notrunc 2>/dev/null + echo " ✓ PA768_Updater.bin written successfully" +elif [ -n "$PA768_FILE" ]; then + echo " ✗ Error: $PA768_FILE not found" + exit 1 +else + echo " - PA768_Updater.bin skipped (not specified)" +fi + +# Step 3: Write firmware at offset 0x4000 (16,384) - entire file +echo "Step 3: Writing firmware at offset 0x4000..." +if [ -n "$FIRMWARE_FILE" ] && [ -f "$FIRMWARE_FILE" ]; then + # Get firmware file size first + FIRMWARE_SIZE=$(stat -f%z "$FIRMWARE_FILE" 2>/dev/null || stat -c%s "$FIRMWARE_FILE" 2>/dev/null) + + # Calculate blocks for 2323_firmware.bin + BLOCK_SIZE=256 + FIRMWARE_BLOCKS=$((FIRMWARE_SIZE / BLOCK_SIZE)) + LAST_BLOCK_SIZE=$((FIRMWARE_SIZE % BLOCK_SIZE)) + + echo " Firmware size: $FIRMWARE_SIZE bytes" + echo " Complete blocks: $FIRMWARE_BLOCKS" + echo " Last block size: $LAST_BLOCK_SIZE bytes" + + dd if="$FIRMWARE_FILE" of="$OUTPUT_FILE" bs=1 seek=16384 conv=notrunc 2>/dev/null + echo " ✓ 2323_firmware.bin written successfully" + + # Write metadata at 0x1dd00 + echo "Step 3a: Writing firmware metadata at offset 0x1dd00..." + + # Create metadata structure using Python for reliable binary output + python3 -c " +import struct +blocks = $FIRMWARE_BLOCKS +last_size = $LAST_BLOCK_SIZE +# struct UserFirmwareMetadata: uint16_t blocks, uint8_t last_block_size, uint8_t reserved[13] +metadata = struct.pack('/dev/null + rm temp_metadata.bin + echo " ✓ Firmware metadata written successfully" + +elif [ -n "$FIRMWARE_FILE" ]; then + echo " ✗ Error: $FIRMWARE_FILE not found" + exit 1 +else + echo " - Firmware skipped (not specified)" + FIRMWARE_SIZE=0 + FIRMWARE_BLOCKS=0 + LAST_BLOCK_SIZE=0 +fi + +# Step 4: Write IUM data at offset 0x1dc00 (121,856) +echo "Step 4: Writing IUM data at offset 0x1dc00..." +if [ -n "$IUM_FILE" ] && [ -f "$IUM_FILE" ]; then + IUM_SIZE=$(stat -f%z "$IUM_FILE" 2>/dev/null || stat -c%s "$IUM_FILE" 2>/dev/null) + echo " IUM size: $IUM_SIZE bytes" + dd if="$IUM_FILE" of="$OUTPUT_FILE" bs=1 count=256 seek=$IUM_OFFSET conv=notrunc 2>/dev/null + echo " ✓ 2323_ium.bin written successfully" +elif [ -n "$IUM_FILE" ]; then + echo " ✗ Error: $IUM_FILE not found" + exit 1 +else + echo " - IUM data skipped (not specified)" +fi + +# Verify final file size +ACTUAL_SIZE=$(stat -f%z "$OUTPUT_FILE" 2>/dev/null || stat -c%s "$OUTPUT_FILE" 2>/dev/null) +echo "" +echo "Merge completed successfully!" +echo "Output file: $OUTPUT_FILE" +echo "Expected size: $MAX_LENGTH_DEC bytes" +echo "Actual size: $ACTUAL_SIZE bytes" + +if [ "$ACTUAL_SIZE" -eq "$MAX_LENGTH_DEC" ]; then + echo "✓ File size matches expected length" +else + echo "⚠ Warning: File size mismatch" +fi + +# Show memory layout with firmware blocks +echo "" +echo "Memory Layout:" +if [ -n "$PA768_FILE" ]; then + echo " 0x00000 - varies: PA768_Updater.bin (entire file)" +fi +if [ -n "$FIRMWARE_FILE" ] && [ $FIRMWARE_SIZE -gt 0 ]; then + FIRMWARE_END=$((16384 + FIRMWARE_SIZE)) + echo " 0x04000 - 0x$(printf '%x' $((FIRMWARE_END - 1))): 2323_firmware.bin ($FIRMWARE_SIZE bytes, $FIRMWARE_BLOCKS blocks + $LAST_BLOCK_SIZE bytes)" + echo " 0x1dd00 - 0x1dd0f: Firmware metadata (blocks=$FIRMWARE_BLOCKS, last_block_size=$LAST_BLOCK_SIZE)" +fi +if [ -n "$IUM_FILE" ]; then + echo " 0x1dc00 - 0x1dcff: 2323_ium.bin (256 bytes)" +fi +echo " Total size: $ACTUAL_SIZE bytes (0x$(printf '%x' $ACTUAL_SIZE))" + +echo "" +echo "Use 'hexdump -C $OUTPUT_FILE | grep 1dd00' to verify metadata" diff --git a/CP02/split_and_merge.sh b/CP02/split_and_merge.sh new file mode 100755 index 0000000..e91343c --- /dev/null +++ b/CP02/split_and_merge.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# 用法: ./split256_and_merge.sh <待切分文件> <输出文件名> +# 示例: ./split256_and_merge.sh /Users/ching/Downloads/4_SW2303P_B_V1.0_00_E5C3.bin ~/Downloads/PA503_Updater.bin 40_4.bin + +input="$1" +updater="$2" +output="$3" + +if [ -z "$input" ] || [ -z "$updater" ] || [ -z "$output" ]; then + echo "用法: $0 <待切分文件> <输出文件名>" + exit 1 +fi + +# 检查文件是否存在 +if [ ! -f "$input" ]; then + echo "文件不存在: $input" + exit 1 +fi +if [ ! -f "$updater" ]; then + echo "文件不存在: $updater" + exit 1 +fi + +# 获取文件大小(兼容 Linux / macOS) +if stat --version >/dev/null 2>&1; then + size=$(stat -c%s "$input") # Linux +else + size=$(stat -f%z "$input") # macOS +fi + +if [ "$size" -lt 256 ]; then + echo "文件太小 (<256 字节),无法切分" + exit 1 +fi + +head_size=$((size - 256)) + +# 输出文件名 +head_file="${input}.head" +tail_file="${input}.tail" + +# 切分 +dd if="$input" of="$head_file" bs=1 count=$head_size status=none +dd if="$input" of="$tail_file" bs=1 skip=$head_size status=none + +echo "切分完成:" +echo " 前面部分: $head_file ($head_size 字节)" +echo " 最后部分: $tail_file (256 字节)" + +# 调用 merge 脚本 +if ./merge_2323_firmware.sh -u "$updater" -f "$head_file" -i "$tail_file" -o "$output"; then + echo "✅ 合并成功,输出文件: $output" + + # 获取输出文件大小 + if stat --version >/dev/null 2>&1; then + out_size=$(stat -c%s "$output") # Linux + else + out_size=$(stat -f%z "$output") # macOS + fi + + # 删除最后 1 字节 + if [ "$out_size" -gt 0 ]; then + if command -v truncate >/dev/null 2>&1 && truncate -s $((out_size - 1)) "$output" 2>/dev/null; then + echo "已删除最后 1 字节(使用 truncate)" + else + # 使用 dd 备用方案 + dd if="$output" of="${output}.tmp" bs=1 count=$((out_size - 1)) status=none && mv "${output}.tmp" "$output" + echo "已删除最后 1 字节(使用 dd)" + fi + echo "最终文件大小: $(stat -c%s "$output" 2>/dev/null || stat -f%z "$output")" + fi + + # 检查是否 < 0x1df00 + final_size=$(stat -c%s "$output" 2>/dev/null || stat -f%z "$output") + if [ "$final_size" -gt 122624 ]; then + echo "⚠️ 警告: 文件大小仍然 >= 0x1df00 (当前 $final_size 字节)" + fi + + # 清理临时文件 + rm -f "$head_file" "$tail_file" + echo "⚠️⚠️⚠️ Remember signing output file! ⚠️⚠️⚠️" +else + echo "合并失败,保留临时文件以便排查" +fi