4. 固件压缩算法识别与解压
任务目标
给定一个经过压缩的固件镜像文件,通过文件头特征识别其使用的压缩算法(gzip或LZMA),并完成解压,恢复原始固件内容。本案例以实际固件中常见的gzip和LZMA两种算法为对象,提供可独立运行的Python识别与解压脚本。
环境依赖
- Python 3.6+
- 标准库:
gzip、lzma、binascii(无需第三方包)
核心实现
固件压缩算法识别基于文件头魔数:gzip以1F 8B开头,LZMA单独使用(非XZ容器)时通常以5D 00 00开头(LZMA SDK默认属性字节)。以下代码读取固件文件,自动识别并解压:
import gzip
import lzma
import sys
import os
def identify_and_decompress(input_path, output_path=None):
"""识别固件压缩算法并解压。支持gzip和LZMA(裸流)。"""
with open(input_path, 'rb') as f:
data = f.read()
if len(data) < 4:
raise ValueError("文件过小,无法识别压缩格式")
# 识别压缩算法
if data[:2] == b'\x1f\x8b':
algo = 'gzip'
try:
decompressed = gzip.decompress(data)
except OSError as e:
raise RuntimeError(f"gzip解压失败: {e}")
elif data[:3] == b'\x5d\x00\x00':
algo = 'lzma'
try:
# 裸LZMA流需要显式指定格式
decompressed = lzma.decompress(data, format=lzma.FORMAT_ALONE)
except lzma.LZMAError as e:
raise RuntimeError(f"LZMA解压失败: {e}")
else:
raise ValueError(f"无法识别的压缩格式 (文件头: {data[:4].hex()})")
# 输出结果
if output_path is None:
output_path = input_path + '.out'
with open(output_path, 'wb') as f:
f.write(decompressed)
print(f"识别算法: {algo}")
print(f"原始大小: {len(data)} 字节")
print(f"解压大小: {len(decompressed)} 字节")
print(f"输出文件: {output_path}")
return algo, decompressed
if __name__ == '__main__':
if len(sys.argv) < 2:
print("用法: python firmware_decompress.py <固件文件> [输出文件]")
sys.exit(1)
identify_and_decompress(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)
运行方式
# 对固件文件执行识别与解压
python firmware_decompress.py firmware.bin
# 指定输出文件名
python firmware_decompress.py firmware.bin firmware.raw
验证方法
使用已知压缩工具构造测试样本,验证脚本正确性:
# 构造gzip测试样本
echo "test gzip firmware content" > test.txt
gzip -k test.txt # 生成 test.txt.gz
# 构造LZMA测试样本(使用Python生成裸LZMA流)
python -c "
import lzma
data = b'test lzma firmware content'
comp = lzma.compress(data, format=lzma.FORMAT_ALONE)
open('test.lzma', 'wb').write(comp)
print('LZMA样本已生成')
"
# 运行识别脚本
python firmware_decompress.py test.txt.gz
python firmware_decompress.py test.lzma
通过条件:脚本输出正确的算法名称(gzip/LZMA),解压后的文件内容与原始test.txt完全一致(可用diff test.txt test.txt.gz.out验证)。若固件使用其他压缩算法(如zlib、xz容器),脚本会明确提示“无法识别的压缩格式”,此时需根据实际固件扩展识别规则。
常见问题
- LZMA解压报错:部分固件使用XZ容器(文件头
FD 37 7A 58)而非裸LZMA流。若遇到此情况,将代码中format=lzma.FORMAT_ALONE改为format=lzma.FORMAT_XZ即可。 - 多层压缩:某些固件先压缩再加密或多次压缩。本脚本只处理单层压缩,若解压后数据仍非明文,需对输出文件再次执行识别流程。
- 文件头偏移:部分固件在压缩数据前有头部描述信息。若识别失败,可尝试在文件中搜索
1F 8B或5D 00 00特征字节,从偏移位置截取后解压。