# 固件解密

有些IOT设备会对固件加密甚至签名来提高研究门槛和升级时的安全性，因为加解密比较耗费资源，这类设备一般配置会比较高，比如一些路由器和防火墙。

### 一、**固件加密判断**

判断固件是否加密比较简单，有经验的小伙伴有二进制编辑器打开就能看出一二，一般会存在以下特性。

除了固件指示头没有可见字符，(除去header)数据按比特展开01频率基本一致binwalk(-e)无法解析固件结构，且(-A)没有识别出任何cpu架构指令。

如果满足上述特点，就会猜测固件已被加密，固件解密一般会从这几个角度，但也不局限于下面的方法。

### 二、**硬件获取密钥**

此种方法只限于固件始终以加密状态存在，当系统启动时才通过解密解包加载至flash，且设备缺乏(UART/JTAG等)动态调试手段。由于flash中有完整的解密过程，可以通过编程器读取flash，逆向解密算法和密钥，达到解密固件的目的。比如从某设备的读取的flash内存分布如下：

```
0x000000-0x020000 boot section
0x020000-0x070000 encrypt section
0x070000-0x200000 encrypt section
0x200000-0x400000 config section
```

显然我们需要的加密过程在boot section中，我们需要从中找到加密算法和密钥，一般加密都采用AES等公开分组算法，关键是找到分组模式，IV(非ECB)和密钥。将boot加载到IDA pro中，并没有自动识别：

![](https://image.3001.net/images/20201108/1604819400_5fa799c8397b91f658e6c.jpg)

可以通过对比ARM代码最开始部分的中断向量表结构手动识别，常见的入口代码如下所示。

```
.globl _start
_start:
    b       reset
    ldr     pc, _undefined_instruction
    ldr     pc, _software_interrupt
    ldr     pc, _prefetch_abort
    ldr     pc, _data_abort
    ldr     pc, _not_used
    ldr     pc, _irq
    ldr     pc, _fiq
...
_irq:
        .word irq
```

之后可以就可以逆向得到加密算法为AES，密钥通过设备序列号的sha256哈希获得。

![](https://image.3001.net/images/20201108/1604819431_5fa799e7b59c119be629e.jpg)

通过IDA pro识别此类结构将在后文介绍RTOS时探讨，利用这种固件加密方式的设备安全级别教高，一般设备只在升级时进行解密验证。

### 三、**调试直接读取**

这个方法最容易理解，即在设备启动后利用UART、JTAG、Console或网络等手段把固件(打包)回传，这样就绕过了解密环节。值得注意的是需要设备提供这些接口，具体方法因设备不同而异，这些接口的使用将会在硬件篇里作介绍。

### 四、**对比边界版本**

此种方法适用于厂商一开始没采用加密方案，即旧版固件未加密，在某次升级中添加了解密程序，随后升级使用加密固件。这样我们就可以从一系列固件中找到加密和未加密之间的边界版本，解包最后一个未加密版本逆向升级程序即可还原加密过程。

![](https://image.3001.net/images/20201108/1604819495_5fa79a2713d8aa24f8eab.jpg)

通过下载上图所示某路由器固件，解包后通过搜索包含诸如“firmware”、“upgrade”、“update”、“download”等关键字的组合定位升级程序位置。当然存在调试手段也可以在升级时ps查看进程更新定位升级程序和参数：

```
/usr/sbin/encimg -d -i <fw_path> -s <image_sign>
```

通过IDA pro逆向encimg程序很快得到加解密过程代码，采用了AES CBC模式：

```
AES_set_decrypt_key (
   // user input key
   const unsigned char *userKey,
   // size of key
   const int bits,
   // encryption key struct which will be used by
   // encryption function
   AES_KEY *key
)

AES_cbc_encrypt (
   // input buffer
   const unsigned char *in,
   // output buffer
   unsigned char *out,
   // buffer length
   size_t length,
   // key struct return by previous function
   const AES_KEY *key,
   // initializatin vector
   unsigned char *ivec,
   // is encryption or decryption
   const int enc
)
```

### 五、**向升级程序**

此种方法适用于已经通过接口或边界版本得到升级程序，可以利用分组算法的盒检测工具来判别加密算法和定位位置，当然binwalk也可以解析某些简单情况，比如某工控HMI固件：

```
iot@attifyos ~/Documents> binwalk hmis.tar.gz
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
34       0x22        OpenSSL encyption, salted, salt:0x5879382A7
```

直接加载升级程序，定位openssl调用很容易就得到解密命令：

![](https://image.3001.net/images/20201108/1604819530_5fa79a4acd776fcb96ba9.jpg)

### 六、漏洞获取密钥

如果找不到边界版本，又找不到调试接口或不熟悉硬件调试，可以考虑采用历史版本漏洞先获取设备控制权，在拿到升级程序逆向加密算法。这种方法比较取巧，需要设备存在RCE漏洞的历史固件，通过降级操作植入漏洞获取权限，下载所需升级程序，然后逆向得到加密算法。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wokough.gitbook.io/iot-firmware-aio/wiki/gu-jian-jie-mi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
