STM32 & RT-Thread的逆向入门
backahasten@0xFA
现在,各种MCU的价格越来越低,同等条件下能买到的ROM和RAM资源也多了。对一些复杂逻辑的应用,相比于花费大量的时间去扣底层还不如使用操作系统加快开发速度,使用了操作系统之后,针对固件的逆向会比无os的固件逆向有一些不同。
RT-Thread是国产实时操作系统的典范,我个人的特别喜欢。RT-Thread有很多的BSP可以适配众多的芯片和架构,在本文还是使用最常见的stm32来进行介绍。
硬件上我是用了正点原子核RT-Thread联合开发的潘多拉开发板,在本文中没有使用潘多拉的硬件,我比较偷懒的使用了开发板配套的例子去做逆向。
启动
我一直相信一句话,不会开发也就不会安全,开发能力决定着安全能力的天花板。在逆向STM32 & RT-Thread的过程中也是这个样子的。我们打开一个bin文件,第一件事就是找到main函数,这个main函数并不是stm32的main函数而是操作系统的main函数。例如在stm32 & keil开发中,RT-Thread使用$Sub$$main
调用初始化操作系统,如果是不带操作系统的裸开发,这个函数就是逻辑的main函数了。
详细的启动过程可以在https://www.rt-thread.org/document/site/tutorial/quick-start/stm32f103-simulator/stm32f103-simulator/中找到,为了本文的完整,我们复制过来一些代码。
//components.c 中定义
/* re-define main function */
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
这里面调用了两个函数,第一个函数关闭了中断,开始初始化操作系统。
int rtthread_startup(void)
{
rt_hw_interrupt_disable();
/* board level initalization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
在rtthread_startup();
函数中,进行各个组件的初始化,其他我们不用管,重点看其中的rt_application_init();
函数,这个函数用于线程任务的初始化,在其中,我们就可以脱离操作系统代码的范畴,进入到真正的逻辑代码的位置。
操作
我们使用潘多拉开发板的例子29_iot_web_server,这个例子复杂程度中等,可以充分的分析各个点。keil编译之后,会的带axf文件和Hex文件,其中axf带有符号表,而hex文件只有基础的组织结构,由于嵌入式的特点,为了节约资源,烧录进芯片里的固件基本都不会带符号表。为了仿造真实情况下拿到的固件,我们使用jlink的工具打开hex文件之后保存成bin文件。之后使用ida pro打开。
打开文件之后,正确载入逆向,显示如下:(使用IDA PRO逆向ARM M核心的方法请参考我之前的文章)
选中的地址 0x8000495为芯片的上电PC载入地址,找到地址之后,如下:
由于最开始是硬件PC设置,没有软件引用关系,ida pro没有识别,在0x8000494处按C,进行反汇编:
发现可以看到stm32的启动代码,进入函数0x800188:
发现0x800018C处的函数没有识别,依旧按C:
发现识别出来一个函数的跳转,继续跟踪,进入:
发现有四个函数,具体函数的意义是什么,没有符号表不得而知。
这里介绍一个小技巧,我们现在处于上帝模式,我们有axf文件,axf文件中有符号表,并且,这一段程序在操作系统中,所有的stm32都是一样的,我们现在反汇编axf文件,与bin文件互相参照,可以更好的分析启动过程。打开axf文件,找到位置:
选中的函数,也就是第二个函数,是有关操作系统线程的设置,选择进入:
发现又是一堆函数,我们依旧参考axf文件:
第六个函数跳转是任务的设置,选择进入:
在这里我们可以发现任务main函数的设置,进入函数0x802485C
还有两个函数,参考一下axf:
上面的是任务进入的设置,下面是main函数的逻辑,进入main函数:
按F5转变成伪代码:
这就是任务main函数的逻辑,接下来就可以进行下一步的分析了。
实际上,还有另外一个方法,直接搜索字符串main:
同样可以找到main函数的位置。但是由于任务名称可能修改,从启动代码 逐步分析是最可靠的方法。
没有符号表依然很难受,我在那篇文章中也介绍了恢复符号表的方法,有需要大家可以参考。