DIR-882路由固件加解密

  • A+
所属分类:逆向工程

固件下载

下载固件https://support.dlink.com/resource/PRODUCTS/DIR-882/REVA/DIR-882_REVA_FIRMWARE_v1.10B02.zip,这里下载的版本为DIR-882

DIR-882路由固件加解密解压缩可以看到给了两个版本一个为加密版,一个为未加密版。

加密版

DIR-882路由固件加解密

未加密版

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
DIR-882路由固件加解密

可以看出此程序解密程序

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;
}
DIR-882路由固件加解密

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, 064);
  v12 = 0;
  v13 = 0;
  memset(file_buf, 0sizeof(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, 11, file_fp, 0);// 固件映射到内存
      if ( v6 )
      {
        v11 = open(a2, 0x102);
        if ( v11 >= 0 )
        {
          v9 = file_size;
          if ( file_size - 1 == lseek(v11, file_size - 10) )// 比较写入内存文件和固件文件的大小
          {
            write(v11, &unk_402E8C, 1);
            close(v11);
            v11 = open(a2, 0x102);
            v7 = mmap(0, v9, 31, 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 + 0x2764) )// 比较消息摘要,这里反编译错误($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 + 0x1764) )// 这里反编译错误($v0+0x5c)
                  {
                    sub_400D34(v7, v13, aes_key, (int)firmware_de);// 计算解密固件+aes_key的SHA512
                    if ( !memcmp(firmware_de, firmware_fp + 70x40) )// 这里反编译错误($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
DIR-882路由固件加解密

运行解密程序

# ./bin/imgdecrypt /home/DIR882A1_FW110B02.bin
DIR-882路由固件加解密

查看解密效果

$ sudo binwalk DIR882A1_FW110B02.bin
DIR-882路由固件加解密

可以看到成功解密

参考链接

  • 加密固件之依据老固件进行解密

  • Breaking the D-Link DIR3060 Firmware Encryption

end


招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系[email protected]



DIR-882路由固件加解密

原文始发于微信公众号(ChaMd5安全团队):DIR-882路由固件加解密

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: