4. 验证与确认概述
时钟,是数字系统的“心跳”。
每个寄存器、每个状态机,都依赖这个稳定的节拍来同步运作。但你想过没有——如果这个心跳突然乱跳一下,系统会怎样?
这就是时钟毛刺注入要干的事。说白了,就是在极短的时间内,给芯片的时钟信号里塞进一个“不该出现的脉冲”。这个脉冲可能让寄存器采到错误的数据,让状态机跳到不该去的状态,甚至让安全启动的校验逻辑直接“跳过”。
我最早接触这个技术,是在评估一款车规级 MCU 的安全启动方案时。当时客户说他们的芯片有“硬件安全模块保护”,结果我用一个简单的时钟毛刺发生器,在启动过程中打了三个毛刺……嗯,芯片直接跳过了签名校验,跑起了我自己的代码。从那以后,我再也不敢小看这个“小脉冲”了。
4.1 时钟毛刺原理
先看一张图,理解毛刺到底长什么样。
你看到上面那个红色波形了吗?在第三个时钟周期里,多了一个窄脉冲。这就是毛刺。
它的宽度通常只有正常时钟周期的 1/10 到 1/5。比如你的芯片跑 100 MHz,周期是 10 ns,毛刺宽度可能只有 1-3 ns。这个窄脉冲,刚好能让寄存器进入“亚稳态”——既不是 0,也不是 1,而是介于两者之间的不确定状态。
4.2 时钟抖动与毛刺
很多人会把时钟抖动和时钟毛刺搞混。我刚开始学的时候也犯过这个错。
时钟抖动,是时钟边沿在时间轴上的随机偏移。它是物理世界的噪声导致的——电源纹波、热噪声、衬底耦合。抖动是随机的,你没法精确控制它什么时候发生。
时钟毛刺,是人为注入的确定性脉冲。它的位置、宽度、幅度都是可控的。我们做故障注入,要的就是这种“可控的不确定性”。
| 特性 | 时钟抖动 | 时钟毛刺 |
|---|---|---|
| 来源 | 物理噪声(电源、热、衬底) | 人为注入(毛刺发生器) |
| 可控性 | 不可控,随机发生 | 完全可控(位置、宽度、幅度) |
| 时间精度 | 皮秒级随机偏移 | 纳秒级精确注入 |
| 对系统影响 | 时序裕量降低,偶尔出错 | 确定性故障,可绕过安全逻辑 |
| 安全设计应对 | 时序冗余、时钟树优化 | 毛刺检测电路、双沿采样 |
你想想看,如果芯片设计时只考虑了随机抖动,留了 20% 的时序裕量,那我的毛刺只要比这个裕量更窄,就能让寄存器采到错误数据。这就是攻击的本质——利用设计者留下的“安全余量”。
4.3 利用时钟毛刺绕过安全启动
安全启动,是嵌入式系统的第一道防线。它的流程大致是这样的:
- BootROM 加载:芯片上电后,BootROM 先运行。
- 校验第一级 Bootloader:用公钥验证签名。
- 加载并校验下一级:逐级验证,直到操作系统。
- 执行:所有校验通过,系统启动。
这个链条里,只要有一级校验被绕过,整个安全体系就崩了。时钟毛刺的目标,就是让“校验通过”这个判断出错。
具体怎么做?我拆解一下:
- 定位校验点:先找到签名校验函数在 BootROM 中的执行位置。这通常需要反汇编 BootROM,找到那个“比较”指令。
- 确定注入时机:在比较指令执行的那个时钟周期,注入毛刺。让比较结果从“不相等”变成“相等”。
- 扫描参数:毛刺宽度、注入位置、电压配合。这三个参数需要组合扫描。
- 验证效果:如果芯片加载了未签名的固件却没有报错,说明绕过成功。
为什么会这样?因为毛刺让寄存器在建立时间(setup time)窗口内采到了不稳定的数据。数据总线上本来应该是“0”,但毛刺导致寄存器采到了“1”。这就是亚稳态的威力。
4.4 攻击流程与关键参数
下面这张图,是我在实际项目中总结的攻击流程。你可以把它当成一个“操作手册”。
参数扫描是最耗时的环节。我一般会写一个自动化脚本,让毛刺发生器自动扫描三个参数:
// 伪代码:参数扫描逻辑
for (glitch_width = 1.0; glitch_width <= 5.0; glitch_width += 0.2) {
for (glitch_offset = 0; glitch_offset <= 100; glitch_offset += 1) {
for (vcore = 0.9; vcore <= 1.2; vcore += 0.05) {
set_core_voltage(vcore);
trigger_glitch(glitch_width, glitch_offset);
if (check_bypass_success()) {
log_success(glitch_width, glitch_offset, vcore);
break;
}
reset_target();
}
}
}
这个脚本跑一轮,可能需要几个小时。但一旦找到那组“黄金参数”,后面每次注入都能稳定绕过。
4.5 防御思路
讲完了攻击,咱们也得聊聊怎么防。毕竟你可能是做安全设计的工程师,不是黑客。
针对时钟毛刺注入,常见的防御手段有这些:
- 毛刺检测电路:监控时钟周期,如果检测到周期异常缩短,立即触发复位。但要注意——检测阈值要留够裕量,否则正常抖动也会误触发。
- 双沿采样:关键寄存器同时用上升沿和下降沿采样,然后比较结果。如果两次采样不一致,说明可能受到了毛刺干扰。
- 时序冗余:在关键路径上增加额外的时序裕量,让毛刺无法突破建立时间窗口。但代价是性能下降。
- 冗余校验:签名校验执行两次,如果两次结果不一致,拒绝启动。我见过一个芯片,校验三次,三取二通过——这招对毛刺很有效。
- 电压-频率联动监控:如果检测到电压下降,同时降低时钟频率,缩小毛刺的“攻击窗口”。
好了,这一章的内容就到这里。时钟毛刺注入,说难不难,说简单也不简单。关键就三点:理解时序边界、精确控制参数、耐心扫描。你如果手头有开发板,可以自己搭个毛刺发生器试试——相信我,亲手绕过一次安全启动,比看十遍文档都管用。