2. 固件解包与内核镜像分析

任务说明

本案例以一个典型的IoT路由器固件为例,完成以下目标:解包固件文件系统、定位Linux内核镜像、分析内核版本与CPU架构、提取内核编译配置。通过该流程掌握固件逆向分析的基础步骤。

环境准备

  • 操作系统:Ubuntu 22.04(或任意Linux发行版)
  • 工具:binwalk、file、strings、extract-vmlinux脚本(Linux内核源码scripts目录下)
  • 示例固件:router_fw.bin(可从公开固件库获取,如DD-WRT或OpenWrt的工厂镜像)

固件解包

使用binwalk扫描固件结构并提取文件系统:

binwalk router_fw.bin
binwalk -Me router_fw.bin

-M递归扫描,-e自动提取。解包后目录中通常包含squashfs-root(根文件系统)和若干原始内核镜像文件。

定位内核镜像

在解包产物中查找内核镜像:

find _router_fw.bin.extracted -type f -name "*kernel*" -o -name "*vmlinux*" -o -name "*zImage*"
file _router_fw.bin.extracted/* | grep -i kernel

常见位置:_router_fw.bin.extracted/<offset>.kernel(binwalk根据偏移命名)。若未直接识别,可用strings搜索内核特征字符串:

strings _router_fw.bin.extracted/* | grep -i "Linux version"

分析内核版本与架构

对定位到的内核镜像执行:

file _router_fw.bin.extracted/12345.kernel
strings _router_fw.bin.extracted/12345.kernel | grep "Linux version"

file输出显示架构(如ARM、MIPS、x86),strings输出包含完整版本字符串,例如:Linux version 3.10.14 (builder@host) (gcc version 4.8.3) #1 SMP Wed Jul 30 10:23:45 CST 2014。

提取内核配置

内核配置(.config)通常嵌入在镜像的IKCONFIG段中。使用内核官方脚本提取:

wget https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-ikconfig
chmod +x extract-ikconfig
./extract-ikconfig _router_fw.bin.extracted/12345.kernel > kernel.config

若镜像被压缩(如gzip、lzma),需先解压。使用extract-vmlinux脚本还原未压缩内核:

wget https://raw.githubusercontent.com/torvalds/linux/master/scripts/extract-vmlinux
chmod +x extract-vmlinux
./extract-vmlinux _router_fw.bin.extracted/12345.kernel > vmlinux
./extract-ikconfig vmlinux > kernel.config

验证方法

完成以上步骤后,通过以下条件判定成功:

  1. kernel.config文件非空,且包含以CONFIG_开头的有效配置项(如CONFIG_ARM=y);
  2. 从配置中能确认架构:grep CONFIG_ARM kernel.config或grep CONFIG_MIPS kernel.config;
  3. 版本字符串与file输出架构一致,且版本号完整可读。

关键代码实现

以下脚本将上述步骤整合为自动化流程,输入固件路径,输出内核版本、架构和配置文件:

#!/bin/bash
# usage: ./analyze_kernel.sh router_fw.bin

FW="$1"
if [ -z "$FW" ]; then echo "Usage: $0 <firmware.bin>"; exit 1; fi

# 1. 解包
binwalk -Me "$FW" > /dev/null 2>&1
EXTRACT_DIR="_${FW}.extracted"

# 2. 查找内核镜像(优先匹配zImage/内核偏移文件)
KERNEL_IMG=""
for f in $(find "$EXTRACT_DIR" -type f); do
    if file "$f" | grep -qE "Linux kernel|kernel image|uImage|zImage"; then
        KERNEL_IMG="$f"
        break
    fi
done

if [ -z "$KERNEL_IMG" ]; then
    # 回退:用strings搜索版本字符串定位
    for f in $(find "$EXTRACT_DIR" -type f -size +100k); do
        if strings "$f" 2>/dev/null | grep -q "Linux version"; then
            KERNEL_IMG="$f"
            break
        fi
    done
fi

if [ -z "$KERNEL_IMG" ]; then
    echo "[-] Kernel image not found"
    exit 1
fi
echo "[+] Kernel image: $KERNEL_IMG"

# 3. 版本与架构
echo "[+] File info:"
file "$KERNEL_IMG"
echo "[+] Version string:"
strings "$KERNEL_IMG" | grep -m1 "Linux version"

# 4. 提取配置
# 先尝试直接提取
./extract-ikconfig "$KERNEL_IMG" > kernel.config 2>/dev/null
if [ ! -s kernel.config ]; then
    # 失败则先解压vmlinux
    ./extract-vmlinux "$KERNEL_IMG" > vmlinux 2>/dev/null
    if [ -s vmlinux ]; then
        ./extract-ikconfig vmlinux > kernel.config 2>/dev/null
    fi
fi

if [ -s kernel.config ]; then
    echo "[+] Config extracted to kernel.config"
    echo "[+] Architecture from config:"
    grep -E "CONFIG_(ARM|ARM64|MIPS|X86_32|X86_64|RISCV)=" kernel.config | head -5
else
    echo "[-] Config extraction failed (kernel may not have IKCONFIG enabled)"
fi

运行前确保extract-ikconfig和extract-vmlinux已下载并赋予执行权限,且与脚本同目录。

预期输出示例

[+] Kernel image: _router_fw.bin.extracted/30720.kernel
[+] File info:
_router_fw.bin.extracted/30720.kernel: Linux kernel ARM boot executable zImage (little-endian)
[+] Version string:
Linux version 3.10.49 (root@buildhost) (gcc version 4.8.3 (Buildroot 2014.08) ) #1 SMP PREEMPT Fri Aug 22 10:15:22 CST 2014
[+] Config extracted to kernel.config
[+] Architecture from config:
CONFIG_ARM=y

以上输出为基于代码逻辑的预期结果,实际内容取决于固件本身。若kernel.config提取失败,说明该内核未启用CONFIG_IKCONFIG,此时可通过strings vmlinux | grep "CONFIG_"部分恢复配置项,但无法获得完整.config文件。