3. 固件加密与解密初步

任务目标

分析一段未知固件,判断其是否经过加密或压缩处理,并尝试使用常见工具(如 binwalk、firmware-mod-kit)或已知密钥进行解密/解包,最终提取出可识别的文件系统内容。

环境准备

在 Linux 虚拟机中安装以下工具(以 Ubuntu/Debian 为例):

sudo apt update
sudo apt install -y binwalk firmware-mod-kit unzip

固件样本特征

本案例使用一个模拟固件 sample_fw.bin,其构造方式为:先使用 AES-128-ECB 加密一个包含 squashfs 文件系统的镜像,再在头部添加 16 字节自定义头(含魔数 FWENC 和加密标志)。该样本用于演示加密固件的识别与解密流程。

识别加密固件

首先使用 binwalk 扫描固件,观察输出特征:

binwalk sample_fw.bin

预期输出中不会直接出现 squashfs 或 filesystem 等明文标识,而是显示高熵数据段或未知数据。此时可进一步检查文件头:

xxd sample_fw.bin | head -5

若头部出现非标准魔数(非 U-Boot、TRX、HDR0 等常见格式),且熵值较高,则高度怀疑固件已加密。

解密与解包实现

以下 Python 脚本演示了如何识别自定义头、提取密文并使用已知密钥解密,然后通过 binwalk 识别解包后的文件系统:

import struct
from Crypto.Cipher import AES

# 已知密钥(来自固件开发文档或硬编码分析)
KEY = b'0123456789abcdef'  # 16字节密钥

def decrypt_firmware(input_file, output_file):
    with open(input_file, 'rb') as f:
        data = f.read()
    
    # 检查自定义头
    if data[:4] != b'FWENC':
        print("[-] 未识别加密标志,可能未加密")
        return False
    
    # 解析头部:4字节魔数 + 4字节加密标志 + 8字节原始长度
    magic, enc_flag, orig_len = struct.unpack('<4sIQ', data[:16])
    print(f"[+] 魔数: {magic}, 加密标志: {enc_flag}, 原始长度: {orig_len}")
    
    if enc_flag != 1:
        print("[-] 加密标志为0,固件未加密")
        return False
    
    # 提取密文(从偏移16开始)
    ciphertext = data[16:]
    
    # AES-128-ECB 解密
    cipher = AES.new(KEY, AES.MODE_ECB)
    # 补齐到16字节倍数
    pad_len = 16 - (len(ciphertext) % 16)
    if pad_len != 16:
        ciphertext += b'\x00' * pad_len
    
    plaintext = cipher.decrypt(ciphertext)
    
    # 截断到原始长度
    plaintext = plaintext[:orig_len]
    
    with open(output_file, 'wb') as f:
        f.write(plaintext)
    
    print(f"[+] 解密完成,输出: {output_file}")
    return True

if __name__ == '__main__':
    decrypt_firmware('sample_fw.bin', 'decrypted_fw.bin')

运行与验证

  1. 生成模拟固件(仅用于本案例复现):
# 创建一个小型squashfs镜像(需root)
mkdir rootfs
echo "test file" > rootfs/test.txt
mksquashfs rootfs rootfs.sqsh

# 加密并添加头
python3 -c "
from Crypto.Cipher import AES
import struct

with open('rootfs.sqsh', 'rb') as f:
    data = f.read()

key = b'0123456789abcdef'
cipher = AES.new(key, AES.MODE_ECB)
pad_len = 16 - (len(data) % 16)
if pad_len != 16:
    data += b'\x00' * pad_len
enc = cipher.encrypt(data)

header = struct.pack('<4sIQ', b'FWENC', 1, len(data))
with open('sample_fw.bin', 'wb') as f:
    f.write(header + enc)
print('sample_fw.bin 生成完成')
"
  1. 运行解密脚本:
python3 decrypt_firmware.py
  1. 使用 binwalk 验证解密结果:
binwalk decrypted_fw.bin

通过条件:binwalk 输出中能识别出 Squashfs filesystem 标记,且解包后能读取到 test.txt 文件内容。

常见问题

  • 密钥未知:若密钥未知,可尝试在固件中搜索硬编码密钥(strings 命令),或使用 firmware-mod-kit 的 extract-firmware.sh 脚本自动尝试常见解包方式。
  • 非 ECB 模式:若为 CBC 模式,需额外提取 IV(通常位于头部或密文前 16 字节),修改脚本中 AES.new(key, AES.MODE_CBC, iv) 即可。
  • 压缩而非加密:若 binwalk 显示 gzip 或 lzma 标记,则属于压缩固件,直接使用 binwalk -e 即可自动解压。