前言
由于接触到的各种设备的固件都有不同的架构,在逆向的过程中也会遇到各种各样的问题,所以打算写一篇文章记录下来。
奇奇怪怪的芯片和架构
Telink(泰凌微)
芯片名称: Tlsr8251
架构名称: Telink
ghidra插件: https://github.com/trust1995/Ghidra_TELink_TC32
官方sdk:https://wiki.telink-semi.cn/wiki/chip-series/TLSR825x-Series/
开发文档:https://wiki.telink-semi.cn/doc/an/AN-21112301-C_Telink%20B85m%20BLE%20Single%20Connection%20SDK%20Developer%20Handbook.pdf
OM(昂瑞微)
芯片名称: OMEM6621
架构名称: arm-cortex-m4
开发文档: 需要找官方签署保密协议
官方sdk: 需要找官方签署保密协议
BK(博通)
芯片名称: bk3432
NuvoTon(新唐)
芯片名称: NANO100SE3BN
架构名称: arm-cortex-m0
官方文档: https://www.nuvoton.com/products/microcontrollers/arm-cortex-m0-mcus/nano100-102-base-series/nano100se3bn/?group=Document&tab=2
一些零零散散的小知识
需要自己新增segment
逆向omem6621芯片的固件中遇到的这个问题,一开始是通过中断入口地址来确定固件的基地址,但是ida一通分析之后只解析出来400多个函数,用bindiff恢复符号表后还有很多库函数比对不出来,那大概率就是少分析了很多函数。
比对了sdk编译出来的固件,得知了在ROM中还有两块区域需要单独抓到其他地址处,这样才能满足调用需求,所以要在ida中edit segment再move segment。
缩减固件体积
telink架构
telink旗下的产品分为两种,一种是自研架构,需要使用tc32 toolchain,还有一种是基于开源架构risc-v,采用risc-v toolchain。本模块以telink自研的架构为例。
通常来说,编译telink架构的固件可以使用官方提供的telink ide。但是我们的目的是为了缩减固件大小,ide更改编译选项已经不能满足需求,所以这里直接使用makefile编译
makefile的内容无关紧要,注意一下使用几个优化固件大小的编译选项即可
接着可以查找cstartup_825x.s文件,这个是系统的启动文件,用于堆栈的初始化配置,中断向量表以及引导程序。
在文件中可以找到下列的代码,这一段初始化代码调用了大量的tmov r8, r8指令用于延时,显然会对固件大小造成负担,这里进行优化,删除tmov r8, r8指令又或者是采用其他的延时方案都可以
1 | @******************************************************************************************************** |
接下来的思路是优化代码本身,这里只是提供一个优化函数的思路,仅供参考。
在控制gpio接口时会用到gpio_set_func函数,经过比对这个函数的调用会占用大量字节,找到drive/gpio.c,查看这个函数的实现。根据不同的参数情况进行了多种if分支,如果固件代码中调用的gpio_set_func只走一种分支,又或者我们不需要调用gpio_set_mux函数也可以达到一样的效果,这里就可以将gpio_set_func中的函数拆分开,从而节约固件大小。
1 | void gpio_set_func(GPIO_PinTypeDef pin, GPIO_FuncTypeDef func) |
如何寻找调试接口
要想获取一款设备的固件,通常可以从拦截固件更新包,或者是通过电路板的调试接口来下载固件,那么寻找电路板的调试接口就成为了固件逆向的重中之重。
调试接口有多种标准,例如JTAG,SWD等,不同标准要找的调试接口数量以及类型都不一样
JTAG
SWD
telink私有调试协议
telink芯片采取自己的私有调试协议进行固件的烧录以及提取,为单线调试协议,只需要将烧录器的swm接口和电路板的sws接口连接再将gnd相连即可
以上图中的电路板举例 调试接口一般都是扎堆存在,所以重点关注红框圈起来的部分。
4个的部分在电路板背面可以看到有标识其他用途,所以先排除
那么就是在左侧的7个接口中找到sws和gnd调试接口,这里可以用万用表来找到
首先将万用表调成蜂鸣挡
接着将黑表笔接到电路板的负极上,红表笔依次接入接口,如果发出蜂鸣声说明红表笔当前所指的接口是gnd接口
随后用遍历的方式来寻找sws接口,如果可以读取到flash芯片那么就找到了调试接口。
固件分析的经验之谈
Arm架构m核心的启动
很常见的一个知识点,arm架构中的m系列芯片,主打的是一个低成本高能效的需求。常用于小型设备中,比如家用电子设备等。
Mcu复位后需要一段启动代码来引导程序,而存储于固件头部的地址信息,称之为中断入口地址。
从上到下分别是:
1 | 1. __initial_sp 栈顶 |
通过将栈顶赋值为sp寄存器,随后跳转到复位中断函数,初始化Mcu时钟,然后执行main函数。
所以通常可以用这个知识点来判断固件的基地址以及main函数的位置
ROM LIB库
在嵌入式设备中,将稳定且不常修改的功能模块预先编译为二进制代码,烧录到设备的Flash或ROM区域,称之为ROM Library。
应用程序运行时,通过地址跳转或者符号引用来调用函数和访问数据。
一些芯片的官方文档中,通常会随着sdk一同给出lib文件。
多地址启动机制
这个机制第一次看到是在采用telink芯片的设备中,可以在flash中烧录多个固件,bootloder根据标识位来决定启动哪个固件。
以telink为例,默认的启动地址是0x0开始的,此外还可以从0x20000,0x40000开始启动。
这一机制的用处,我个人认为是为了方便dfu升级,dfu传入的新固件可以存放于0x20000处,这样就不需要删除原本的固件也可以做到更新固件了。
那么还有一个问题,bootloader是如何判断该从哪个地址启动的呢。根据的是启动地址的0x8偏移处的4个标识字节
bootloader会先从0x0开始读取这个位置的值,如果符合那么就启动该位置的固件,否则继续往后。