2. 固件头部解析与元数据提取
任务目标
编写一个Python脚本,对给定固件二进制文件进行头部解析,提取版本号、校验和、加载地址等关键元数据,并输出结构化结果。
固件头部格式定义
本案例使用一个自定义的、固定长度的固件头部格式(共32字节),其布局如下:
| 偏移 | 大小(字节) | 字段 | 说明 |
|---|---|---|---|
| 0 | 4 | 魔数 | 固定为 0xAA55AA55,用于校验文件是否为有效固件 |
| 4 | 2 | 版本号 | 主版本号(1字节)+ 次版本号(1字节),如0x0102表示v1.2 |
| 6 | 2 | 头部长度 | 头部总字节数,本格式固定为32 |
| 8 | 4 | 固件总长度 | 整个固件文件字节数 |
| 12 | 4 | 加载地址 | 固件在内存中的加载基地址 |
| 16 | 4 | 入口点 | 程序入口地址 |
| 20 | 4 | 校验和 | 从偏移32开始到文件末尾所有字节的累加和(取低32位) |
| 24 | 8 | 时间戳 | 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字节,不是有效固件。
- 魔数不匹配:文件不是本格式固件,或文件偏移错误。
- 校验和不匹配:固件数据被篡改或头部校验和字段本身有误,需检查固件生成端逻辑。