拆解 TP-Link 监控摄像头设置备份文件 config.bin 的格式

因为各种原因,手上有一个中国大陆版的 TL-IPC6128-EZ,tplink为了不让用戶將它拿去其他国家用,可谓是下了真功夫。

其中最坑的就是OSD的水印时间是写死的中国大陆时区CST-8。

除了手动设定时间、搭建一个做过手脚的ntp伺服器以外,我选择看看机器备份档config.bin是否包含时区信息,于是有了这篇文章。(剧透:有时区信息,但是改了也没用)

整个过程踩了不少坑,记录下来。

1. 整体结构

config.bin
├─ [0x00:0x10] MD5(ciphertext)               ← 唯一的明文头
└─ [0x10:EOF ] DES-CBC( 外层明文 )           ← 单 DES,key 来自设备encrypt_key,IV=0
     ├─ 0x000 TP_HEAD(0x18) + vendor_id/zone_code + 长度字段
     ├─ 0x200 顶层 NVMP-CONFIG 容器:magic + product_id + datalen
                config_file_head:
                +0x00  "NVMP-CONFIG\0" // 12 字节 magic
                +0x10  product_id  u32 BE // product_id
                +0x14  datalen     u32 BE // 其后所有节点字节数
     └─ 0x218 配置节点(node)
          ├─ +0x10 count(LE)  // JSON 条目数
          ├─ +0x14 clen(LE)   // zlib 压缩流长度
          ├─ +0x18 ulen(LE)   // 解压后长度
          └─ +0x1c body = DES-CBC( zlib( item0\0 item1\0 … ) ) // 内层再一层 DES + zlib

外层与内层各有一层 DES(同 key、同 IV=0),内层 DES 之内再套 zlib,zlib 之内是一串 NUL 分隔的 JSON 配置表。加密为单 DES,非 AES

key不知道是不是统一的,我这个型号的key是:D30474E282B5A282

2. 加密:单 DES

/bin/main 内含 AES_cbc_Decrypt_no_paddingAES256-CBC 等符号,但与 config.bin 无关:AES_cbc_Decrypt_no_padding0x2f339c)全程序仅一个调用者,其上下文皆为 password、socket、RTSP 字符串,猜测是tplink云平台相关的东西。

config.bin 的解密路径为 uc_post_handle → 0x32b548 → 0x32b26c

0x32b26c:
  读取 /tmp/base-files/etc/encrypt_key
  这是一个16 个十六进制字符,比如我手上这台是D30474E282B5A282
  hex_str_to_bytes(dst, key_str, 8)      → 转换成 8 字节 DES 密钥
  密钥核心 0x30137c / 0x3013dc:
       bic r3, r3, #7      ; 长度向下取整到 8 的倍数
       … 每轮步进 #8 …      ; 8 字节一组分组

同一把 key、同一个 IV=0 同时用于外层文件内层节点 body 两处 DES。加解密为同一函数 0x32b26cmode 参数:0x32b540(mode=0) 为加密、0x32b548(mode=1) 为解密。

3. 外层明文布局

偏移相对明文起点(= 文件偏移 − 0x10):

+0x000  TP_HEAD (0x18)
          +0x00  00 00 01 00
          +0x04  20 字节签名            ← 和设备 /tp_header 比对
+0x020  vendor_id   u16 LE
+0x022  zone_code   u16 LE
+0x040  len_head    u32 BE
+0x044  len_data    u32 BE              ← 校验:filelen ≥ len_head + len_data
+0x200  config_file_head:
          +0x00  "NVMP-CONFIG\0"        12 字节 magic
          +0x10  product_id  u32 BE     (= 设备 product_id)
          +0x14  datalen     u32 BE     (其后所有节点字节数)
+0x218  第一个配置节点

除开头 16 字节的 MD5 外,所有字段(含 product_id、TP_HEAD 签名)皆位于 DES 解密后的明文内,文件表面不可见。实测本机 product_id = 61281 (0xEF61)vendor_id = 0zone_code = 0

4. 内层:NVMP 配置节点

逆向自 parse_config (0x32c514),并以真机数据交叉验证。

4.1 节点头(28 字节,0x218 起)

+0x00  "NVMP-CONFIG\0"          12 字节
+0x0c  flag                      通常 00 00 00 01 (BE 1)
+0x10  count   u32 LE            JSON 条目数     (实测 165)
+0x14  clen    u32 LE            zlib 压缩流长度 (实测 9583,未补齐)
+0x18  ulen    u32 LE            解压后字节数    (实测 120431)
+0x1c  body

注意端序:节点头的 count/clen/ulen小端,而顶层容器的 product_id/datalen大端,同文件混用Orz。

4.2 body 的三层解码

body(磁盘上,补齐到 8 字节)
  ── DES-CBC 解密 // 以 78 DA 开头的 zlib 流
  ── zlib 解压    // 120431 字节明文
  ── 按 \0 切分   // 165 段紧凑 JSON

每段为一张配置表,例如:

{"system":{"system":{"sys":{"dev_alias":"%e9%81%93%e8%b7%afA","timezone":"CST-8"}}}}
{"image":{"para":{"common":{"luma":"50","contrast":"50"}}}}

特征:值几乎皆为字符串(含数字、布尔);中文等经 URL 百分号编码;顶层键可重复(多条 systemimage),须以保序列表处理,不可合并为单一字典;count 等于条目数,切分时丢弃尾部空段。

这里出现了时区,但是实测修改这个值并没有任何卵用,OSD时间不遵循这个时区设定,tplink 煞笔。

4.3 写回时的长度字段重算

comp       = zlib(blob, level=9)
clen       = len(comp)          # 未补齐
body       = DES(comp 补齐到 8)
datalen    = 28 + len(body)     # 顶层容器,大端
ulen       = len(blob)
count      = len(items)
外层       = 头部[:0x218] + 节点;补齐 8 字节,重算 len_head/len_data,外层 DES + MD5

5. 服务端校验:uc_post_handle

HTTP 路由 /admin/system/upload_conf 对应处理函数 0x210420。逆向之后得到校验顺序:

context 非空 
↓
MD5(file[16:]) == file[0:16]↓
DES-CBC 解密成功 
↓
总长 ≥ BE32(@0x40) + BE32(@0x44)↓
明文长度 > 0x200(config_file_head 有效) 
↓
BE32(@0x210) == 设备 product_id↓
TP_HEAD[0x04:0x18] 匹配 && vendor_id 匹配 && zone_code 匹配 
↓
parse_config 成功

全数通过后进入成功分支(0x210634 → msg_send(0x5014))应用配置。

负责落盘的函数(~0x291f80)实际调用:

  • fopen/fwrite → 写 /tmp/base-files/etc/hardware.config(及 /tmp/hardware.config)
  • fopen/fwrite → 写 /tmp/app_config.bin
  • system() ×2 → 执行(逐字命中固件内字符串):
    • mkdir -p /tmp/base-files/etc/;
    • tar -zxvf /tmp/app_config.bin -C /tmp/;rm -rf /tmp/app_config.bin;chmod -R 777 /tmp/base-files;chmod -R 777 /tmp/radio; // tplink非常风骚的操作,这里可以构造一个带路径穿越的tar.gz包,直接覆盖任意文件

用ai写的config.bin参数值修改、打包工具

使用说明

一、 基础准备

在运行脚本之前,你需要确保环境满足以下要求:

  1. 健康的大脑
  2. Python 3。
  3. 安装依赖库: 该脚本需要 pycryptodome 库来进行 DES 解密。
    不懂去问AI

二、大概流程

如果你想修改摄像头的某个功能参数(比如修改网络设置、账号等),最标准的流程是:导出 → 修改 → 校验 → 导入

第一步:导出当前配置为 JSON

首先,你需要从设备上获取一份原始的 config.bin 文件。 使用 export 命令将其转换为人类可读的 JSON 文件:

bashpython build_config_bin.py export config.bin config.json

会得到一个 config.json 文件,里面包含了所有的配置项。

第二步:修改 JSON 文件

手动打开 config.json,找到你想修改的参数进行编辑。

第三步:将修改后的 JSON 导入并生成新文件

使用 import 命令。这里需要原始的 config.bin 作为模板,因为它包含了设备唯一的硬件标识(如 product_id, vendor_id 等)。

bashpython build_config_bin.py import config.bin config.json new_config.bin

新构造的文件在同目录下 new_config.bin

第四步:校验文件

先本地检查一下可不可以通过固件检查:

bashpython build_config_bin.py verify new_config.bin

结果:如果看到 全部通过,说明文件格式本身合法,大概率可以上传成功(摄像头可以轻松变砖 笑)。


三、 其他功能

除了上述标准流程,脚本还提供了一些快捷工具:

1. 快速修改

如果你只需要改一个简单的字符串参数,可以使用 set 命令直接在二进制文件上操作:

bash# 假设你要把某个路径下的 key 改为 "new_value"
python build_config_bin.py set config.bin output.bin --kv "path.to.key=new_value"

2. 跨设备适配(重建配置)

如果你有一份 A 设备的配置,想把它的内容搬到 B 设备上,可以使用 rebuild。它会保留 A 设备的配置内容,但把 B 设备的硬件 ID 填入头部:

bashpython build_config_bin.py rebuild template.bin output.bin --product-id 61281 --vendor-id 0 --zone-code 0

3. 查看内部结构

可以使用 nodes 命令查看配置里有哪些节点:

bashpython build_config_bin.py nodes config.bin

这会列出所有的 NVMP 节点、类型(JSON 还是二进制数据)以及它们在文件中的位置。

4. 提取特定内容

如果你只需要提取某一部分(比如提取出里面的 app_config 压缩包):

bashpython build_config_bin.py extract config.bin 0 some_data.bin

注:0 是节点序号,可以在 nodes 命令中查看。

5. 基础解密/加密

如果你只是想单纯地把文件转换成明文或加密回二进制:

  • 解密python build_config_bin.py decrypt config.bin plaintext.txt
  • 加密python build_config_bin.py encrypt plaintext.txt config.bin

重要提示

DES Key:硬编码了 DES 解密密钥(D30474E282B5A282),不确定其他型号可不可以用。

备份:在操作任何 config.bin 之前,请务必保留原始文件的备份。

硬件匹配:除非你非常清楚自己在做什么,否则不要在不同型号的摄像头之间随意混用 config.bin,这可能导致设备变砖。

不提供任何保证。

作者

OX

我是一個住在大阪農村,在家種菜的人。 曾經一時興起學吹單簧管,結果沒堅持下來。 現在一邊上學一邊炒作垃圾股賺零花錢。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *