3. 固件熵分析
任务目标
对固件二进制文件执行熵分析,通过滑动窗口计算局部熵值,判断整个固件是否经过加密或压缩(整体高熵),并定位固件内部的高熵区域(如密钥存储、压缩段)与低熵区域(如向量表、文件系统头部)。
实现原理
熵衡量数据的随机程度,取值范围为0~8 bit/byte。普通代码和常量数据熵值通常较低(<6.5),而加密数据或已压缩内容的熵值接近8。通过分块计算熵值并绘制曲线,可以直观识别固件中不同性质的数据区域。
完整代码
import math
import sys
import struct
from collections import Counter
def shannon_entropy(data: bytes) -> float:
"""计算单块数据的香农熵(bit/byte)"""
if not data:
return 0.0
counter = Counter(data)
length = len(data)
entropy = 0.0
for count in counter.values():
p = count / length
entropy -= p * math.log2(p)
return entropy
def analyze_firmware(filepath: str, block_size: int = 256, threshold: float = 7.5):
"""
滑动窗口计算固件局部熵值。
返回 (整体熵, 高熵区域列表, 低熵区域列表)
"""
with open(filepath, 'rb') as f:
firmware = f.read()
total_size = len(firmware)
if total_size == 0:
print("文件为空")
return
# 整体熵
overall_entropy = shannon_entropy(firmware)
# 分块熵
high_regions = []
low_regions = []
offset = 0
block_index = 0
while offset < total_size:
block = firmware[offset:offset + block_size]
ent = shannon_entropy(block)
start = offset
end = min(offset + block_size, total_size) - 1
if ent >= threshold:
high_regions.append((start, end, round(ent, 3)))
elif ent < 5.0:
low_regions.append((start, end, round(ent, 3)))
offset += block_size
block_index += 1
# 输出结果
print(f"文件大小: {total_size} bytes")
print(f"整体熵值: {overall_entropy:.4f} bit/byte")
if overall_entropy > 7.5:
print("判定: 固件整体呈高熵,可能整体加密或压缩")
elif overall_entropy > 6.0:
print("判定: 固件混合区域,部分数据可能加密/压缩")
else:
print("判定: 固件整体低熵,可能为明文代码/数据")
print(f"\n高熵区域 (熵值>={threshold}):")
for start, end, ent in high_regions[:20]:
print(f" 偏移 0x{start:08X} - 0x{end:08X} 熵值 {ent}")
print(f"\n低熵区域 (熵值<5.0):")
for start, end, ent in low_regions[:20]:
print(f" 偏移 0x{start:08X} - 0x{end:08X} 熵值 {ent}")
# 返回供外部使用的数据
return {
'overall_entropy': overall_entropy,
'high_regions': high_regions,
'low_regions': low_regions,
'block_size': block_size,
'total_size': total_size
}
if __name__ == "__main__":
if len(sys.argv) < 2:
print("用法: python entropy_analysis.py <固件文件路径> [块大小,默认256]")
sys.exit(1)
path = sys.argv[1]
bs = int(sys.argv[2]) if len(sys.argv) > 2 else 256
analyze_firmware(path, bs)
运行方式
# 基本用法(块大小默认256字节)
python entropy_analysis.py firmware.bin
# 指定块大小(例如512字节)
python entropy_analysis.py firmware.bin 512
验证方法
使用已知明文固件和加密固件各一份进行对比测试:
- 明文固件(如未加密的ARM Cortex-M固件):整体熵值通常低于6.5,且低熵区域集中在起始向量表(偏移0x0附近)和只读数据段。
- 加密固件(如AES加密后的镜像):整体熵值应接近8.0,且所有分块熵值均高于7.5,不存在明显低熵区域。
- 混合固件(部分压缩/加密):熵曲线会呈现明显的高低起伏,高熵区域对应压缩或加密段,低熵区域对应明文头部或配置数据。
通过观察输出中高/低熵区域的偏移范围,即可判断固件是否加密/压缩,并定位具体区域。
预期输出示例
文件大小: 1048576 bytes
整体熵值: 7.9821 bit/byte
判定: 固件整体呈高熵,可能整体加密或压缩
高熵区域 (熵值>=7.5):
偏移 0x00000000 - 0x000000FF 熵值 7.991
偏移 0x00000100 - 0x000001FF 熵值 7.985
...
常见问题
- 块大小选择:256字节适合大多数场景。块过小(<64)会导致熵值波动剧烈,产生大量误报;块过大(>4096)会掩盖小尺寸高熵区域(如256字节的AES密钥)。
- 阈值调整:若固件包含大量UTF-8字符串,其熵值约6.0~6.5,可适当降低高熵阈值至7.0以识别压缩段。
- 高熵区域定位精度:定位精度等于块大小。如需精确定位,可在发现高熵区域后,对该区域使用更小的块(如32字节)二次扫描。
上图展示了典型混合固件的熵值分布模式:起始向量表区域熵值较低(约4~6),随后加密段熵值接近8,明文代码段熵值回落至5~7,末尾文件系统区域熵值中等。实际分析时,将脚本输出的高/低熵区域偏移与此模式对照,即可快速判断固件结构。