2. 固件熵分析

任务目标

对固件二进制文件进行分块熵分析,通过熵值分布判断固件整体是否经过加密或压缩,并定位高熵区域(通常对应加密数据或压缩段)与低熵区域(如向量表、字符串、未初始化填充)。

核心原理

熵衡量数据的随机程度。未加密的代码/数据通常熵值在 4.0~6.5 之间;加密或高度压缩的数据熵值接近 8.0;全 0xFF 或 0x00 填充块熵为 0。通过滑动窗口计算每个块的 Shannon 熵,即可从统计特征上识别固件内部结构。

完整实现(Python 3)

import math
import sys
from collections import Counter

BLOCK_SIZE = 256  # 每块字节数,可按需调整(常用 256/512/1024)

def shannon_entropy(data: bytes) -> float:
    """计算一段字节数据的 Shannon 熵(0~8 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 = BLOCK_SIZE):
    with open(filepath, 'rb') as f:
        firmware = f.read()

    total_len = len(firmware)
    print(f"文件大小: {total_len} 字节")
    print(f"分块大小: {block_size} 字节")
    print(f"总块数: {math.ceil(total_len / block_size)}")
    print("-" * 60)

    # 整体熵
    whole_entropy = shannon_entropy(firmware)
    print(f"整体熵值: {whole_entropy:.4f} bit/byte")
    if whole_entropy > 7.5:
        print("判定: 固件整体极可能已加密或强压缩")
    elif whole_entropy > 6.8:
        print("判定: 固件整体可能部分加密/压缩,或包含大段高随机数据")
    else:
        print("判定: 固件整体未加密(可能包含压缩段,需结合分块结果)")
    print("-" * 60)

    # 分块熵统计
    high_blocks = []   # 高熵块(>= 7.5)
    low_blocks = []    # 低熵块(<= 1.0)
    offset = 0
    block_idx = 0

    while offset < total_len:
        chunk = firmware[offset:offset + block_size]
        ent = shannon_entropy(chunk)
        start = offset
        end = min(offset + block_size, total_len) - 1

        if ent >= 7.5:
            high_blocks.append((start, end, ent))
        elif ent <= 1.0:
            low_blocks.append((start, end, ent))

        # 打印每块熵值(可注释掉以减少输出)
        # print(f"块{block_idx:4d} [0x{start:08X}-0x{end:08X}] 熵={ent:.4f}")

        offset += block_size
        block_idx += 1

    # 汇总高/低熵区域
    print(f"\n高熵区域(熵≥7.5,疑似加密/压缩): {len(high_blocks)} 块")
    for start, end, ent in high_blocks[:20]:  # 最多显示前20块
        print(f"  0x{start:08X} - 0x{end:08X}  熵={ent:.4f}")
    if len(high_blocks) > 20:
        print(f"  ... 其余 {len(high_blocks)-20} 块略")

    print(f"\n低熵区域(熵≤1.0,疑似填充/空数据): {len(low_blocks)} 块")
    for start, end, ent in low_blocks[:10]:
        print(f"  0x{start:08X} - 0x{end:08X}  熵={ent:.4f}")
    if len(low_blocks) > 10:
        print(f"  ... 其余 {len(low_blocks)-10} 块略")

    # 连续高熵区间合并(相邻高熵块合并为一个大区间)
    if high_blocks:
        merged = []
        cur_start, cur_end, cur_max = high_blocks[0]
        for start, end, ent in high_blocks[1:]:
            if start == cur_end + 1:  # 相邻块
                cur_end = end
                cur_max = max(cur_max, ent)
            else:
                merged.append((cur_start, cur_end, cur_max))
                cur_start, cur_end, cur_max = start, end, ent
        merged.append((cur_start, cur_end, cur_max))
        print(f"\n合并后的连续高熵区间: {len(merged)} 个")
        for start, end, ent in merged:
            size_kb = (end - start + 1) / 1024
            print(f"  0x{start:08X} - 0x{end:08X}  ({size_kb:.1f} KB)  峰值熵={ent:.4f}")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("用法: python entropy_analysis.py <固件文件路径>")
        sys.exit(1)
    analyze_firmware(sys.argv[1])

运行方式

python entropy_analysis.py firmware.bin

无需安装第三方库,仅依赖 Python 3 标准库(math、sys、collections)。

验证方法与判定标准

  • 构造已知样本测试:生成一个包含明确高低熵区域的测试文件,验证程序输出与预期一致:
python -c "
import os, random
# 前 1KB 全 0xFF(低熵),中间 2KB 随机字节(高熵),后 1KB 全 0x00(低熵)
data = b'\xff'*1024 + bytes(random.getrandbits(8) for _ in range(2048)) + b'\x00'*1024
with open('test_fw.bin','wb') as f: f.write(data)
print('测试文件已生成: test_fw.bin (4KB)')
"
python entropy_analysis.py test_fw.bin

通过条件:

  1. 整体熵值应落在 4~6 之间(因包含低熵填充与高熵随机段混合);
  2. 分块输出中,偏移 0x00000400 至 0x00000BFF 范围内的块熵应 ≥7.5,被标记为高熵区;
  3. 偏移 0x00000000 至 0x000003FF 及 0x00000C00 至 0x00000FFF 的块熵应 ≤1.0,被标记为低熵区;
  4. 合并后的连续高熵区间应显示为 0x00000400 - 0x00000BFF,大小约 2.0 KB。

对真实固件执行时,若整体熵值 >7.5 且高熵块占比超过 90%,可判定固件已整体加密;若高熵区呈局部块状分布,则对应固件中的压缩段或加密密钥/签名区域。

常见问题

  • 块大小选择:256 字节适合粗粒度扫描;若需精确定位小段高熵数据(如 128 字节的密钥),可改用 64 或 128 字节块,但输出行数会增加。
  • 高熵阈值调整:某些压缩算法(如 LZMA)熵值可达 7.9 以上;若固件使用弱加密或编码转换,熵值可能略低(7.0~7.5),可下调阈值至 7.0 重新分析。
  • 误报处理:固件中的随机填充(如安全启动签名后的随机 padding)也会产生高熵块,需结合固件基地址和已知结构(如头部偏移)综合判断。