# 云丁鹿客门锁BLE通信的分析（下）

## **简介**

\
上一篇文章中我们通过SWD接口提取出了门锁固件，并使用IDA顺利加载了固件，本篇将继续对固件进行分析，研究门锁如何处理手机通过BLE下发BleKey的通信数据。书接上文，我们仿照果加门锁固件的处理方式，对鹿客门锁的固件进行解析之后，IDA 的导航条如下图所示

![图1-1 IDA直接解析固件后的导航条](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQk5rCvW38x4tQ06P3%2Fimage.png?alt=media\&token=b7e7d302-2ef2-4213-bfc1-3fa6113bc512)

上图中可以看到，目前固件只有开始的一小部分被解析成代码了，其他绝大部分固件数据都是unexplored状态。浏览固件中的字符串，可以发现如下图所示的内容：

![图1-2 固件中的部分字符串](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQk8yxwUB8A4Pmw9Cs%2Fimage.png?alt=media\&token=e64de7de-59ab-4d40-a323-ad2f0cc2f55a)

结合被顺利解析的代码片段，我们可以推测目前此部分代码仅仅是bootloader，后续的主程序可能使用了FreeRTOS系统（一种开源的实时操作系统，[https://www.freertos.org/）。](https://www.freertos.org/%EF%BC%89%E3%80%82)&#x20;

针对当前的情况，我们可以选择直接“莽”，即由bootloader开始分析，一直分析到BLE的处理流程，但是由于门锁固件使用了FreeRTOS操作系统，这个分析思路实现起来会更麻烦一点。这里可以选择一种更直接的研究方法：既然门锁电路板上已经提供了SWD引脚，这里我们就看看能不能“投机取巧”一下，直接通过SWD调试，快速定位到固件的关键位置。关于bootloader和FreeRTOS，后续会以一篇番外的形式来分享其分析方法。

## **SEGGER JLink的调试功能**

在进行调试之前，先简单介绍一下如何使用SEGGER JLink（下文以JLink代称）的调试功能。上一篇文章中，我们在提取固件时使用了JLink的命令行工具，这个命令行工具也包含了诸如设置断点、暂停CPU核心、读取/写入寄存器或指定位置的内存、单步调试等动态调试所需要的指令，具体可以查阅SEGGER的Wiki（<https://wiki.segger.com/J-Link\\_Commander）。可以选择openOCD配合JLink进行调试，但我们在本篇中暂不介绍openOCD，而是选择另一款调试工具。>

在实际调试过程中，命令行工具通常需要搭配IDA使用，用起来非常繁琐，且信息展示不够直观。SEGGER提供了一个图形界面工具——Ozone，可以在一定程度上解决以上问题，关于Ozone的介绍可以参考SEGGER的官方网站（<https://www.segger.com/products/development-tools/ozone-j-link-debugger/），软件以及使用手册的下载页面也可以在这里找到。下载后的安装一路next就可以了，想必大家都很熟练（滑稽）。>

安装完成后，可以通过“File->New->New Project Wizard”选项来创建新的项目，创建时需要选择待调试芯片的型号、调试接口类型、通信速率等信息。继续，点击“Debug->Start Debug Session->Attach to Running Program”连接到待调试设备，注意整个调试过程中需要保持电脑、调试器和设备的接通状态，如下图所示。

**SEGGER JLink的调试功能**

\
在进行调试之前，先简单介绍一下如何使用SEGGER JLink（下文以JLink代称）的调试功能。上一篇文章中，我们在提取固件时使用了JLink的命令行工具，这个命令行工具也包含了诸如设置断点、暂停CPU核心、读取/写入寄存器或指定位置的内存、单步调试等动态调试所需要的指令，具体可以查阅SEGGER的Wiki（[https://wiki.segger.com/J-Link\\\_Commander）。可以选择openOCD配合JLink进行调试，但我们在本篇中暂不介绍openOCD，而是选择另一款调试工具。在实际调试过程中，命令行工具通常需要搭配IDA使用，用起来非常繁琐，且信息展示不够直观。SEGGER提供了一个图形界面工具——Ozone，可以在一定程度上解决以上问题，关于Ozone的介绍可以参考SEGGER的官方网站（https://www.segger.com/products/development-tools/ozone-j-link-debugger/），软件以及使用手册的下载页面也可以在这里找到。下载后的安装一路next就可以了，想必大家都很熟练（滑稽）。安装完成后，可以通过“File->New->New](https://wiki.segger.com/J-Link\\_Commander）。可以选择openOCD配合JLink进行调试，但我们在本篇中暂不介绍openOCD，而是选择另一款调试工具。在实际调试过程中，命令行工具通常需要搭配IDA使用，用起来非常繁琐，且信息展示不够直观。SEGGER提供了一个图形界面工具——Ozone，可以在一定程度上解决以上问题，关于Ozone的介绍可以参考SEGGER的官方网站（https://www.segger.com/products/development-tools/ozone-j-link-debugger/），软件以及使用手册的下载页面也可以在这里找到。下载后的安装一路next就可以了，想必大家都很熟练（滑稽）。安装完成后，可以通过“File->New->New) Project Wizard”选项来创建新的项目，创建时需要选择待调试芯片的型号、调试接口类型、通信速率等信息。继续，点击“Debug->Start Debug Session->Attach to Running Program”连接到待调试设备，注意整个调试过程中需要保持电脑、调试器和设备的接通状态，如下图所示。

![图2-1 连接到设备](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkNTRBdSq1LaUtVt9%2Fimage.png?alt=media\&token=6245ed7c-cc1b-4e78-a55f-419854f4d257)

连接到待调试设备后，Ozone可以像IDA一样展示多个subview，这样就可以同步观察很多信息了，下图是笔者某次调试的界面。

![图2-2 Ozone调试界面](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkXcXyJcOHTeL3hPu%2Fimage.png?alt=media\&token=ce29b1ab-ee63-4cee-a5e4-242612d44ae4)

如上图所示，我们在调试时可以一次性看到寄存器的数据、多个内存区域的数据以及反汇编后的代码，下方的控制台区域可以执行一些指令或预先编写好的脚本，除此之外，左上角的断点设置区域，可以看到断点除了Location以外，还有Type和Extra两个属性，通过阅读用户手册可以确定这两个属性是用于设置断点类型的，如执行断点，读写断点、TRACE断点等，如下图所示：

![图2-3 Ozone中断点的属性](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkaTgsX5mY5bqmruS%2Fimage.png?alt=media\&token=a71d7812-2476-46d3-be49-82ec37cf8c81)

Ozone还提供了很多强大的功能，在后文中，我们用到的时候就会逐一介绍这些功能。

## **通过SWD接口对门锁进行调试**

翻阅门锁MCU的芯片手册可以看到，芯片提供了AES处理模块，该模块的内存映射如下图所示。<br>

![图3-1 EFM32中的内存映射](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkgRK_WkDn3ChU6t8%2Fimage.png?alt=media\&token=75c6ec96-4907-4f6f-945c-01d9755ca810)

显然，我们假设鹿客门锁使用芯片的AES模块进行加解密操作，而不是自写AES算法，那么必然需要访问0x400E0000\~0x400E0400这片区域中的内存地址，那么我们只要在设置适当的读写断点，然后等待手机与门锁进行通信时触发断点就可以了。

在断点设置区域右键，选择Set Data Breakpoint，会弹出如下图所示的窗口，

![图3-2 设置数据端点](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkjxE7bJ7QmIPG0bL%2Fimage.png?alt=media\&token=9a6f8d74-16d0-4f60-a27f-23d3bab4ee15)

上图中的设置，表示当CPU向0x400E00XX地址写入数据时触发断点。

断点设置完成之后，在手机上点击开锁，由于我们设置的数据断点是监控一片内存区域，所以开锁过程中会多次被触发，后可以看到如下图左侧的一小段代码，右侧是执行到0xED0C地址时的寄存器数据。

![图3-3 写入待解密数据的断点](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQknXS_JiWByDP7Qcb%2Fimage.png?alt=media\&token=349b3133-b76b-4acf-b2ed-aca2ca37f611)

上图中，0x400E001C地址（R0 + 28）是AES\_DATA寄存器，这一小段代码所处的函数向AES\_DATA寄存器写数据，应该就是AES的处理函数（下文以AESFunc代称）。回溯调用栈，可以找到调用AESFunc的外层函数，以及AESFunc函数的起始地址，进而使用IDA的F5功能对AESFunc函数进行分析，如下图：

![图3-4 AESFunc函数的伪代码](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkqhZwNeDk893KaD7%2Fimage.png?alt=media\&token=886d0c33-a211-40aa-9a5f-7e7049a46fa1)

结合芯片手册，由伪代码很容易能判断出AESFunc各个参数的作用，在本系列第一篇文章中，我们已经知道了开锁时的解密密钥就是BleKey，而门锁BleKey的获取过程，是由服务器下发一组数据totalData至手机app，app没有进行任何处理，直接通过BLE通信转发给了门锁。那么，接下来看一看门锁是如何处理totalData的。

### **3.2 门锁获取BleKey的过程**

首先我们需要看一下，totalData的内容是什么，如下图所示：

![图3-5 totalData字段及其base64解码数据](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkuu4jhB2Za7rCWfD%2Fimage.png?alt=media\&token=bf73e5e7-0b7b-4a39-8568-61f363bac3c0)

可以看到totalData是一串二进制数据经过base64编码后的结果，红框之前的部分可以视作数据的header，包含消息头、数据包序号、校验等内容， header之后的body部分，即红、蓝、黑框中的数据，这三组数据的结构是相同的，如下图所示：

![图3-6 totalData中payload用到的数据结构](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQkzBq3nrg4u_efR-X%2Fimage.png?alt=media\&token=a77e0b37-f588-435f-a82f-12d4e42b7f8e)

红框中data\_type为0x03的数据，其data\_content=0x5FFFECAE，该部分与AES根密钥密文有关。totalData的body部分还有两组数据，其具体作用不再详细说明，说太多有些不妥。

接着，我们在AESFunc的入口下一个断点，可以看到BleKey的值如下图所示：

![图3-7 内存中的BleKey](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQl1UC5uzEpWhMpMmR%2Fimage.png?alt=media\&token=bedcd28e-f44d-4019-9345-62412388906d)

从图3-5和图3-7中看不到BleKey和totalData之间的联系，想必totalData和BleKey之间还是经过了某些解密或解码转换，我们需要继续寻找门锁对totalData的处理过程。保持AESFunc函数的断点，在调试过程中可以发现totalData也是经过 AESFunc函数进行解密的。此时，回溯调用栈即可找到如下图所示的关键代码。

![图3-8 totalData数据的处理](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQl4in9c-SG3n2Jy8n%2Fimage.png?alt=media\&token=7650d484-08b9-4225-908e-6a344702dee6)

上图中的AESEntry函数为AESFunc的封装，这部分就是由totalData生成BleKey的核心代码，该流程可以整理为下图。

![图3-9 BleKey获取流程图](https://3519990514-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-Mc9RUlvJLCDxdpf1zh2%2F-MdQfxPcwmGI03eMXsWH%2F-MdQl7gL50OWuEGMvjmI%2Fimage.png?alt=media\&token=d6c59281-b504-48e8-9d26-0d6230d14b5b)

在这个流程中，如果没有根密钥就无法解密BleKey，通过调试可以找到根密钥在Flash中的存储地址是0x7E09C。

## **总结**

鹿客门锁的系列文章就先到这里，其他的功能逻辑分析就不再做过多讨论，感兴趣的读者可以自行探索。在关于鹿客的第一篇文章中，我们通过逆向手机app知道了门锁蓝牙功能的通信数据有AES加密保护；第二篇文章分享了如何使用SWD接口提取固件；本篇文章进一步通过对门锁固件的调试，来分析门锁获取BleKey的过程。在下一篇番外中，我们会介绍关于鹿客门锁的其他分析技巧，但不会再更多的讨论鹿客门锁的运行逻辑了，感兴趣的同学请多多关注我们的后续文章。最后，希望大家都能有所收获。
