1
简介
上一篇文章中我们通过SWD接口提取出了门锁固件,并使用IDA顺利加载了固件,本篇将继续对固件进行分析,研究门锁如何处理手机通过BLE下发BleKey的通信数据。
书接上文,我们仿照果加门锁固件的处理方式,对鹿客门锁的固件进行解析之后,IDA 的导航条如下图所示
图1-1 IDA直接解析固件后的导航条
上图中可以看到,目前固件只有开始的一小部分被解析成代码了,其他绝大部分固件数据都是unexplored状态。浏览固件中的字符串,可以发现如下图所示的内容:
图1-2 固件中的部分字符串
结合被顺利解析的代码片段,我们可以推测目前此部分代码仅仅是bootloader,后续的主程序可能使用了FreeRTOS系统(一种开源的实时操作系统,https://www.freertos.org/)。
针对当前的情况,我们可以选择直接“莽”,即由bootloader开始分析,一直分析到BLE的处理流程,但是由于门锁固件使用了FreeRTOS操作系统,这个分析思路实现起来会更麻烦一点。这里可以选择一种更直接的研究方法:既然门锁电路板上已经提供了SWD引脚,这里我们就看看能不能“投机取巧”一下,直接通过SWD调试,快速定位到固件的关键位置。关于bootloader和FreeRTOS,后续会以一篇番外的形式来分享其分析方法。
2
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”连接到待调试设备,注意整个调试过程中需要保持电脑、调试器和设备的接通状态,如下图所示。
图2-1 连接到设备
连接到待调试设备后,Ozone可以像IDA一样展示多个subview,这样就可以同步观察很多信息了,下图是笔者某次调试的界面。
图2-2 Ozone调试界面
如上图所示,我们在调试时可以一次性看到寄存器的数据、多个内存区域的数据以及反汇编后的代码,下方的控制台区域可以执行一些指令或预先编写好的脚本,除此之外,左上角的断点设置区域,可以看到断点除了Location以外,还有Type和Extra两个属性,通过阅读用户手册可以确定这两个属性是用于设置断点类型的,如执行断点,读写断点、TRACE断点等,如下图所示:
图2-3 Ozone中断点的属性
Ozone还提供了很多强大的功能,在后文中,我们用到的时候就会逐一介绍这些功能。
3
通过SWD接口对门锁进行调试
翻阅门锁MCU的芯片手册可以看到,芯片提供了AES处理模块,该模块的内存映射如下图所示。
图3-1 EFM32中的内存映射
显然,我们假设鹿客门锁使用芯片的AES模块进行加解密操作,而不是自写AES算法,那么必然需要访问0x400E0000~0x400E0400这片区域中的内存地址,那么我们只要在设置适当的读写断点,然后等待手机与门锁进行通信时触发断点就可以了。
在断点设置区域右键,选择Set Data Breakpoint,会弹出如下图所示的窗口,
图3-2 设置数据端点
上图中的设置,表示当CPU向0x400E00XX地址写入数据时触发断点。
断点设置完成之后,在手机上点击开锁,由于我们设置的数据断点是监控一片内存区域,所以开锁过程中会多次被触发,后可以看到如下图左侧的一小段代码,右侧是执行到0xED0C地址时的寄存器数据。
图3-3 写入待解密数据的断点
上图中,0x400E001C地址(R0 + 28)是AES_DATA寄存器,这一小段代码所处的函数向AES_DATA寄存器写数据,应该就是AES的处理函数(下文以AESFunc代称)。回溯调用栈,可以找到调用AESFunc的外层函数,以及AESFunc函数的起始地址,进而使用IDA的F5功能对AESFunc函数进行分析,如下图:
图3-4 AESFunc函数的伪代码
结合芯片手册,由伪代码很容易能判断出AESFunc各个参数的作用,在本系列第一篇文章中,我们已经知道了开锁时的解密密钥就是BleKey,而门锁BleKey的获取过程,是由服务器下发一组数据totalData至手机app,app没有进行任何处理,直接通过BLE通信转发给了门锁。那么,接下来看一看门锁是如何处理totalData的。
3.2 门锁获取BleKey的过程
首先我们需要看一下,totalData的内容是什么,如下图所示:
图3-5 totalData字段及其base64解码数据
可以看到totalData是一串二进制数据经过base64编码后的结果,红框之前的部分可以视作数据的header,包含消息头、数据包序号、校验等内容, header之后的body部分,即红、蓝、黑框中的数据,这三组数据的结构是相同的,如下图所示:
图3-6 totalData中payload用到的数据结构
红框中data_type为0x03的数据,其data_content=0x5FFFECAE,该部分与AES根密钥密文有关。totalData的body部分还有两组数据,其具体作用不再详细说明,说太多有些不妥。
接着,我们在AESFunc的入口下一个断点,可以看到BleKey的值如下图所示:
图3-7 内存中的BleKey
从图3-5和图3-7中看不到BleKey和totalData之间的联系,想必totalData和BleKey之间还是经过了某些解密或解码转换,我们需要继续寻找门锁对totalData的处理过程。保持AESFunc函数的断点,在调试过程中可以发现totalData也是经过 AESFunc函数进行解密的。此时,回溯调用栈即可找到如下图所示的关键代码。
图3-8 totalData数据的处理
上图中的AESEntry函数为AESFunc的封装,这部分就是由totalData生成BleKey的核心代码,该流程可以整理为下图。
图3-9 BleKey获取流程图
在这个流程中,如果没有根密钥就无法解密BleKey,通过调试可以找到根密钥在Flash中的存储地址是0x7E09C。
4
总结
鹿客门锁的系列文章就先到这里,其他的功能逻辑分析就不再做过多讨论,感兴趣的读者可以自行探索。在关于鹿客的第一篇文章中,我们通过逆向手机app知道了门锁蓝牙功能的通信数据有AES加密保护;第二篇文章分享了如何使用SWD接口提取固件;本篇文章进一步通过对门锁固件的调试,来分析门锁获取BleKey的过程。在下一篇番外中,我们会介绍关于鹿客门锁的其他分析技巧,但不会再更多的讨论鹿客门锁的运行逻辑了,感兴趣的同学请多多关注我们的后续文章。最后,希望大家都能有所收获。
作者:Yimi Hu & Light @ PwnMonkeyLab
联系方式: