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
验证方法:
- 将上述代码保存为
parse_fw_header.py并执行,观察输出是否与预期一致。 - 修改
sample_header中的平台ID为1或3,重新运行,确认平台名称相应变为STM32F103或ESP32。 - 将魔数改为
0x12345678,运行后应输出解析失败: 魔数校验失败并退出码为1,验证错误处理逻辑。 - 实际使用时,将
main()中注释的文件读取代码取消注释,替换为真实固件文件路径即可解析实际固件头。
关键说明
- 格式字符串
'<IHHHHI16x'中:<表示小端字节序,I为4字节无符号整数,H为2字节无符号整数,16x跳过16字节填充。 - 解析结果中
load_address以十六进制字符串返回,便于直接查看地址值。 - 若固件头格式与上述不同(如字段顺序、大小端),需相应调整
HEADER_FORMAT和字段偏移。