4. 固件头信息解析

任务说明

解析一段给定固件二进制文件的头部结构,提取固件版本、硬件平台、加载地址三项关键信息。固件头采用自定义格式,共32字节,字段布局如下:

  • 偏移0x00,4字节:魔数 0x4649524D("FIRM"小端)
  • 偏移0x04,2字节:硬件平台ID(1=STM32F103,2=STM32F407,3=ESP32)
  • 偏移0x06,2字节:固件主版本号
  • 偏移0x08,2字节:固件次版本号
  • 偏移0x0A,2字节:固件补丁版本号
  • 偏移0x0C,4字节:加载地址(小端)
  • 偏移0x10,16字节:保留字段(填充0xFF)

本案例使用Python标准库struct解析二进制数据,不依赖第三方包。

完整代码

import struct
import sys

# 固件头格式定义
HEADER_FORMAT = '<IHHHHI16x'  # 小端: uint32魔数, 3×uint16版本, uint32加载地址, 16字节保留
HEADER_SIZE = struct.calcsize(HEADER_FORMAT)  # 应为32

PLATFORM_NAMES = {
    1: 'STM32F103',
    2: 'STM32F407',
    3: 'ESP32'
}

def parse_firmware_header(data: bytes) -> dict:
    """解析固件头,返回包含版本、平台、加载地址的字典"""
    if len(data) < HEADER_SIZE:
        raise ValueError(f'数据长度不足:需要{HEADER_SIZE}字节,实际{len(data)}字节')

    magic, major, minor, patch, load_addr, _ = struct.unpack(HEADER_FORMAT, data[:HEADER_SIZE])

    if magic != 0x4649524D:
        raise ValueError(f'魔数校验失败:0x{magic:08X},期望0x4649524D')

    if major not in PLATFORM_NAMES:
        raise ValueError(f'未知硬件平台ID:{major}')

    return {
        'platform': PLATFORM_NAMES[major],
        'version': f'{major}.{minor}.{patch}',
        'load_address': hex(load_addr)
    }

def main():
    # 构造一个示例固件头(模拟从固件文件读取的前32字节)
    sample_header = struct.pack(
        HEADER_FORMAT,
        0x4649524D,  # 魔数
        2,           # 平台: STM32F407
        1,           # 主版本
        4,           # 次版本
        2,           # 补丁版本
        0x08010000,  # 加载地址
    )

    # 实际使用时替换为从文件读取:
    # with open('firmware.bin', 'rb') as f:
    #     sample_header = f.read(HEADER_SIZE)

    try:
        info = parse_firmware_header(sample_header)
        print(f'硬件平台: {info["platform"]}')
        print(f'固件版本: {info["version"]}')
        print(f'加载地址: {info["load_address"]}')
    except ValueError as e:
        print(f'解析失败: {e}', file=sys.stderr)
        sys.exit(1)

if __name__ == '__main__':
    main()

运行与验证

运行方式:

python3 parse_fw_header.py

预期输出:

硬件平台: STM32F407
固件版本: 1.4.2
加载地址: 0x8010000

验证方法:

  1. 将上述代码保存为parse_fw_header.py并执行,观察输出是否与预期一致。
  2. 修改sample_header中的平台ID为1或3,重新运行,确认平台名称相应变为STM32F103或ESP32。
  3. 将魔数改为0x12345678,运行后应输出解析失败: 魔数校验失败并退出码为1,验证错误处理逻辑。
  4. 实际使用时,将main()中注释的文件读取代码取消注释,替换为真实固件文件路径即可解析实际固件头。

关键说明

  • 格式字符串'<IHHHHI16x'中:<表示小端字节序,I为4字节无符号整数,H为2字节无符号整数,16x跳过16字节填充。
  • 解析结果中load_address以十六进制字符串返回,便于直接查看地址值。
  • 若固件头格式与上述不同(如字段顺序、大小端),需相应调整HEADER_FORMAT和字段偏移。