【说在前面的话】
在《【嵌入式秘术】手把手教你如何劫持RTOS(上)》中,我们做了简单的热身——介绍了一种在你拥有某一个库的源代码或者.lib文件时,如何劫持“针对库中已有函数调用”的方法,并以SysTick_Handler为例,手把手的介绍了具体步骤。虽然是付费阅读,但还没有看过的小伙伴也不用担心,本文的内容并不依赖前文所介绍的知识。事实上,前文介绍的方法存在以下的局限性:
- 依赖Arm Compiler 5/6中linker的特殊语法,使用其它编译器(比如 gcc、iar或者llvm)的小伙伴就没有办法了;
- 由于手中已经拥有目标库的源代码(或者没有源代码,却拥有.lib文件本身),实际上这种劫持操作应用场景有限——换句话说,如果我能合法的获得源代码或者编译后的库,真的要做什么修改,为什么不联系库的提供方呢?自己去劫持,出了代码质量问题,又能找谁提供技术支持呢?
更进一步的:
- 如果库是事先烧录在芯片的ROM(或者我们没有权限读写的FLASH)里,又该怎么办呢?
既然库是以不可改写(甚至是不可直接读取)的方式提供出来的,谁会想去劫持并改变它的内容呢?
“难道你是要克隆别人的产品”?
大侠饶命,且听我说完:这堂课本质上是一期“黑魔法防御课”,通过展示黑客常用的克隆技法来做到知己知彼,提高产品的安全度。让我们首先站在普通产品开发者的视角来看一个常见的反克隆思路。
【常见的反产品克隆的方法】
早在十几年前,国内对嵌入式产品的“破解”已经非常普遍了——那时候不光有“号称第十八代安全技术的STC”,也有“AVR破解立等可取”的“常识”。时光荏苒、破解如歌,到了今天,依然有号称“第八十代安全技术的STC”和“S某T破解立等可取”的“常识”。唯一不变的是破解的目的——产品克隆,或者叫抄袭。
产品克隆可以节省大量的研发成本,并以价格战的形式迅速占领别人辛辛苦苦开拓出来的市场,考虑到国内客户的忠诚度往往很难培养,产品抄袭对很多小作坊来说几乎是“财富密码”。对一款具体的嵌入式产品来说,克隆往往意味着攻击者:
- 以抄板的形式逆向获取PCB版图
- 以包括但不限于物理暴力开盖等方式获取完整的FLASH镜像
- 获取处理器芯片型号和供货渠道
获得PCB版图和芯片供货渠道一般都不是问题,因此这里就不再赘述,而对所获取的FLASH镜像来说,攻击者一般有以下最基础的诉求:
- 替换产品的版权信息(比如bootloader或者控制台中的版权字符串);
- 用所获取的FLASH镜像进行大规模生产:
- 生产出来的产品和原产品一样稳定可靠
- 甚至在原有产品基础上能做小幅度改进
为了阻止攻击者不劳而获,产品的开发者不得不从源头上考虑对策:
- 首先,由于产品所使用的芯片往往是大厂公开出售的通用芯片,因此无法从控制货源的角度来阻止别人大规模生产;
- 其次,公开的通用芯片被破解出FLASH镜像几乎是无可避免的事情(只有时间早晚的问题);
既然FLASH镜像必然会流落到破解者的手中,阻止这一镜像文件用于批量生产就成为避免克隆的关键,为此:
- 开发者往往会考虑通过各种手段(比如特制的编程器),将每一个芯片的镜像都与芯片的UID绑定——换句话说,A芯片的镜像被破解者下载到B芯片后就会立即发现自己被克隆了;
为了应对这种UID绑定的做法,破解者往往会对镜像进行逆向。借助很多强大且成熟的软件工具,(一般从UID寄存器地址这一特征入手)很容易就能找到“实现与UID进行比较”的代码片段,之后,通过简单的“将比较跳转语句替换为NOP”的方式绕过检测,实现“精确爆破”。
由于RISC指令集本身就很简单,可以认为“无法有效阻止破解实施爆破”,因此,很多开发者采取了如下的手段加以应对:
- 在代码中引入对镜像自身的完整性检验,简单来说就是
- 在芯片的生产阶段,由编程器计算出整个镜像的校验码,比如CRC码,让后将其与镜像一起下载到芯片里;
- 在芯片的启动阶段,重新扫描整个镜像并计算校验码,如果这个校验码与事先保存的不同,就可以认为程序已经被篡改;
- 由于完整性检测本身也包含在镜像中,因此很难掩耳盗铃的认为攻击者无法通过逆向发现对校验码的比较操作,并进而实施NOP爆破。为了应对这种情况:
- 对镜像完整性的检测除了镜像自身会做一次以外,可以让第三方也去随机的做一些验证,比如让Bootloader在跳转到应用之前做一次验证。
- 第三方所使用的完整性检测算法最好与镜像自身所使用的不同,且第三方用于比较的参考校验值最好也保存在一个不同的地方。由于比较操作并不发生在镜像内部,因此也不容易被逆向分析所发现。
通过上述方法,我们似乎实现了以下的防护目标:
- 由于UID的存在,每个芯片的镜像都与UID绑定,因而不能用于大规模克隆产品的生产;
- 由于自身完整性校验和第三方完整性校验的存在,一旦攻击者爆破了UID的比较算法或是自身完整性校验算法,都可以立即被发现;
在有能力发现产品被克隆的情况下,很多聪明的小伙伴会选择“让对方付出代价”,其具体做法为:
- 检测到自身被破解后,在前几十次(甚至几百次)执行过程中,都顺利执行,以躲过出厂前的检测和销售品控;
- 在几百次使用以后,开始随机出现不稳定情况(不工作或者错误的工作,甚至丢出此产品是盗版之类的信息)——由于对方已经大规模出货,而故障发生是在一段时间以后,已经生产的产品就很难再销售出去,从而付出了血的代价。
至此我们就可以高枕无忧了么?很遗憾……上述做法5年前就很流行了……你们怕不是忘记了“道高一尺魔高一丈”的说法?
什么?完整性检测?不不不,我们不修改你的镜像,不破坏完整性,但这一点都不妨碍我们:
- 更改你的版权信息
- 爆破你的UID检测
- 批量生产
那这些破解者是怎么做到的呢?下面就为你详细展开。