4、防回滚计数器机制:计数器工作原理、一次性可编程(OTP)存储、计数器耗尽与生命周期管理。

防回滚,说白了就是不让设备“吃回头草”。

你想想看,攻击者拿到了一个旧版本固件,里面可能藏着已知漏洞。他要是能刷回去,那之前辛辛苦苦修漏洞、打补丁,全白干了。防回滚计数器,就是专门堵这条路的。

我个人习惯把它比作“里程表”。车子只能往前开,里程数只能增加,不能减少。固件也一样,版本只能往上升,不能往下降。这个“里程表”,就是咱们今天要聊的防回滚计数器。

4.1 计数器工作原理:单向递增的“信任链”

它的逻辑其实很简单。每次固件升级,计数器就加一。新固件启动时,会先检查计数器的值。如果当前固件版本号小于计数器的值,那就说明这是个旧版本,直接拒绝启动。

我在项目中遇到过一种情况:有人想绕过这个检查,直接修改固件里的版本号。但计数器是硬件维护的,你改软件没用。硬件说“不”,软件就得乖乖听话。

核心逻辑:

  • 版本号:固件自身携带的版本标识。
  • 计数器值:硬件中存储的、只增不减的数值。
  • 比较规则:固件版本号 ≥ 计数器值 → 允许启动;否则 → 拒绝启动。

这里有个细节要注意:计数器值通常等于“已成功启动的最新固件版本号”。也就是说,新固件第一次启动成功后,才会把计数器更新到对应的值。这样做是为了防止刷入一个坏固件,导致计数器被“污染”,再也回不去了。

我的建议: 计数器更新时机要放在“新固件验证通过并稳定运行后”。别一刷进去就更新,万一新固件起不来,设备就变砖了。

下面这张图,把整个流程串起来了:

防回滚计数器工作流程 启动加载 读取计数器值 版本 ≥ 计数器? 拒绝启动 允许启动 更新计数器值 注意 更新时机要谨慎

4.2 一次性可编程(OTP)存储:硬件级的“只写一次”

计数器值存在哪?普通Flash?不行。Flash能擦写,攻击者可以把它改小。所以得用一次性可编程(OTP)存储器。

OTP,顾名思义,只能写一次。写进去之后,就再也改不回来了。这正好符合计数器“只增不减”的需求。

常见的OTP实现方式有两种:

类型 原理 优点 缺点
熔丝(Fuse) 通过大电流烧断熔丝,表示“1” 实现简单,速度快 占用面积大,精度低
反熔丝(Anti-Fuse) 通过高压击穿绝缘层,形成导电通道 面积小,可靠性高 工艺要求高

嗯,这里要注意。OTP虽然叫“一次性”,但很多芯片会提供多个OTP位。你可以一次烧一个位,或者一次烧一个字节。这样就能实现“多次递增”的效果。

我曾经踩过一个坑: 有个项目用了OTP,但没仔细看数据手册。我以为每个bit都能单独烧写,结果发现必须按word对齐。最后浪费了好几个bit,计数器空间直接少了一半。所以,用之前一定要把芯片的OTP编程模型搞清楚。

4.3 计数器耗尽与生命周期管理:当“里程表”爆表了

计数器总有耗尽的一天。比如你用了16位的OTP,最多只能升级65535次。对于消费类产品,这绰绰有余。但对于工业设备或服务器,可能几年就耗尽了。

计数器耗尽后,设备就再也无法升级了。这等于宣判了设备的“死刑”。所以,生命周期管理必须提前规划。

我个人习惯把生命周期分为三个阶段:

  1. 正常期:计数器充裕,正常升级。
  2. 预警期:计数器剩余不多(比如只剩10%),系统发出警告,提醒用户尽快更新。
  3. 耗尽期:计数器归零,设备锁定,只能执行最后的安全策略。

耗尽后的处理策略:

  • 安全锁定:设备停止所有功能,只保留基本通信能力,用于发送“退役”信号。
  • 强制退役:设备自动进入只读模式,数据可导出,但不能再运行新固件。
  • 物理更换:对于关键设备,可以设计可更换的OTP模块,直接换一个新的。

你可能会问:“能不能把计数器清零,重新开始?”

答案是:不能。OTP的特性决定了它无法被擦除。一旦耗尽,就是终点。所以,设计时一定要留足余量。

我的建议: 对于生命周期较长的产品,优先选择32位或64位的计数器。别为了省那点成本,把设备的路走窄了。

最后,总结一下防回滚计数器的几个关键点:

  • 单向递增:计数器只能加,不能减。
  • 硬件存储:用OTP,别用Flash。
  • 更新时机:新固件稳定运行后再更新计数器。
  • 生命周期:提前规划耗尽策略,别等爆表了再想办法。

嗯,这一节的内容就到这。计数器机制虽然简单,但它是固件安全的基石。搞懂了它,后面的安全启动、安全升级,才能立得住。