魔数障眼法:Tenda-4G06 路由器固件解密及模拟实战

新发布固件解密固件模拟魔数
2026-08-17 07:17
1424

魔数障眼法:Tenda-4G06 路由器固件解密及模拟实战

0X01:分析固件:

从官网获取路由器固件,可以看到更新时间在一个月前,到手的时候发现是.bin文件格式。

image.png
按照常规流程,使用binwalk -E查看该固件的熵值:

image.png
熵值接近 1.0, 通常意味着数据是高度随机的,有概率是被加密的固件。
尝试binwalk -Me直接提取:

image.png

0x02:厂商定制的squashfs文件系统

面对这个疑似加密的固件,我们思来想去,决定回到这个.bin文件本身:
我们知道,固件在十六进制中的完整结构是这样的:
image.png
此时我们把固件放入010 editor中查看它的HEX信息:

image.png

头部 4 字节 27 05 19 56 是标准 U-Boot uImage 魔数,
uImage 头有两个 CRC32 字段:ih_hcrc(头部校验)和 ih_dcrc(数据体校验)。

使用脚本验证其CRC:

import struct, zlib
d = open("US_4G06V3.0re_V04.06.01.33_multi_TDE01.bin", "rb").read()
magic, hcrc, t, sz, load, ep, dcrc = struct.unpack(">7I", d[:28])

hdr = bytearray(d[:64]); hdr[4:8] = b"\0\0\0\0"     # hcrc 字段清零再算
print(hex(zlib.crc32(hdr) & 0xffffffff), hex(hcrc))  # 头 CRC → 相等
print(hex(zlib.crc32(d[64:64+sz]) & 0xffffffff), hex(dcrc))  # 体 CRC → 相等
print(64 + sz == len(d))                             # 若长度严丝合缝 → True

image.png
通过逐字节复算 uImage 头部 CRC(ih_hcrc)与数据体 CRC(ih_dcrc),
确认两个校验值均与固件内存储值完全一致,且文件长度满足标准 uImage 结构(64 + sz = total)。
加密的数据不可能用标准 crc32 通过完整性校验。证明这个固件是标准的uimage结构,没有加密。

但是找遍整个文件,我们并没有发现squashfsXZ 压缩格式的魔数,这点实在蹊跷,
通过查阅资料结合不断分析这个文件,我们确认该系列固件使用了定制修改的文件系统结构和压缩方法。
不光squashfs的魔数被修改为y9nice,同时XZ压缩格式的魔数同样被替换成厂商定义的标识。
XZ 流的尾部魔数仍是标准值 59 5A("YZ"),"YZTenda" 特征串就是「上一条流的尾 "YZ" + 下一条流的头 "Tenda"」相邻拼接出来的。
image.png

image.png

image.png

0x03:顺腾摸瓜:恢复原始文件,魔改XZ工具

已知 Squashfs 魔数被篡改为 y9nice(原应为 hsqs),
XZ 流也被一并修改。修复的第一步很直接:把魔数改回去。
编写一个Python脚本用于修改魔数:

#!/usr/bin/env python3
import sys

# 固件文件名
BIN_FILE = "US_4G06V3.0re_V04.06.01.33_multi_TDE01.bin"
SQUASHFS_FILE = "V4.bin"

# 魔数位置和值(根据你前面的分析结果)
MAGIC_OFFSET = 0x25AC50
OLD_MAGIC = b"y9nice"    # 6字节旧魔数
NEW_MAGIC = b"hsqs"      # 4字节新魔数

# 读取固件
d = open(BIN_FILE, "rb").read()

# 验证旧魔数是否存在
actual_magic = d[MAGIC_OFFSET:MAGIC_OFFSET + len(OLD_MAGIC)]
if actual_magic != OLD_MAGIC:
    print(f"警告: 预期魔数 {OLD_MAGIC},实际是 {actual_magic}")
    sys.exit(1)

print(f"魔数验证通过: {OLD_MAGIC}")

# 数据体起始位置 = 魔数起始位置 + 魔数长度
data_start = MAGIC_OFFSET + len(OLD_MAGIC)

# 拼装新Squashfs: 新魔数(4字节) + 数据体
squashfs_data = NEW_MAGIC + d[data_start:]

# 写入文件
with open(SQUASHFS_FILE, "wb") as f:
    f.write(squashfs_data)

print(f"已生成: {SQUASHFS_FILE},大小 {len(squashfs_data)} 字节")

image.png
完成后得到名为V4.bin的文件,在010Editor中也能直观看到:
image.png

image.png
将修改后的文件保存,使用unsquashfs -s V4.bin查看文件系统的超级块信息(superblock
image.png
superblock全部字段合法,魔数一对上,后面的字段全对了, 这是一个结构完全正确、能够被标准工具识别的 Squashfs 文件系统镜像。
这就是最有力的证据:文件结构未作任何改动,仅有头部标识字段被替换。

尝试使用unsquashfs -d out ./V4.bin分离:
image.png
遇到了XZ 解压失败,LZMA/XZ 错误码 7 通常表示数据格式无效或数据损坏。这是因为厂商的魔改 XZ 数据与标准的 XZ 解压器不兼容。

更换工具,使用binwalk分离:
image.png
虽然分离失败了,但是也成功识别了squashfs 文件块。

紧接着,我们需要解决 Squashfs 内部 XZ 流被魔改的问题。
Binwalk、unsquashfs 等主流工具均无法直接处理这份固件,原因在于其 XZ 压缩段使用了非标准的魔数,并将 CRC 校验逻辑反转——任何遵循标准规范的解压工具都会在完整性检查阶段直接报错退出。为此,我们决定对 XZ 工具本身进行定制化魔改。

魔改点定位:

动手前先明确要改什么。固件的 XZ 流有一处格式层改动 + 一处校验层改动:
image.png

已知我们需要修改的地方,那就下载XZ源码,开始动手魔改:

#!/bin/bash

set -e
cd /tmp
rm -rf xz-5.6.2
if [ ! -s xz-5.6.2.tar.gz ]; then
  curl -sL --retry 3 --retry-delay 5 --max-time 180 -o xz-5.6.2.tar.gz https://tukaani.org/xz/xz-5.6.2.tar.gz \
    || curl -sL --retry 3 --max-time 180 -o xz-5.6.2.tar.gz https://github.com/tukaani-project/xz/releases/download/v5.6.2/xz-5.6.2.tar.gz
fi
tar xf xz-5.6.2.tar.gz
cd xz-5.6.2

python3 - <<'PYEOF'
import re

def patch(path, subs):
    s = open(path, encoding='utf-8').read()
    for old, new in subs:
        if old not in s:
            print("!! NOT FOUND in %s: %.60r" % (path, old))
        else:
            s = s.replace(old, new)
    open(path, 'w', encoding='utf-8').write(s)
    print("patched:", path)

def kill_crc_error(path):
    """把 crc/memcmp 校验附近的 return LZMA_DATA_ERROR 全部替换成空语句"""
    lines = open(path, encoding='ut

试读结束,发布七天后转为公开

公开时间:2026年8月24日 07:17:22

提前解锁全文,需花费 50 积分

登录解锁全文
分享到

参与评论

0 / 200

全部评论 1

s.*的头像
大佬,学到了
2026-08-17 08:54
投稿
签到
联系我们
关于我们