【说在前面的话】
作为一篇严肃的技术文章,我们首先要以最大的善意来看待读者打开这篇文章的动机——肯定是学习目的啦——并在此基础上精确定义“所要实现的功能、目的”、以及“所使用的环境”是怎样的:
环境:
我们要实现的功能:
限制要素:
【先从劫持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() 的调用。怎么办呢?