查看原文
其他

【嵌入式秘术】手把手教你如何劫持RTOS(上)

GorgonMeducer 傻孩子 裸机思维 2022-09-10
前言
无论出于何种"学习"目的,作为用户,有时候我们总难免需要在“运行时刻”对RTOS所使用的关键系统资源"检视一番",比如 SysTick,PendSV,SVCall之类中断发生的时候,操作系统的用户可以在RTOS之前“神不知鬼不觉”的先获得执行自己指定的小程序的机会——哪怕是写个“到此一游”也好——这种操作当然不能去修改RTOS的源代码;尤其是RTOS以库的形式提供时特别有意义。那么具体如何去实现呢?

【说在前面的话】


作为一篇严肃的技术文章,我们首先要以最大的善意来看待读者打开这篇文章的动机——肯定是学习目的啦——并在此基础上精确定义“所要实现的功能、目的”、以及“所使用的环境”是怎样的:

环境
  • Cortex-M 微控制器

  • 任意的RTOS——无论其是以源代码形式还是以库的形式参与工程编译

  • 裸机环境

  • 使用 Arm Compiler 作为编译环境

    • 其它编译环境需要阅读对应的用户手册找到类似的解决方案

  • RTOS 或者某些库 占用了关键的系统资源,比如 SysTick、PendSV以及SVCall

  • RTOS 可能将用户任务限制在非特权模式下运行,且受到MPU的监管而无法访问任务权限以外的资源


我们要实现的功能
  • 劫持SysTick、PendSV、SVCall 之类关键的被 RTOS 占用的中断处理,即:

    • 当这些中断被触发时,首先执行我们指定的代码

    • 劫持的过程不能影响RTOS已有的正常功能


  • 由于我们所劫持的系统资源具有非常高的系统权限,理论上我们可以:

    • 对RTOS 的行为进行额外的追踪和调试

    • 重新配置MPU


限制要素

    • 不能修改 RTOS的源代码、或者说对于RTOS以库提供的形式实际上我们也无法修改对应的中断处理程序



【先从劫持SysTick开始】


大部分 RTOS 会使用由 SysTick、PendSV 或是 SVCall 产生的异常(Exception)来完成系统的上下文切换,因此只要劫持了这些关键异常的中断处理程序就能轻松实现对 RTOS 的劫持。
这里,“劫持”(Hijack)虽然是一个听起来特别酷的信息安全词汇,但翻译成人话并不复杂,即:当对应的异常被触发时,在执行原先由RTOS 所提供的异常处理程序之前,首先执行由第三方(也就是我们)所“插入”的特定小程序;并在“事成之后”重新回到原本RTOS的处理程序中去执行,从而掩盖了我们所作的“小动作”


要理解这一过程以及具体的操作方法,我们不妨以劫持SysTick为例,对于PendSV以及SVCall来说,完全可以如法炮制,本文也不再赘述。为了将问题简化,我们不妨假设某一个已经编译成Library的算法库,它在内部提供了一个SysTick的异常处理程序:
//! 某一个算法库占用了SysTick,并实现了一个对应的异常处理程序__attribute__((used))       /* Linker不要开枪,自己人! */void SysTick_Handler(void){ /* 我们不知道、也不关心这个算法库在 SysTick 的异常处理程序里做了什么     * 我们不妨就将其看作一个黑盒子。     */     ...     printf("Running original Systick_Handler...\r\n");}

假设,作为用户,我们有一系列操作希望加入到SysTick_Handler中:

__attribute__((used)) /* Linker不要开枪,自己人!*/void user_code_insert_to_systick_handler(void){    /* 我们有一堆想在 SysTick_Handler 中做的事情 */    ...    printf("I am hijacking SysTick_Handler...\r\n");}

由于该函数已经固化在算法的库文件里了,我们实际上没法像修改源代码那样向SysTick_Handler中添加对函数 user_code_insert_to_systick_handler() 的调用。怎么办呢?

微信扫一扫付费阅读本文

可试读30%

微信扫一扫付费阅读本文

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存