2. 固件头部解析与魔数识别
任务目标
编写一个Python脚本,对给定固件文件的头部进行解析,提取魔数(magic number)、版本号、固件长度等关键字段,并根据魔数识别固件类型。本案例以常见的嵌入式固件格式(类U-Boot头部)为解析对象,输入为二进制固件文件,输出为解析后的字段信息与识别结果。
固件头部格式定义
本案例解析的固件头部为32字节定长结构,布局如下:
| 偏移 | 大小(字节) | 字段 | 说明 |
|---|---|---|---|
| 0 | 4 | magic | 魔数,0x27051956(U-Boot标准) |
| 4 | 4 | header_crc | 头部CRC32校验值 |
| 8 | 4 | timestamp | 时间戳(Unix时间) |
| 12 | 4 | version | 版本号(大端uint32) |
| 16 | 4 | image_len | 固件数据长度(不含头部) |
| 20 | 4 | image_crc | 固件数据CRC32 |
| 24 | 4 | header_version | 头部格式版本(当前为1) |
| 28 | 4 | reserved | 保留字段,应为0 |
实现代码
import struct
import sys
from pathlib import Path
# 已知魔数对应的固件类型
KNOWN_MAGIC = {
0x27051956: "U-Boot Legacy Image",
0x214E4553: "SES (Secure Embedded System)",
0xFEEDFACE: "Common Bootloader",
}
HEADER_SIZE = 32
HEADER_FORMAT = "<IIIIIIII" # 小端8个uint32
def parse_firmware_header(filepath):
data = Path(filepath).read_bytes()
if len(data) < HEADER_SIZE:
raise ValueError(f"文件过小:{len(data)} 字节,无法包含{HEADER_SIZE}字节头部")
magic, header_crc, timestamp, version, image_len, image_crc, header_ver, reserved = \
struct.unpack_from(HEADER_FORMAT, data, 0)
# 识别固件类型
fw_type = KNOWN_MAGIC.get(magic, "未知类型")
# 基本合法性检查
if magic == 0x27051956 and header_ver != 1:
print(f"[警告] U-Boot头部格式版本异常: {header_ver}")
if reserved != 0:
print(f"[警告] 保留字段非零: {reserved:#x}")
return {
"magic": f"0x{magic:08X}",
"固件类型": fw_type,
"header_crc": f"0x{header_crc:08X}",
"timestamp": timestamp,
"version": version,
"image_len": image_len,
"image_crc": f"0x{image_crc:08X}",
"header_version": header_ver,
"reserved": reserved,
}
def main():
if len(sys.argv) != 2:
print("用法: python fw_parser.py <固件文件路径>")
sys.exit(1)
try:
result = parse_firmware_header(sys.argv[1])
except (ValueError, OSError) as e:
print(f"解析失败: {e}")
sys.exit(1)
print("=== 固件头部解析结果 ===")
for key, value in result.items():
print(f"{key:<14}: {value}")
if __name__ == "__main__":
main()
运行与验证
环境依赖:Python 3.6+,无需第三方库。
运行方式:
python fw_parser.py firmware.bin
验证方法:使用以下命令生成一个测试固件文件,然后运行解析脚本验证输出:
python -c "
import struct
# 构造一个合法的U-Boot头部
magic = 0x27051956
header_crc = 0x12345678
timestamp = 1700000000
version = 0x01020304
image_len = 4096
image_crc = 0xDEADBEEF
header_ver = 1
reserved = 0
header = struct.pack('<IIIIIIII', magic, header_crc, timestamp, version, image_len, image_crc, header_ver, reserved)
# 追加4KB假数据
with open('test_fw.bin', 'wb') as f:
f.write(header + b'\x00' * image_len)
print('测试固件已生成: test_fw.bin')
"
然后执行:
python fw_parser.py test_fw.bin
预期输出:
=== 固件头部解析结果 ===
magic : 0x27051956
固件类型 : U-Boot Legacy Image
header_crc : 0x12345678
timestamp : 1700000000
version : 16909060
image_len : 4096
image_crc : 0xDEADBEEF
header_version : 1
reserved : 0
通过条件:脚本能正确输出上述字段值,且固件类型识别为“U-Boot Legacy Image”。若将魔数改为其他已知值(如0x214E4553),类型识别应相应变化。对于非32字节的短文件,脚本应报错退出。
常见问题
- 字节序问题:不同固件格式可能使用大端序,若解析结果异常,可将格式字符串改为
">IIIIIIII"测试。 - 魔数未知:脚本会输出“未知类型”,此时可自行在
KNOWN_MAGIC字典中补充定义。 - 头部长度可变:本案例固定为32字节头部,实际固件可能头部更长,需根据魔数动态确定头部长度,可扩展为查表方式。