固件信息
Draytek Vigor 3910是Draytek旗下的一款多WAN安全路由器。
厂商:Draytek
型号:Vigor 3910
固件版本:3971、4325
固件解密
首先对于Draytek的固件解密有draytools这么一个工具,不过该工具所能支持的型号有限,当涉及到对应的型号固件时可以考虑使用hexacon_draytek_2022_final中所展示的方式打造自己的draytools。
对于draytools不支持的型号,文章中也提出了,Vigor 3910是AARCH64的,并且在3.9.x与4.x版本有很大的不同,最直观的就是这两个版本的熵值是不同的。
V3.9.7.1
V4.3.2.5
从对两个版本的Firmware查看熵值后可以看到,在V3.9.7.1中是可以解析出特征的,但是V4.3.2.5并无法像V3.9.7.1一样解析出特征,因此可以大胆猜测V4.3.x.x可能进行了某种加密,使得我们无法直接对其进行获取。
然后通过文章我们知道,Firmware中使用了54 48 55 4E 44 45 52 58去分割 boot.bin,init.bin 和 ATF stage 1。
如上所示,我们通过010edit查找54 48 55 4E 44 45 52 58得到了每一个部分开始的地址。于是我们可以通过dd命令将其提取出来。
dd if=v3910_3971.all of=stage1.bin skip=262150 bs=16
或者
dd if=v3910_3971.all of=stage2.bin skip=4194400 bs=1
这里的262150是40006H的十进制表达。
获得stage1.bin后通过010edit查看内容,可以发现其中有BL1、BL31等内容。
这些字符是ATF中的内容,所以猜测,3910型号中使用了ATF技术。而参考ATF 启动流程,CPU 会先执行 BL1,BL1 通常被烧录在 ROM 中,BL1 完成初始化工作之后会通过 UUID 查找其他 BL 的位置,BL2、BL3、系统镜像等部分被单独打包在 fip.bin,存放到 flash 内。考虑到关键部分应该在 fip.bin,所以要想办法找到此文件并解包。
查看 ARM ATF 开源代码了解到fip.bin以0xAA640001开头。
#define TOC_HEADER_NAME 0xAA640001
#define TOC_HEADER_SERIAL_NUMBER 0x12345678
toc_header = (fip_toc_header_t *)buf;
toc_header->name = TOC_HEADER_NAME;
toc_header->serial_number = TOC_HEADER_SERIAL_NUMBER;
toc_header->flags = toc_flags;
通过值去寻找位置。
dd if=stage1.bin of=fip.bin skip=262136 bs=1
然后使用ATF编译fiptool
git clone https://github.com/ARM-software/arm-trusted-firmware
cd arm-trusted-firmware
make fiptool
生成的fip工具在arm-trusted-firmware-master/tools/fiptool下。
我们主要是用unpack对获得的fip.bin进行拆分。
./fiptool unpack ../../../3971/fip.bin
拆分完后会获得nt-fw.bin、soc-fw.bin以及tb-fw.bin。
而通过对与ATF源码的查看我们可以知道nt-fw.bin则是BL33。
并且通过对ATF的学习,我们可以了解到,在ATF的启动流程中,BL33部分一般为uboot或者内核部分。
BL1 - AP Trusted ROM,一般为BootRom。
BL2 - Trusted Boot Firmware,一般为Trusted Bootloader。
BL31 - EL3 Runtime Firmware,一般为SML,管理SMC执行处理和中断,运行在secure monitor中。
BL32 - Secure-EL1 Payload,一般为TEE OS Image。
BL33 - Non-Trusted Firmware,一般为uboot、linux kernel。
在ATF中这一部分的修改,要相对修改其他部分容易,因为其可以是非可信部分。
因此去查看一下这部分。
查看BL33的hex值可以发现,其中有一些如Decrypt file、expand 32-byte k的字段,推测可能是在这里进行了解密的操作。
于是对这个文件进行逆向。
在逆向的过程中发现了一个函数,其中有这么一串字符串0DraytekKd5Jason3DraytekKd5Jason长度符合32字节,且在下方有Start to decrypt,故这32字节长度的字符大概率就是这个加密算法的密钥。
然后在下方发现似乎是获取了名为nonce的值。
nonce值是固件头部中的一个内容,这个值的内容为:UODAjyXZOzH0,且长度为12字节。
如果不需要这个值那就不会去获取,因此结合上面的32字节密钥以及这个12字节的大概为偏移值。可以大致推断可能是chacha20。
在分辨出大致的解密方向之后,我们需要判断该解密多大的数据。
从固件中可以以看到,加密的enc_Image是从0xF0处开始有0x34A1A00的大小。
通过dd将加密部分取出来。
dd if=v3910_4325.all of=enc_Image skip=15 count=3449248 bs=16(然后要删除前三字节)
或者
dd if=v3910_4325.all of=enc_Image skip=243 count=55187968 bs=1
然后通过对解密程序的分析,写出解密脚本对enc_Image进行解密。
from Crypto.Cipher import ChaCha20
def do_decrypt(enc_Image):
nonce = b"UODAjyXZOzH0"
with open(enc_Image, "rb") as f:
enc_data = f.read()
key = b"0DraytekKd5Eason3DraytekKd5Eason" # J to E
cipher = ChaCha20.new(key=key, nonce=nonce)
dec_data = cipher.decrypt(enc_data)
with open(enc_Image + "_decrypted.bin", "wb") as f:
f.write(dec_data)
print("Done!")
if __name__ == "__main__":
filename = "enc_Image.bin"
do_decrypt(filename)
对enc_Image进行解密之后,我们就可以考虑对解密后获得的镜像进行查看。
首先是考虑解密之后能否直接使用binwalk解包。
但是很可惜,并不能怎么做。
通过查看其熵值推断可能是压缩了。
接着就逆向查看一下内部有什么。
通过字符串查找发现,存在有关于解压缩的内容,通过这些推断,可能存在lz4、xz、lzo。
为了确定使用了什么压缩算法,通过对decompressor failed进行交叉引用查看,发现在sub_991c1c中被引用。
但是通过对该函数的查看发现并没有太多的线索可以确定使用了什么压缩的方式。
接着就对sub_991c1c交叉引用查看有没有上层函数。
对上层的sub_991f9c函数进行查看发现在函数最开始就对sub_991c1c进行了调用,并且这里所使用的值大都为全局变量。
分析sub_991f9c函数中对sub_991c1c的调用,其中首次调用中全局变量9DFC18是一个以0x184c2102开头的数据。
而另一个全局变量是值为0x2A0B6D6的数。
… …
如需阅读全文
请点击阅读原文跳转
Linux Kernel 0.12(4-3-1)-main.c终端初始化(前奏)
原文始发于微信公众号(IOTsecZone):Draytek3910 固件解密及漏洞分析