feat(cp02): add firmware processing and signing utilities
- Add get_last_bytes.sh to extract final 2 bytes from IUM files - Add read_bytes.sh for reading specific byte offsets from binaries - Add pack_resources.sh to package and upload CP02S resources to S3 - Add signer_new.py for firmware and bootloader signing with custom SHA1 - Update extract_firmware.sh to use fixed output filenames - Update README.md with comprehensive documentation for new tools
This commit is contained in:
parent
c86d02552f
commit
8f928a93e8
@ -28,6 +28,49 @@
|
|||||||
- 解析并生成 metadata.json (blocks 和 last_block_size)
|
- 解析并生成 metadata.json (blocks 和 last_block_size)
|
||||||
- 如未指定前缀,自动使用原文件名
|
- 如未指定前缀,自动使用原文件名
|
||||||
|
|
||||||
|
### get_last_bytes.sh
|
||||||
|
- **功能**: 获取 IUM 文件的最后两个字节并以十六进制格式返回
|
||||||
|
- **用法**: `./get_last_bytes.sh [ium_file_path]`
|
||||||
|
- **描述**:
|
||||||
|
- 读取 IUM 文件的最后 2 个字节
|
||||||
|
- 反转字节顺序(交换第一和第二个字节)
|
||||||
|
- 以十六进制格式输出结果
|
||||||
|
- 如未指定文件路径,默认使用 ium.bin
|
||||||
|
|
||||||
|
### read_bytes.sh
|
||||||
|
- **功能**: 从二进制文件的特定偏移量读取 2 个字节
|
||||||
|
- **用法**: `./read_bytes.sh <bin_file>`
|
||||||
|
- **描述**:
|
||||||
|
- 从偏移量 0x1dcfe 读取 2 个字节
|
||||||
|
- 反转字节顺序(小端序转换)
|
||||||
|
- 返回十六进制格式的结果
|
||||||
|
- 包含文件大小和偏移量验证
|
||||||
|
|
||||||
|
### pack_resources.sh
|
||||||
|
- **功能**: 打包 CP02S 资源目录中的 .bin 文件
|
||||||
|
- **用法**: `./pack_resources.sh`(无参数)
|
||||||
|
- **描述**:
|
||||||
|
- 从指定目录收集所有 .bin 文件
|
||||||
|
- 创建包含 resources/ 作为顶级目录的 tar.gz 压缩包
|
||||||
|
- 自动上传到 S3 存储(需要配置 AWS CLI)
|
||||||
|
- 源目录:`/Users/ching/develop/IonBridge/files/CP02S/littlefs/resources`
|
||||||
|
|
||||||
|
### signer_new.py
|
||||||
|
- **功能**: 用于签名 CP02 固件和引导加载程序的 Python 工具
|
||||||
|
- **用法**: `python3 signer_new.py --output_dir <output_directory> [options]`
|
||||||
|
- **选项**:
|
||||||
|
- `--firmware <path>` - 未签名的用户应用程序固件路径
|
||||||
|
- `--bootloader <path>` - 未签名的引导加载程序路径
|
||||||
|
- `--output_dir <path>` - 保存签名文件的目录(必需)
|
||||||
|
- `--enable_swd` - 启用 SWD 功能
|
||||||
|
- `--boot_directly` - 启用直接引导到用户应用程序
|
||||||
|
- **描述**:
|
||||||
|
- 实现自定义 SHA1 签名算法(基于逆向工程)
|
||||||
|
- 为固件和引导加载程序生成签名的二进制文件
|
||||||
|
- 创建包含版本号、校验和和版权信息的元数据
|
||||||
|
- 支持多种芯片 ID 的引导加载程序变体
|
||||||
|
- 输出签名的二进制文件和相关 JSON 元数据
|
||||||
|
|
||||||
## 使用示例
|
## 使用示例
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -40,4 +83,19 @@
|
|||||||
# 提取固件组件
|
# 提取固件组件
|
||||||
./extract_firmware.sh 40_ufcs2.bin firmware # 指定输出前缀
|
./extract_firmware.sh 40_ufcs2.bin firmware # 指定输出前缀
|
||||||
./extract_firmware.sh merged.bin # 使用原文件名作为前缀
|
./extract_firmware.sh merged.bin # 使用原文件名作为前缀
|
||||||
|
|
||||||
|
# 获取 IUM 文件的最后两个字节
|
||||||
|
./get_last_bytes.sh ium.bin # 使用默认文件
|
||||||
|
./get_last_bytes.sh custom_ium.bin # 指定文件路径
|
||||||
|
|
||||||
|
# 从二进制文件读取特定偏移量的字节
|
||||||
|
./read_bytes.sh 40_ufcs2.bin
|
||||||
|
|
||||||
|
# 打包资源文件
|
||||||
|
./pack_resources.sh # 打包并上传到 S3
|
||||||
|
|
||||||
|
# 签名固件
|
||||||
|
python3 signer_new.py --firmware firmware.bin --output_dir ./output
|
||||||
|
python3 signer_new.py --bootloader bootloader.bin --output_dir ./output --enable_swd
|
||||||
|
python3 signer_new.py --firmware firmware.bin --bootloader bootloader.bin --output_dir ./output --boot_directly
|
||||||
```
|
```
|
||||||
@ -79,7 +79,7 @@ echo " 计算出 firmware 大小: $FIRMWARE_SIZE bytes"
|
|||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# 提取 head file (firmware: 从 0x4000 开始)
|
# 提取 head file (firmware: 从 0x4000 开始)
|
||||||
HEAD_FILE="${PREFIX}.firmware"
|
HEAD_FILE="firmware.bin"
|
||||||
echo "提取 head file (firmware)..."
|
echo "提取 head file (firmware)..."
|
||||||
dd if="$INPUT" of="$HEAD_FILE" bs=1 skip=$FIRMWARE_OFFSET count=$FIRMWARE_SIZE status=none
|
dd if="$INPUT" of="$HEAD_FILE" bs=1 skip=$FIRMWARE_OFFSET count=$FIRMWARE_SIZE status=none
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 提取 tail file (IUM: 从 0x1dc00 开始, 256 bytes)
|
# 提取 tail file (IUM: 从 0x1dc00 开始, 256 bytes)
|
||||||
TAIL_FILE="${PREFIX}.ium"
|
TAIL_FILE="ium.bin"
|
||||||
echo "提取 tail file (IUM)..."
|
echo "提取 tail file (IUM)..."
|
||||||
dd if="$INPUT" of="$TAIL_FILE" bs=1 skip=$IUM_OFFSET count=$IUM_SIZE status=none
|
dd if="$INPUT" of="$TAIL_FILE" bs=1 skip=$IUM_OFFSET count=$IUM_SIZE status=none
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 生成 metadata.json
|
# 生成 metadata.json
|
||||||
METADATA_FILE="${PREFIX}_metadata.json"
|
METADATA_FILE="metadata.json"
|
||||||
echo "生成 metadata.json..."
|
echo "生成 metadata.json..."
|
||||||
|
|
||||||
cat > "$METADATA_FILE" << EOF
|
cat > "$METADATA_FILE" << EOF
|
||||||
|
|||||||
28
CP02/get_last_bytes.sh
Executable file
28
CP02/get_last_bytes.sh
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to get the last two bytes of ium file
|
||||||
|
# Returns the bytes in 0x format
|
||||||
|
# Usage: ./get_last_bytes.sh [ium_file_path]
|
||||||
|
|
||||||
|
# Use provided path or default to ium.bin
|
||||||
|
ium_file=${1:-ium.bin}
|
||||||
|
|
||||||
|
if [ ! -f "$ium_file" ]; then
|
||||||
|
echo "Error: $ium_file file not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get file size
|
||||||
|
file_size=$(stat -f%z "$ium_file")
|
||||||
|
|
||||||
|
if [ "$file_size" -lt 2 ]; then
|
||||||
|
echo "Error: File is too small (less than 2 bytes)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract last 2 bytes and reverse byte order
|
||||||
|
last_bytes=$(xxd -s -2 "$ium_file" | cut -d' ' -f2 | tr -d '\n')
|
||||||
|
# Reverse the byte order (swap first and second byte)
|
||||||
|
byte1=${last_bytes:0:2}
|
||||||
|
byte2=${last_bytes:2:2}
|
||||||
|
echo "${byte2}${byte1}"
|
||||||
71
CP02/pack_resources.sh
Executable file
71
CP02/pack_resources.sh
Executable file
@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script to pack CP02S resources directory containing only .bin files
|
||||||
|
# Creates tar.gz with resources as top-level directory
|
||||||
|
|
||||||
|
SOURCE_DIR="/Users/ching/develop/IonBridge/files/CP02S/littlefs/resources"
|
||||||
|
OUTPUT_FILE="cp02s_resources.tar.gz"
|
||||||
|
|
||||||
|
# Check if source directory exists
|
||||||
|
if [ ! -d "$SOURCE_DIR" ]; then
|
||||||
|
echo "Error: Source directory does not exist: $SOURCE_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create temporary directory structure
|
||||||
|
TEMP_DIR=$(mktemp -d)
|
||||||
|
TEMP_RESOURCES="$TEMP_DIR/resources"
|
||||||
|
|
||||||
|
# Create resources directory in temp
|
||||||
|
mkdir -p "$TEMP_RESOURCES"
|
||||||
|
|
||||||
|
# Copy only .bin files preserving directory structure
|
||||||
|
bin_count=0
|
||||||
|
find "$SOURCE_DIR" -name "*.bin" -type f | while read -r file; do
|
||||||
|
# Get relative path from source directory
|
||||||
|
relative_path="${file#$SOURCE_DIR/}"
|
||||||
|
target_path="$TEMP_RESOURCES/$relative_path"
|
||||||
|
|
||||||
|
# Create target directory if needed
|
||||||
|
mkdir -p "$(dirname "$target_path")"
|
||||||
|
|
||||||
|
# Copy the file
|
||||||
|
cp "$file" "$target_path"
|
||||||
|
((bin_count++))
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if any .bin files were found
|
||||||
|
bin_files=$(find "$TEMP_RESOURCES" -name "*.bin" 2>/dev/null | wc -l)
|
||||||
|
if [ "$bin_files" -eq 0 ]; then
|
||||||
|
echo "Warning: No .bin files found in $SOURCE_DIR"
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Save current directory before changing
|
||||||
|
ORIGINAL_DIR="$PWD"
|
||||||
|
|
||||||
|
# Create tar.gz from temp directory
|
||||||
|
cd "$TEMP_DIR"
|
||||||
|
tar -czf "$OUTPUT_FILE" resources/
|
||||||
|
|
||||||
|
# Move the archive back to original directory
|
||||||
|
mv "$OUTPUT_FILE" "$ORIGINAL_DIR/"
|
||||||
|
|
||||||
|
# Return to original directory
|
||||||
|
cd "$ORIGINAL_DIR"
|
||||||
|
|
||||||
|
# Cleanup temp directory
|
||||||
|
rm -rf "$TEMP_DIR"
|
||||||
|
|
||||||
|
echo "Created $OUTPUT_FILE with resources/ containing $bin_files .bin files"
|
||||||
|
|
||||||
|
# Upload to S3
|
||||||
|
echo "Uploading $OUTPUT_FILE to S3..."
|
||||||
|
if aws --endpoint=https://s3.cn-southeast-1.ifanrprod.com --profile=iot s3 cp "$OUTPUT_FILE" s3://iot-private/batch_input/; then
|
||||||
|
echo "Successfully uploaded $OUTPUT_FILE to S3"
|
||||||
|
else
|
||||||
|
echo "Error: Failed to upload to S3"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
32
CP02/read_bytes.sh
Executable file
32
CP02/read_bytes.sh
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
echo "Usage: $0 <bin_file>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BIN_FILE="$1"
|
||||||
|
OFFSET=0x1dcfe
|
||||||
|
|
||||||
|
if [ ! -f "$BIN_FILE" ]; then
|
||||||
|
echo "Error: File '$BIN_FILE' not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
FILE_SIZE=$(stat -f%z "$BIN_FILE" 2>/dev/null || stat -c%s "$BIN_FILE" 2>/dev/null)
|
||||||
|
DECIMAL_OFFSET=$((OFFSET))
|
||||||
|
|
||||||
|
if [ $DECIMAL_OFFSET -ge $FILE_SIZE ]; then
|
||||||
|
echo "Error: Offset $OFFSET ($DECIMAL_OFFSET) exceeds file size ($FILE_SIZE bytes)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $((DECIMAL_OFFSET + 2)) -gt $FILE_SIZE ]; then
|
||||||
|
echo "Error: Cannot read 2 bytes starting from offset $OFFSET (file too small)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BYTES=$(xxd -s $DECIMAL_OFFSET -l 2 -p "$BIN_FILE")
|
||||||
|
BYTE1=$(echo "$BYTES" | cut -c1-2)
|
||||||
|
BYTE2=$(echo "$BYTES" | cut -c3-4)
|
||||||
|
echo "$BYTE2$BYTE1"
|
||||||
266
CP02/signer_new.py
Normal file
266
CP02/signer_new.py
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from absl import app
|
||||||
|
from absl import flags
|
||||||
|
from absl import logging
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
import struct
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
# Define command-line flags
|
||||||
|
flags.DEFINE_string('signer', None, 'Path to the unsigned user application firmware.')
|
||||||
|
flags.DEFINE_string('firmware', None, 'Path to the unsigned user application firmware.')
|
||||||
|
flags.DEFINE_string('bootloader', None, 'Path to the unsigned bootloader.')
|
||||||
|
flags.DEFINE_string('output_dir', None, 'Directory to save the signed files.')
|
||||||
|
flags.DEFINE_boolean('enable_swd', False, 'Enable SWD feature')
|
||||||
|
flags.DEFINE_boolean('boot_directly', False, 'Enable boot directly to user application')
|
||||||
|
|
||||||
|
# Ensure the flags are required
|
||||||
|
flags.mark_flag_as_required('output_dir')
|
||||||
|
|
||||||
|
def leftrotate(x, n):
|
||||||
|
"""Left rotate a 32-bit integer x by n bits."""
|
||||||
|
return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
def sha1_signer_python(data):
|
||||||
|
"""Python implementation matching sha1_signer binary exactly (based on reverse engineering)"""
|
||||||
|
BLOCK_LEN = 64
|
||||||
|
FIXED_SIZE = 0x1DF00 # sha1_signer processes exactly 0x1DF00 bytes
|
||||||
|
|
||||||
|
# Create fixed-size buffer initialized with zeros (malloc + memset in C)
|
||||||
|
buffer = bytearray(FIXED_SIZE)
|
||||||
|
|
||||||
|
# Copy file data to buffer (fread in C)
|
||||||
|
data_to_copy = min(len(data), FIXED_SIZE)
|
||||||
|
buffer[:data_to_copy] = data[:data_to_copy]
|
||||||
|
# Remaining bytes stay as 0
|
||||||
|
|
||||||
|
# Custom initial hash values (confirmed from reverse engineering)
|
||||||
|
hash_state = [0xef343ca7, 0x5c41a2de, 0x7ce9704f, 0x62312814, 0x4de11904]
|
||||||
|
|
||||||
|
def sha1_compress(block, state):
|
||||||
|
"""Compress one 64-byte block (16 rounds only, confirmed from disassembly)"""
|
||||||
|
a, b, c, d, e = state[0], state[1], state[2], state[3], state[4]
|
||||||
|
|
||||||
|
# Process only 16 rounds (cmpl $0xf in disassembly)
|
||||||
|
for i in range(16):
|
||||||
|
# Load schedule using big-endian (confirmed from disassembly)
|
||||||
|
schedule = (block[i * 4] << 24) | (block[i * 4 + 1] << 16) | \
|
||||||
|
(block[i * 4 + 2] << 8) | block[i * 4 + 3]
|
||||||
|
|
||||||
|
# Round function with constant 0x7ebce2a7 (confirmed from disassembly)
|
||||||
|
f = ((b & c) | (~b & d)) & 0xFFFFFFFF
|
||||||
|
temp = (leftrotate(a, 5) + f + e + schedule + 0x7ebce2a7) & 0xFFFFFFFF
|
||||||
|
e = d
|
||||||
|
d = c
|
||||||
|
c = leftrotate(b, 30)
|
||||||
|
b = a
|
||||||
|
a = temp
|
||||||
|
|
||||||
|
# Update state
|
||||||
|
state[0] = (state[0] + a) & 0xFFFFFFFF
|
||||||
|
state[1] = (state[1] + b) & 0xFFFFFFFF
|
||||||
|
state[2] = (state[2] + c) & 0xFFFFFFFF
|
||||||
|
state[3] = (state[3] + d) & 0xFFFFFFFF
|
||||||
|
state[4] = (state[4] + e) & 0xFFFFFFFF
|
||||||
|
|
||||||
|
# Process complete 64-byte blocks from fixed-size buffer
|
||||||
|
data_len = FIXED_SIZE
|
||||||
|
offset = 0
|
||||||
|
while data_len - offset >= BLOCK_LEN:
|
||||||
|
sha1_compress(buffer[offset:offset + BLOCK_LEN], hash_state)
|
||||||
|
offset += BLOCK_LEN
|
||||||
|
|
||||||
|
# Handle remaining bytes and padding (standard SHA1 padding)
|
||||||
|
block = bytearray(BLOCK_LEN)
|
||||||
|
rem = data_len - offset
|
||||||
|
if rem > 0:
|
||||||
|
block[0:rem] = buffer[offset:offset + rem]
|
||||||
|
|
||||||
|
# Add padding byte
|
||||||
|
block[rem] = 0x80
|
||||||
|
rem += 1
|
||||||
|
|
||||||
|
# If not enough room for length, process this block and start a new one
|
||||||
|
LENGTH_SIZE = 8
|
||||||
|
if BLOCK_LEN - rem < LENGTH_SIZE:
|
||||||
|
sha1_compress(bytes(block), hash_state)
|
||||||
|
block = bytearray(BLOCK_LEN)
|
||||||
|
|
||||||
|
# Add message length in bits (big-endian, standard SHA1 encoding)
|
||||||
|
length_bits = data_len * 8
|
||||||
|
for i in range(LENGTH_SIZE):
|
||||||
|
block[BLOCK_LEN - 1 - i] = (length_bits >> (i * 8)) & 0xFF
|
||||||
|
|
||||||
|
# Process final block
|
||||||
|
sha1_compress(bytes(block), hash_state)
|
||||||
|
|
||||||
|
return hash_state
|
||||||
|
|
||||||
|
def generate_metadata_python(file_path):
|
||||||
|
"""Generate metadata using Python implementation of sha1_signer."""
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
|
|
||||||
|
# Use exact clone of sha1_signer binary (based on reverse engineering)
|
||||||
|
hash_values = sha1_signer_python(data)
|
||||||
|
|
||||||
|
# Convert hash values to hex strings (same format as original)
|
||||||
|
checksum = [f"{val:08x}" for val in hash_values]
|
||||||
|
|
||||||
|
# Generate version number using UTC date format (YYYYMMDDHH)
|
||||||
|
utc_now = datetime.datetime.utcnow()
|
||||||
|
version_number = int(f"{utc_now.year:04d}{utc_now.month:02d}{utc_now.day:02d}{utc_now.hour:02d}")
|
||||||
|
|
||||||
|
# Set copyright
|
||||||
|
copyright_text = "Copyright (c) ifanr Inc, All Rights Reserved."
|
||||||
|
|
||||||
|
return {
|
||||||
|
"version_number": version_number,
|
||||||
|
"checksum": checksum,
|
||||||
|
"copyright": copyright_text
|
||||||
|
}
|
||||||
|
|
||||||
|
def extract_release_id(file_path):
|
||||||
|
"""Extract the release ID from the binary."""
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
f.seek(0xA8)
|
||||||
|
release_id = struct.unpack('<I', f.read(4))[0]
|
||||||
|
return f"{release_id:x}"
|
||||||
|
|
||||||
|
def modify_firmware(firmware_bin, metadata, enable_swd = False, boot_directly = False):
|
||||||
|
"""Modifies the firmware binary based on the metadata."""
|
||||||
|
if not enable_swd:
|
||||||
|
# Insert 0xb10cdeb9 at 0x1DF10 to disable SWD
|
||||||
|
firmware_bin = (
|
||||||
|
firmware_bin[:0x1DF10] # Everything before 0x1DF10
|
||||||
|
+ struct.pack("<I", 0xb10cdeb9) # Insert 0xb10cdeb9 as 4-byte little-endian
|
||||||
|
+ firmware_bin[0x1DF14:] # Everything after 0x1DF10
|
||||||
|
)
|
||||||
|
if boot_directly:
|
||||||
|
# Insert 0xb007b007 at 0x1DF14 to boot directly to user application
|
||||||
|
firmware_bin = (
|
||||||
|
firmware_bin[:0x1DF14] # Everything before 0x1DF14
|
||||||
|
+ struct.pack("<I", 0xb007b007) # Insert 0xb007b007 as 4-byte little-endian
|
||||||
|
+ firmware_bin[0x1DF18:] # Everything after 0x1DF14
|
||||||
|
)
|
||||||
|
|
||||||
|
version_number = metadata.get("version_number", 0)
|
||||||
|
firmware_bin = (
|
||||||
|
firmware_bin[:0x1DFF0]
|
||||||
|
+ struct.pack("<I", version_number)
|
||||||
|
+ firmware_bin[0x1DFF4:]
|
||||||
|
)
|
||||||
|
|
||||||
|
checksum = metadata.get("checksum", [0] * 5)
|
||||||
|
for i, hexstr in enumerate(checksum):
|
||||||
|
firmware_bin = (
|
||||||
|
firmware_bin[: 0x1DFA0 + i * 4]
|
||||||
|
+ struct.pack("<I", int(hexstr, 16))
|
||||||
|
+ firmware_bin[0x1DFA4 + i * 4 :]
|
||||||
|
)
|
||||||
|
|
||||||
|
copyright = metadata.get("copyright", "").encode()
|
||||||
|
copyright = copyright[:0x30]
|
||||||
|
if len(copyright) < 0x30:
|
||||||
|
copyright += b"\x00" * (0x30 - len(copyright))
|
||||||
|
firmware_bin = firmware_bin[:0x1DFC0] + copyright + firmware_bin[0x1DFEF + 1 :]
|
||||||
|
|
||||||
|
return firmware_bin
|
||||||
|
|
||||||
|
def modify_bootloader(firmware_bin, metadata):
|
||||||
|
"""Modifies the bootloader binary based on the metadata."""
|
||||||
|
version_number = metadata.get('version_number', 0)
|
||||||
|
firmware_bin = firmware_bin[:0x1ff4] + struct.pack('<I', version_number) + firmware_bin[0x1ff8:]
|
||||||
|
|
||||||
|
flash_timestamp = metadata.get('flash_timestamp', 0)
|
||||||
|
firmware_bin = firmware_bin[:0x1ff8] + struct.pack('<I', flash_timestamp) + firmware_bin[0x1ffc:]
|
||||||
|
|
||||||
|
chip_id = metadata.get('chip_id', 0)
|
||||||
|
firmware_bin = firmware_bin[:0x1ffe] + struct.pack('<B', chip_id) + firmware_bin[0x1fff:]
|
||||||
|
|
||||||
|
copyright = metadata.get('copyright', '').encode()
|
||||||
|
copyright = copyright[:0x30]
|
||||||
|
if len(copyright) < 0x30:
|
||||||
|
copyright += b'\x00' * (0x30 - len(copyright))
|
||||||
|
firmware_bin = firmware_bin[:0x1fc0] + copyright + firmware_bin[0x1fef + 1:]
|
||||||
|
|
||||||
|
return firmware_bin
|
||||||
|
|
||||||
|
def copy_associated_files(file_path, output_dir):
|
||||||
|
"""Copy the input file and associated map, txt, and elf files to the output directory."""
|
||||||
|
base_name, _ = os.path.splitext(os.path.basename(file_path))
|
||||||
|
for ext in ['.map', '.txt', '.elf']:
|
||||||
|
src_file = file_path.replace('.bin', ext)
|
||||||
|
if os.path.exists(src_file):
|
||||||
|
shutil.copy(src_file, os.path.join(output_dir, os.path.basename(src_file)))
|
||||||
|
|
||||||
|
shutil.copy(file_path, os.path.join(output_dir, os.path.basename(file_path)))
|
||||||
|
|
||||||
|
def main(argv):
|
||||||
|
del argv # Unused.
|
||||||
|
|
||||||
|
if FLAGS.firmware:
|
||||||
|
with open(FLAGS.firmware, 'rb') as f:
|
||||||
|
firmware_bin = f.read()
|
||||||
|
|
||||||
|
if len(firmware_bin) >= 0x1DF00:
|
||||||
|
raise ValueError("Firmware is too big")
|
||||||
|
|
||||||
|
firmware_bin += b"\xFF" * (0x1DF00 - len(firmware_bin))
|
||||||
|
release_id = extract_release_id(FLAGS.firmware)
|
||||||
|
unsigned_firmware_path = os.path.join(FLAGS.output_dir, f"firmware-unsigned-{release_id}.bin")
|
||||||
|
with open(unsigned_firmware_path, 'wb') as f:
|
||||||
|
f.write(firmware_bin)
|
||||||
|
|
||||||
|
# Sign the firmware using Python implementation
|
||||||
|
metadata = generate_metadata_python(unsigned_firmware_path)
|
||||||
|
firmware_bin += b"\xFF" * 0x100
|
||||||
|
signed_firmware_path = os.path.join(FLAGS.output_dir, f"firmware-{release_id}.bin")
|
||||||
|
with open(signed_firmware_path, 'wb') as f:
|
||||||
|
modified_firmware_bin = modify_firmware(firmware_bin, metadata, FLAGS.enable_swd, FLAGS.boot_directly)
|
||||||
|
f.write(modified_firmware_bin)
|
||||||
|
logging.info('Firmware %s written.', signed_firmware_path)
|
||||||
|
|
||||||
|
# Write the metadata file
|
||||||
|
metadata_path = os.path.join(FLAGS.output_dir, f"firmware-{release_id}.json")
|
||||||
|
with open(metadata_path, 'w') as f:
|
||||||
|
json.dump(metadata, f, indent=4)
|
||||||
|
logging.info('Metadata %s written.', metadata_path)
|
||||||
|
copy_associated_files(FLAGS.firmware, FLAGS.output_dir)
|
||||||
|
|
||||||
|
if FLAGS.bootloader:
|
||||||
|
# Sign the bootloader using Python implementation
|
||||||
|
metadata = generate_metadata_python(FLAGS.bootloader)
|
||||||
|
release_id = extract_release_id(FLAGS.bootloader)
|
||||||
|
|
||||||
|
with open(FLAGS.bootloader, 'rb') as f:
|
||||||
|
bootloader_bin = f.read()
|
||||||
|
|
||||||
|
if len(bootloader_bin) > 0x1f00:
|
||||||
|
raise ValueError('Bootloader is too big')
|
||||||
|
bootloader_bin += b'\x00' * (0x1f00- len(bootloader_bin))
|
||||||
|
bootloader_bin += b'\xff' * 0x10
|
||||||
|
bootloader_bin += b'\x00' * 0xf0
|
||||||
|
|
||||||
|
for chip_id in range(5):
|
||||||
|
metadata['chip_id'] = chip_id
|
||||||
|
signed_bootloader_path = os.path.join(FLAGS.output_dir, f"bootloader-chip{chip_id}-{release_id}.bin")
|
||||||
|
with open(signed_bootloader_path, 'wb') as f:
|
||||||
|
modified_bootloader_bin = modify_bootloader(bootloader_bin, metadata)
|
||||||
|
f.write(modified_bootloader_bin)
|
||||||
|
logging.info('Bootloader %s written.', signed_bootloader_path)
|
||||||
|
|
||||||
|
# Write the metadata file
|
||||||
|
metadata_path = os.path.join(FLAGS.output_dir, f"bootloader-{release_id}.json")
|
||||||
|
with open(metadata_path, 'w') as f:
|
||||||
|
json.dump(metadata, f, indent=4)
|
||||||
|
logging.info('Metadata %s written.', metadata_path)
|
||||||
|
copy_associated_files(FLAGS.bootloader, FLAGS.output_dir)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(main)
|
||||||
Loading…
x
Reference in New Issue
Block a user