2. 固件头部解析

任务说明

给定一份固件二进制文件,其头部结构包含魔数、版本号、硬件平台标识、加载地址和固件长度。编写Python脚本解析该头部,提取并校验这些关键信息。本案例使用构造的示例固件文件验证解析逻辑,不涉及真实设备固件。

固件头部结构定义


偏移量  大小    字段              说明
0x00    4字节  魔数              'FWIM' (0x464D4957)
0x04    2字节  版本号主版本       uint16 LE
0x06    2字节  版本号次版本       uint16 LE
0x08    2字节  硬件平台ID         uint16 LE (1=STM32F4, 2=STM32H7, 3=ESP32)
0x0A    2字节  保留字段           置0
0x0C    4字节  加载地址           uint32 LE
0x10    4字节  固件长度           uint32 LE
0x14    4字节  头部CRC32         对偏移0x00~0x13的28字节计算

头部总长24字节(0x18)。CRC32使用标准IEEE 802.3多项式,初始值0xFFFFFFFF,结果按小端序存储。

完整实现代码

import struct
import zlib
import sys

HEADER_SIZE = 24
MAGIC = b'FWIM'
PLATFORM_NAMES = {1: 'STM32F4', 2: 'STM32H7', 3: 'ESP32'}

def parse_firmware_header(filepath):
    with open(filepath, 'rb') as f:
        data = f.read(HEADER_SIZE)
        if len(data) < HEADER_SIZE:
            raise ValueError(f'文件过小,不足头部长度 {HEADER_SIZE} 字节')
        
        magic = data[0:4]
        if magic != MAGIC:
            raise ValueError(f'魔数错误: 期望 {MAGIC!r}, 实际 {magic!r}')
        
        ver_major, ver_minor = struct.unpack_from('<HH', data, 4)
        platform_id, reserved = struct.unpack_from('<HH', data, 8)
        load_addr, fw_len = struct.unpack_from('<II', data, 12)
        crc_stored = struct.unpack_from('<I', data, 20)[0]
        
        # 校验CRC
        crc_calc = zlib.crc32(data[:20]) & 0xFFFFFFFF
        if crc_calc != crc_stored:
            raise ValueError(f'CRC校验失败: 存储 0x{crc_stored:08X}, 计算 0x{crc_calc:08X}')
        
        platform = PLATFORM_NAMES.get(platform_id, f'未知平台(0x{platform_id:04X})')
        
        return {
            'magic': magic.decode('ascii'),
            'version': f'{ver_major}.{ver_minor}',
            'platform_id': platform_id,
            'platform_name': platform,
            'load_address': hex(load_addr),
            'firmware_length': fw_len,
            'crc32': hex(crc_stored),
        }

def create_test_firmware(filepath):
    """生成一个带合法头部的测试固件文件(头部+4字节填充数据)"""
    ver_major, ver_minor = 2, 5
    platform_id = 1
    reserved = 0
    load_addr = 0x08010000
    fw_len = 4  # 仅测试数据长度
    header_without_crc = struct.pack('<4sHHHHII', MAGIC, ver_major, ver_minor,
                                     platform_id, reserved, load_addr, fw_len)
    crc = zlib.crc32(header_without_crc) & 0xFFFFFFFF
    header = header_without_crc + struct.pack('<I', crc)
    with open(filepath, 'wb') as f:
        f.write(header)
        f.write(b'\x00\x01\x02\x03')  # 模拟固件内容

if __name__ == '__main__':
    if len(sys.argv) > 1:
        fw_path = sys.argv[1]
    else:
        fw_path = 'test_fw.bin'
        create_test_firmware(fw_path)
        print(f'已生成测试固件: {fw_path}')
    
    info = parse_firmware_header(fw_path)
    print('固件头部解析结果:')
    for k, v in info.items():
        print(f'  {k}: {v}')

运行与验证

  1. 环境依赖:Python 3.6+,无需第三方库(zlib和struct为标准库)。
  2. 运行方式:python fw_header_parser.py(自动生成测试固件并解析);或 python fw_header_parser.py your_firmware.bin 解析指定文件。
  3. 通过条件:程序输出以下内容且无异常退出,即解析成功:

已生成测试固件: test_fw.bin
固件头部解析结果:
  magic: FWIM
  version: 2.5
  platform_id: 1
  platform_name: STM32F4
  load_address: 0x8010000
  firmware_length: 4
  crc32: 0x...

其中crc32为实际计算值。若手动修改头部任一字节(如版本号),程序应报CRC校验失败或魔数错误,说明校验逻辑生效。

关键说明

  • 解析器按小端序读取所有多字节字段,与常见ARM嵌入式固件约定一致。
  • CRC32覆盖头部前20字节(不含CRC字段本身),存储值位于偏移0x14。
  • 若需解析真实固件,仅需将create_test_firmware替换为实际固件文件路径,并确认头部偏移与字段定义匹配。