2. 固件头部解析与元数据提取

任务目标

编写一个Python脚本,对给定固件二进制文件进行头部解析,提取版本号、校验和、加载地址等关键元数据,并输出结构化结果。

固件头部格式定义

本案例使用一个自定义的、固定长度的固件头部格式(共32字节),其布局如下:

偏移大小(字节)字段说明
04魔数固定为 0xAA55AA55,用于校验文件是否为有效固件
42版本号主版本号(1字节)+ 次版本号(1字节),如0x0102表示v1.2
62头部长度头部总字节数,本格式固定为32
84固件总长度整个固件文件字节数
124加载地址固件在内存中的加载基地址
164入口点程序入口地址
204校验和从偏移32开始到文件末尾所有字节的累加和(取低32位)
248时间戳Unix时间戳(秒)

完整实现代码

import struct
import sys
from datetime import datetime

HEADER_SIZE = 32
MAGIC = 0xAA55AA55

def parse_firmware_header(filepath):
    with open(filepath, 'rb') as f:
        header = f.read(HEADER_SIZE)
        if len(header) < HEADER_SIZE:
            raise ValueError(f"文件过小,不足头部长度 {HEADER_SIZE} 字节")
        
        magic, ver_raw, hdr_len, total_len, load_addr, entry_point, checksum, timestamp = struct.unpack('<IHHIIIIQ', header)
        
        if magic != MAGIC:
            raise ValueError(f"魔数不匹配: 0x{magic:08X}, 期望 0x{MAGIC:08X}")
        if hdr_len != HEADER_SIZE:
            raise ValueError(f"头部长度字段异常: {hdr_len}, 期望 {HEADER_SIZE}")
        
        # 计算实际校验和:从偏移32到文件末尾
        f.seek(HEADER_SIZE)
        body = f.read()
        actual_checksum = sum(body) & 0xFFFFFFFF
        
        major = (ver_raw >> 8) & 0xFF
        minor = ver_raw & 0xFF
        
        return {
            'magic': f'0x{magic:08X}',
            'version': f'{major}.{minor}',
            'header_length': hdr_len,
            'total_length': total_len,
            'load_address': f'0x{load_addr:08X}',
            'entry_point': f'0x{entry_point:08X}',
            'stored_checksum': f'0x{checksum:08X}',
            'actual_checksum': f'0x{actual_checksum:08X}',
            'checksum_match': (checksum == actual_checksum),
            'timestamp': datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S UTC')
        }

def main():
    if len(sys.argv) != 2:
        print("用法: python fw_header_parser.py <固件文件路径>")
        sys.exit(1)
    
    try:
        meta = parse_firmware_header(sys.argv[1])
        print("=== 固件头部元数据 ===")
        for key, value in meta.items():
            print(f"{key}: {value}")
    except Exception as e:
        print(f"解析失败: {e}")
        sys.exit(1)

if __name__ == '__main__':
    main()

运行与验证

依赖:Python 3.6+,无需第三方库。

生成测试固件:使用以下命令创建一个符合格式的测试文件(Linux/macOS):

python3 -c "
import struct
# 构造一个最小固件:头部32字节 + 16字节数据
body = bytes(range(16))
header = struct.pack('<IHHIIIIQ', 0xAA55AA55, 0x0102, 32, 32+len(body), 0x08000000, 0x08000100, sum(body)&0xFFFFFFFF, 1700000000)
open('test_fw.bin','wb').write(header + body)
print('已生成 test_fw.bin')
"

运行解析脚本:

python3 fw_header_parser.py test_fw.bin

预期输出(与代码逻辑严格一致):

=== 固件头部元数据 ===
magic: 0xAA55AA55
version: 1.2
header_length: 32
total_length: 48
load_address: 0x08000000
entry_point: 0x08000100
stored_checksum: 0x00000078
actual_checksum: 0x00000078
checksum_match: True
timestamp: 2023-11-14 22:13:20 UTC

通过条件:脚本成功输出上述字段,且 checksum_match 为 True。若手动修改固件中任意一个数据字节后再次运行,checksum_match 应变为 False,证明校验和验证功能有效。

常见问题

  • struct.error: unpack requires a buffer of 32 bytes:文件小于32字节,不是有效固件。
  • 魔数不匹配:文件不是本格式固件,或文件偏移错误。
  • 校验和不匹配:固件数据被篡改或头部校验和字段本身有误,需检查固件生成端逻辑。