3. 固件熵分析
任务目标
对固件二进制文件进行熵值分析,通过滑动窗口计算局部熵值,识别固件中可能存在的加密或压缩区域,并判断整个固件是否经过加密处理。
实现原理
熵值衡量数据的随机程度。未加密的固件通常包含大量可执行代码和结构化数据,熵值较低(约4.0~6.5);而加密或压缩后的数据接近均匀分布,熵值趋近8.0。通过计算整个文件的全局熵和分块局部熵,可以定位高熵区域并判断固件是否整体加密。
完整实现代码
import math
import sys
from collections import Counter
def calculate_entropy(data: bytes) -> float:
"""计算字节序列的香农熵"""
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 global_entropy(filepath: str) -> float:
"""计算整个文件的全局熵"""
with open(filepath, 'rb') as f:
data = f.read()
return calculate_entropy(data)
def sliding_window_entropy(filepath: str, block_size: int = 256,
step: int = 128) -> list:
"""
滑动窗口计算局部熵
返回 [(offset, entropy), ...]
"""
results = []
with open(filepath, 'rb') as f:
data = f.read()
total_len = len(data)
pos = 0
while pos + block_size <= total_len:
block = data[pos:pos + block_size]
ent = calculate_entropy(block)
results.append((pos, ent))
pos += step
# 处理尾部不足一个块的数据
if total_len % step != 0 and total_len > block_size:
tail_start = total_len - block_size
if tail_start > results[-1][0]:
tail_block = data[tail_start:]
results.append((tail_start, calculate_entropy(tail_block)))
return results
def analyze_firmware(filepath: str, block_size: int = 256,
threshold: float = 7.5) -> dict:
"""综合分析固件熵特征"""
# 全局熵
g_entropy = global_entropy(filepath)
# 局部熵
local_entropies = sliding_window_entropy(filepath, block_size)
# 统计高熵区域
high_entropy_blocks = [(offset, ent) for offset, ent in local_entropies
if ent > threshold]
# 高熵区域占比
high_ratio = len(high_entropy_blocks) / len(local_entropies) if local_entropies else 0
# 判断是否整体加密
is_encrypted = g_entropy > 7.8 and high_ratio > 0.9
return {
'file_size': len(open(filepath, 'rb').read()),
'global_entropy': g_entropy,
'block_count': len(local_entropies),
'high_entropy_blocks': high_entropy_blocks[:10], # 只显示前10个
'high_entropy_ratio': high_ratio,
'likely_encrypted': is_encrypted
}
def print_report(result: dict) -> None:
"""打印分析报告"""
print(f"文件大小: {result['file_size']} 字节")
print(f"全局熵值: {result['global_entropy']:.4f}")
print(f"分块数量: {result['block_count']}")
print(f"高熵块占比: {result['high_entropy_ratio']*100:.1f}%")
print(f"整体加密判定: {'是' if result['likely_encrypted'] else '否'}")
if result['high_entropy_blocks']:
print("\n高熵区域示例(偏移, 熵值):")
for offset, ent in result['high_entropy_blocks'][:5]:
print(f" 0x{offset:08X} {ent:.4f}")
if __name__ == '__main__':
if len(sys.argv) < 2:
print("用法: python entropy_analysis.py <固件文件路径>")
sys.exit(1)
fw_path = sys.argv[1]
report = analyze_firmware(fw_path)
print_report(report)
运行方式
# 基本用法
python entropy_analysis.py firmware.bin
# 自定义块大小和阈值(可选)
python entropy_analysis.py firmware.bin # 默认块大小256字节,阈值7.5
验证方法与判定标准
使用已知样本进行验证:
- 未加密固件:取一个正常的ARM Cortex-M固件(如STM32编译产物),全局熵值通常在5.0~6.8之间,高熵块占比低于50%,判定结果为“否”。
- 加密固件:将上述固件用AES或XOR加密后保存为新文件,全局熵值应接近8.0,高熵块占比超过90%,判定结果为“是”。
- 局部加密固件:仅加密固件中某一段(如偏移0x1000~0x2000),该区域局部熵值应明显高于其他区域,通过输出的高熵块偏移可定位加密范围。
通过条件:对已知加密/未加密样本,程序输出的判定结果与实际情况一致;对局部加密样本,高熵块偏移与加密起始位置吻合。
常见问题
- 块大小选择:256字节适合大多数场景;块过小(<64)导致熵值波动大,块过大(>1024)会掩盖小范围加密区域。
- 阈值调整:7.5作为高熵阈值适用于大多数情况;若固件包含大量常量数据(如查找表),可适当降低至7.0。
- 尾部处理:当文件大小不是步长的整数倍时,代码会额外检查最后一个完整块,避免遗漏尾部高熵区域。