固件下载
下载固件https://support.dlink.com/resource/PRODUCTS/DIR-882/REVA/DIR-882_REVA_FIRMWARE_v1.10B02.zip,这里下载的版本为DIR-882
解压缩可以看到给了两个版本一个为加密版,一个为未加密版。
加密版
未加密版
固件解包
对未加密的版本进行解包
binwalk -Me DIR882A1_FW104B02_Middle_FW_Unencrypt.bin
在_DIR882A1_FW104B02_Middle_FW_Unencrypt.bin.extracted/_A0.extracted/_8AB758.extracted/cpio-root
目录下可以看到目录结构
$ tree -d .
.
├── bin
├── dev
│ └── pts
├── etc
├── etc_ro
│ ├── l7-protocols
│ │ ├── not_commit
│ │ └── untested
│ ├── lighttpd
│ │ ├── lib
│ │ └── www
│ │ └── web
│ │ ├── captcha
│ │ ├── config
│ │ │ └── deviceinfo
│ │ ├── css
│ │ ├── hnap
│ │ ├── HNAP1
│ │ ├── image
│ │ ├── info
│ │ └── js
│ │ ├── bootstrap
│ │ │ ├── css
│ │ │ └── js
│ │ ├── ClientList
│ │ ├── configuration
│ │ ├── ipv6
│ │ ├── localization
│ │ ├── SOAP
│ │ └── wizard
│ ├── linuxigd
│ ├── onetouch
│ │ └── scripts
│ ├── ppp
│ │ ├── 3g
│ │ ├── peers
│ │ └── plugins
│ ├── usb
│ ├── web
│ ├── wifi
│ ├── Wireless
│ │ ├── iNIC
│ │ ├── RT2860AP
│ │ ├── RT2870STA
│ │ ├── RT61AP
│ │ └── WIFI3
│ ├── wlan
│ └── xml
├── home
├── lib
│ ├── firmware
│ ├── ipsec
│ └── modules
│ └── 3.10.14+
│ └── kernel
│ └── net
│ ├── ipv4
│ │ └── netfilter
│ ├── nat
│ │ └── hw_nat
│ └── netfilter
├── media
├── mnt
├── private
├── proc
├── sbin
├── share
│ ├── man
│ │ ├── man1
│ │ ├── man5
│ │ └── man8
│ └── udhcpc
├── sys
├── tmp
├── usr
│ ├── bin
│ ├── codepages
│ ├── lib
│ │ └── pppd
│ │ └── 2.4.5
│ ├── local
│ │ └── ssl
│ └── sbin
├── var
└── www
├── rsstest
├── rssui
│ ├── img
│ └── public
└── tmp -> /var/tmp
90 directories
寻找解密点
-
尝试在后端
lighttpd
中寻找相关字符串。$ sudo find . -name "*httpd*" | xargs strings | grep "firm"
strings: Warning: './etc_ro/lighttpd' is a directory没有找到。
-
尝试在
CGI
中寻找相关字符串。$ sudo find . -name "*cgi*" | xargs strings | grep "firm"
/tmp/firmware.img
/bin/imgdecrypt /tmp/firmware.img
/tmp/firmware.img
mtd_write -r -w write /tmp/firmware.img Kernel &
rm /tmp/firmware.img
rm /tmp/.firmware.orig
wget -O %s "http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s"
rm -f /tmp/firmware.img &
http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s
/tmp/firmware.img
/bin/imgdecrypt /tmp/firmware.img
/tmp/firmware.img
mtd_write -r -w write /tmp/firmware.img Kernel &
rm /tmp/firmware.img
rm /tmp/.firmware.orig
wget -O %s "http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s"
rm -f /tmp/firmware.img &
http://wrpd.dlink.com/router/firmware/query.aspx?model=%s_%s_%s_FW_%s_%s可以看到之中存在
/bin/imgdecrypt
,疑似解密的文件。
分析程序
$ file imgdecrypt
imgdecrypt: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
可以看出此程序解密程序
int __fastcall decrypt_firmare(int a1, int a2)
{
int result; // $v0
const char *v3; // [sp+18h] [-1Ch]
int i; // [sp+1Ch] [-18h]
int aes_key[5]; // [sp+20h] [-14h] BYREF
qmemcpy(aes_key, "0123456789ABCDEF", 16); // aes_en_key
v3 = "/etc_ro/public.pem";
i = -1;
if ( a1 >= 2 )
{
if ( a1 >= 3 )
v3 = *(const char **)(a2 + 8);
if ( sub_40215C((int)v3, 0) ) // 检查证书
{
result = -1;
}
else
{
sub_402554((int)aes_key); // aes加密
printf("key:");
for ( i = 0; i < 16; ++i )
printf("%02X", *((unsigned __int8 *)aes_key + i));// C05FBF1936C99429CE2A0781F08D6AD8
puts("r");
i = sub_401780(*(_DWORD *)(a2 + 4), (int)"/tmp/.firmware.orig", (int)aes_key);// 解密函数
if ( !i )
{
unlink(*(_DWORD *)(a2 + 4));
rename("/tmp/.firmware.orig", *(_DWORD *)(a2 + 4));
}
RSA_free(dword_4131C0); // RSA 对象地址
result = i;
}
}
else
{
printf("%s <sourceFile>rn", *(const char **)a2);
result = -1;
}
return result;
}
sub_402554函数分析
int __fastcall sub_40108C(int a1, int a2, int a3, int *a4, int a5)
{
int v5; // $a0
int v6; // $v1
int v7; // $v0
char v9[244]; // [sp+24h] [-108h] BYREF
int v10[5]; // [sp+118h] [-14h] BYREF
int v12; // [sp+134h] [+8h]
v12 = a2 + (1 - (a2 & 0xF)) * ((a2 & 0xF) != 0);
AES_set_decrypt_key(a3, 128, v9);
v5 = a4[1];
v6 = a4[2];
v7 = a4[3];
v10[0] = *a4;
v10[1] = v5;
v10[2] = v6;
v10[3] = v7;
AES_cbc_encrypt(a1, a5, v12, v9, v10, 0);
return 0;
}
sub_401780函数分析
int __fastcall sub_401780(int file, int a2, int aes_key)
{
int file_fp; // [sp+20h] [-108h]
int v5; // [sp+24h] [-104h]
_DWORD *v6; // [sp+28h] [-100h]
int v7; // [sp+2Ch] [-FCh]
int file_size; // [sp+30h] [-F8h]
int v9; // [sp+34h] [-F4h]
int i; // [sp+38h] [-F0h]
int v11; // [sp+3Ch] [-ECh]
int v12; // [sp+40h] [-E8h]
int v13; // [sp+44h] [-E4h]
_DWORD *firmware_fp; // [sp+48h] [-E0h]
char firmware_de[68]; // [sp+4Ch] [-DCh] BYREF
int file_buf[38]; // [sp+90h] [-98h] BYREF
file_fp = -1;
v11 = -1;
v5 = -1;
v6 = 0;
v7 = 0;
file_size = -1;
v9 = -1;
memset(firmware_de, 0, 64);
v12 = 0;
v13 = 0;
memset(file_buf, 0, sizeof(file_buf));
firmware_fp = 0;
if ( !stat(file, file_buf) )
{
file_size = file_buf[13];
file_fp = open(file, 0);
if ( file_fp >= 0 )
{
v6 = (_DWORD *)mmap(0, file_size, 1, 1, file_fp, 0);// 固件映射到内存
if ( v6 )
{
v11 = open(a2, 0x102);
if ( v11 >= 0 )
{
v9 = file_size;
if ( file_size - 1 == lseek(v11, file_size - 1, 0) )// 比较写入内存文件和固件文件的大小
{
write(v11, &unk_402E8C, 1);
close(v11);
v11 = open(a2, 0x102);
v7 = mmap(0, v9, 3, 1, v11, 0); // 以加密固件大小将待解密的固件映射到内存
if ( v7 )
{
firmware_fp = v6;
if ( sub_400C40((int)v6) ) // 判断固件开头是否为'SHRS'
{
v12 = htonl(firmware_fp[2]); // 13265600
v13 = htonl(firmware_fp[1]); // 13265595
sub_400C94((int)(firmware_fp + 0x1B7), v12, (int)firmware_de);// 从偏移0x6DC计算长度为1326560的SHA512,这里反编译错误($v0+0x6dc)
if ( !memcmp(firmware_de, firmware_fp + 0x27, 64) )// 比较消息摘要,这里反编译错误($v0+0x9c)
{
aes((int)(firmware_fp + 0x1B7), v12, aes_key, firmware_fp + 3, v7);// aes 解密,key为偏移0xC处,这里反编译错误($v0+0xc)
sub_400C94(v7, v13, (int)firmware_de);// 计算解密后的消息摘要
if ( !memcmp(firmware_de, firmware_fp + 0x17, 64) )// 这里反编译错误($v0+0x5c)
{
sub_400D34(v7, v13, aes_key, (int)firmware_de);// 计算解密固件+aes_key的SHA512
if ( !memcmp(firmware_de, firmware_fp + 7, 0x40) )// 这里反编译错误($v0+0x1c)
{ // rsa验证摘要
if ( sub_400E88((int)(firmware_fp + 0x17), 64, (int)(firmware_fp + 0xB7), 0x200) == 1 )// 这里反编译错误($v0+0x2dc)
{
if ( sub_400E88((int)(firmware_fp + 0x27), 64, (int)(firmware_fp + 0x137), 0x200) == 1 )// 这里反编译错误($v0+0x4dc)
v5 = 0;
else
v5 = -1;
}
else
{
v5 = -1;
}
}
else
{
puts("check sha512 vendor failedr");
}
}
else
{
printf("check sha512 before failed %d %drn", v13, v12);
for ( i = 0; i < 64; ++i )
printf("%02X", (unsigned __int8)firmware_de[i]);
puts("r");
for ( i = 0; i < 64; ++i )
printf("%02X", *((unsigned __int8 *)firmware_fp + i + 92));
puts("r");
}
}
else
{
puts("check sha512 post failedr");
}
}
else
{
puts("no image matic foundr");
}
}
}
}
}
}
}
if ( v6 )
munmap(v6, file_size);
if ( v7 )
munmap(v7, v9);
if ( file_fp >= 0 )
close(file_fp);
if ( file_fp >= 0 )
close(file_fp);
return v5;
}
反编译错误分析,函数sub_400C94,其余函数同理
.text:00401AF4 lw $gp, 0x128+var_110($sp)
.text:00401AF8 sw $v0, 0x128+var_E4($sp)
.text:00401AFC lw $v0, 0x128+firmware_fp($sp)
.text:00401B00 nop
.text:00401B04 addiu $v1, $v0, 0x6DC ; $v1为参数,$v0存放firmware_fp,所以应为(firmware_fp+0x6DC)
.text:00401B08 addiu $v0, $sp, 0x128+firmware_de
解密
对程序进行仿真运行
$ cp /usr/bin/qemu-mipsel-static .
$ sudo chroot . ./qemu-mipsel-static /bin/sh
运行解密程序
# ./bin/imgdecrypt /home/DIR882A1_FW110B02.bin
查看解密效果
$ sudo binwalk DIR882A1_FW110B02.bin
可以看到成功解密
参考链接
-
加密固件之依据老固件进行解密
-
Breaking the D-Link DIR3060 Firmware Encryption
end
招新小广告
ChaMd5 Venom 招收大佬入圈
新成立组IOT+工控+样本分析 长期招新
原文始发于微信公众号(ChaMd5安全团队):DIR-882路由固件加解密