|
用过博通芯片路由器的都知道,博通的 bootloader(简称 CFE)里包含了一些参数信息,比如 MAC 地址、无线参数、序列号型号名称等等,可以通过一些工具可以进行修改,比如 https://www.linksysinfo.org/index.php?threads/asuswrt-merlin-on-netgear-r7000.71108/ 或者 https://cfeditor.pipeline.sh/,还有论坛里一些易语言工具,不过这些工具的应用范围有限,只适合参数数据是明文存储的 CFE 文件,其实不用工具用十六进制编辑器也可以编辑:
而对于非明文存储的参数数据,它们就无能为力了,比如 K2P B1 版本的 CFE,它的数据并非是明文保存的:
但是仔细观察其数据存储结构,可以发现:
- 无论是明文形式还是非明文形式,数据前的固定位置都有 ZSIB 和 FLSH 标志。
- FLSH 标志之后有 16 个字节的数据区,然后再是参数数据区。
- 参数数据区之后会有一个 AMZL 标志,这也很容易让人想到 LZMA 压缩算法。
我们可以通过这些标记的字符串和字节序列在源码(我用的是 ASUS RT-AC1200G+ GPL 源码)里找到一些相关的信息:
1、在 asuswrt/release/src-rt-9.x/src/include/hndsoc.h 中,可以看到 BISZ 数据区的描述信息:
/* A boot/binary may have an embedded block that describes its size */
#define BISZ_OFFSET 0x3e0 /* At this offset into the binary */
#define BISZ_MAGIC 0x4249535a /* Marked with this value: 'BISZ' */
#define BISZ_MAGIC_IDX 0 /* Word 0: magic */
#define BISZ_TXTST_IDX 1 /* 1: text start */
#define BISZ_TXTEND_IDX 2 /* 2: text end */
#define BISZ_DATAST_IDX 3 /* 3: data start */
#define BISZ_DATAEND_IDX 4 /* 4: data end */
#define BISZ_BSSST_IDX 5 /* 5: bss start */
#define BISZ_BSSEND_IDX 6 /* 6: bss end */
#define BISZ_SIZE 7 /* descriptor size in 32-bit integers */
正好是 sizeof(BISZ_MAGIC)+sizeof(int32)xBISZ_SIZE=32 个字节大小,和我们在 CFE 文件中看到的一致。
2、在 asuswrt/release/src-rt-9.x/src/linux/linux-2.6.36/arch/mips/include/asm/mach-bcm47xx/nvram.h 中可以看到 FLSH 数据区的结构信息:
struct nvram_header {
u32 magic;
u32 len;
u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
u32 config_ncdl; /* ncdl values for memc */
};
#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */
#define NVRAM_VERSION 1
#define NVRAM_HEADER_SIZE 20
同时结合 asuswrt/release/src-rt-9.x/src/shared/startarm-ca9.S 的部分代码:
/* Embedded NVRAM */
.global embedded_nvram
embedded_nvram:
.fill 0x400,4,~0x48534C46 /* 'FLSH' */
.long 0x4c5a4d41 /* LZMA NVRAM Supported */
可以知道,博通把 CFE 中存储参数信息的数据区叫做 embedded nvram,也就是嵌入的 NVRAM,这个 NVRAM 的 HEADER 占 20 字节。
3、另外简单看下 asuswrt/release/src-rt-9.x/src/shared/startarm-ca9.S,从 /* Entry point */ 开始看:
/* Entry point */
.section .text.startup
.globl startup
startup:
b tr_rst /* 0 - reset */
ldr pc,_tr_und /* 4 - undefined instruction */
ldr pc,_tr_swi /* 8 - software interrupt */
ldr pc,_tr_iab /* 0xc - prefetch abort */
ldr pc,_tr_dab /* 0x10 - data abort */
ldr pc,_tr_bad /* 0x14 - reserved */
ldr pc,_tr_irq /* 0x18 - external interrupt */
ldr pc,_tr_fiq /* 0x1c - fast interrupt */
_tr_und:
.word tr_und
_tr_swi:
.word tr_swi
_tr_iab:
.word tr_iab
_tr_dab:
.word tr_dab
_tr_bad:
.word tr_bad
_tr_irq:
.word tr_irq
_tr_fiq:
.word tr_fiq
_pad:
.word 0x12345678 /* now 16*4=64 */
.org (BISZ_OFFSET-4)
_ddrclk:
.word 0x0
/* Record the size of the binary */
.org BISZ_OFFSET
.word BISZ_MAGIC
.word text_start
.word text_end
.word data_start
.word data_end
.word bss_start
.word bss_end
.word _end
.balign envram_ofs
/* Embedded NVRAM */
.global embedded_nvram
embedded_nvram:
.fill 0x400,4,~0x48534C46 /* 'FLSH' */
.long 0x4c5a4d41 /* LZMA NVRAM Supported */
结合 CFE 的二进制数据:
一个 word 占 4 个字节,第 60 字节处正好有个 0x12345678 (小端)。
很容易发现,CFE 就是由这种(是这种,不一定就是这个)汇编文件编译而来,还可以在 http://armconverter.com/hextoarm/ 上验证一下:
结果和猜测一致。
当然,我们想找出 embedded nvram 数据区的压缩和解压方法,并不需要去研究这个汇编代码,这个文件就到此为止,我们再来看另外一个东西:
在 asuswrt/release/src-rt-9.x/src/ctools 下有一个 nvserial 工具,这个工具的作用就是把一些 NVRAM 数据写入 CFE 文件中:
这一点我是从 https://blog.csdn.net/weixin_42353331/article/details/86060895 了解到的,在他博文的文末。
nvserial 的帮助信息里有这么一条:
可以简单找个一些源码里的文件测试一下:
./nvserial -z -i cfez.bin -o cfez_out_compress.bin -s 10 bcm94702ap.txt
加 -z 时:
不加 -z 时:
!!!
那这个 -z 的压缩算法到底是什么呢?看看 nvserial 的反编译代码:
使用的是一个名为 LzmaCompress 的函数,if 在有 -z 是就调用压缩,没有时直接 memcpy。
我们可以在 asuswrt/release/src-rt-9.x/src/tools/misc/lzma_src/C 找到 LzmaCompress 的 C 源码,其函数定义在 LzmaLib.h 中,虽然并不一定就是 nvserial 用到的代码,但是从反编译结果看,代码基本是一样的,可作为参考。我们看看这个函数的定义:
MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen,
unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */
int level, /* 0 <= level <= 9, default = 5 */
unsigned dictSize, /* default = (1 << 24) */
int lc, /* 0 <= lc <= 8, default = 3 */
int lp, /* 0 <= lp <= 4, default = 0 */
int pb, /* 0 <= pb <= 4, default = 2 */
int fb, /* 5 <= fb <= 273, default = 32 */
int numThreads /* 1 or 2, default = 2 */
);
大致看过一圈之后,可以了解到,这是一个 Lzma1 算法压缩函数,压缩数据没有包装容器。其次,结合反编译的代码可知,LzmaCompress 会将 nvram 第 20 个字节开始到 v66 的数据压缩到 output_nvram 中,从 output_nvram 第 25 个字节的位置开始写。另外 LzmaCompress 还会将 props 信息写入到 output_nvram 中,从第 20 字节处开始写,占 5 字节。
对照上面 nvserial 加 -z 和不加 -z 的输出结果图,很容易了解到,加了 -z 就是把不加 -z 时 nvram_header 后面的数据压缩一下再存进来,props 存在 0x414 - 0x418,压缩数据从 0x419 处开始存储。
既然有 LzmaCompress,那肯定有 LzmaUncompress,LzmaLib.h 就有定义,我们参考 nvserial 中的写法,写一段简单的 c 代码来解压:
if (LzmaUncompress(
(unsigned char *)&output_buffer[0],
&destLen,
(const unsigned char *)&input_buffer[25],
&lSize,
(const unsigned char *)&input_buffer[20],
outPropsSize)) {
fputs("NVRAM uncompression error\n", stderr);
// exit(-1);
} else {
fwrite(output_nvram, sizeof(unsigned char), NVRAM_SIZE, outFile);
}
编译执行,得到解压之后的数据,把 0x00 替换成 0x0A,结果为:
下面还有很多参数,没截上来了。
至此,流传的 CFE 数据 “加密” 的问题已经解决了,其实并不是加密,只是压缩了而已。另外也解释了为什么在原厂分区被覆盖之后,会获得 00:90:4C:... 这种格式的 MAC 地址。
这个 00:90:4C 的地址查 OUI 是来自 Epigram, Inc. 的,但是源码里附带的 nvram*.txt 里都是这钟 MAC,也不知道两者有什么关系。
修改 CFE 的方法也很清楚了:
把这些解压出来的数据放在一个 txt 文件中,以 /n 作为换行符,修改你需要修改的部分,再使用 nvserial 工具压缩到 CFE 中:
./nvserial -z -b 0x400 -c 0x1000 -i mtd0.bin -o mtd0_new.bin nvram_embedded_uncompressed_phicomm.txt
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
评分
-
查看全部评分
|