
做嵌入式安全评估这几年我最常被问的一个问题就是固件都加密了、调试口也关了、系统启动了安全校验软件层面还能怎么测我的回答通常是——你把手伸到电源线上试试。电压故障注入Voltage Fault Injection是硬件攻击里门槛最低、效果最直接的一种手段原理很朴素在芯片执行关键运算的瞬间人为制造一个供电毛刺让内部逻辑短暂出一下错从而绕过校验、篡改控制流、甚至泄露密钥。这篇文章基于我最近在现代RPi单板计算机上做的一组实验把整套从原理到实操、从参数扫描到结果判定的过程完整写下来适合正在做硬件安全评估、固件安全测试或者产品安全设计的工程师参考。你会发现思路一旦打通剩下的大部分工作就是耐心和细致。1. 电压故障注入它是怎么让芯片“算错数”的1.1 故障注入的核心三要素任何一次成功的故障注入都要先回答三个问题在什么时间点干扰干扰哪个物理节点期望产生什么现象时间点决定攻击是否落在目标指令执行的敏感窗口里。现代处理器流水线长、指令多如果毛刺打在了无关指令上系统最多就是复位一下毫无利用价值。物理节点决定被干扰的是哪条数据通路和安全边界例如是CPU核心电压、DDR供电、还是外设IO电压不同节点对最终产生的影响完全不同。现象则决定了这次故障是否可以被攻击者利用比如是函数返回错误值、跳过分支、还是直接崩掉。做RPi这种目标时时间窗口通常用示波器在关键事件比如串口打印、GPIO翻转上抓取节点选择一般先从电源轨开始因为供电节点是所有逻辑共享的“命门”。现象判定的常用手段是观察目标行为是否偏离正常序列比如正常流程要打印“verify ok”注入后却打印了“booting bootloader”说明比较逻辑被绕过了。这三个要素不是孤立的它们共同构成一套攻击假设后续所有参数扫描都是围绕这个假设展开的。1.2 电压毛刺为什么会导致逻辑出错数字电路在正常工作条件下需要在时钟边沿到来前完成数据准备对应芯片内部的建立时间与保持时间约束。如果供电电压出现瞬间跌落晶体管的驱动能力下降组合逻辑的传播延迟变大。一旦延迟超过了时钟周期所允许的裕量寄存器就会采样到中间态或错误数据也就是常说的时序违例timing violation。这个错误本身是瞬时的但可能被后续逻辑放大形成可观测的异常行为。这个错误从微观上看是随机的但又是可引导的。只要攻击者把毛刺精确地打在“签名校验结果送上总线”的那个时钟周期附近就有概率让“校验失败”位翻转成“校验通过”。本质上跟人在集中注意力算账时被故意推了一下手臂差不多——结果是多算一位还是少算一位虽然不确定但计算过程大概率会出错。RPi的BCM2711或BCM2712核心电压较低逻辑门本身就工作在很紧的电压余量下很小的电压跌落就可能触发时序违例这也是为什么现代高主频SoC反而对电压故障更敏感的原因。1.3 电压毛刺的三种注入形态下拉毛刺undervoltage glitch电压瞬间跌落到正常值以下最常用目标是把逻辑门暂时“饿死”让它错过正确计算窗口上冲毛刺overvoltage glitch电压瞬间抬升对某些带负压逻辑或者模拟电路更有效但对RPi这种纯数字CMOS为主的目标用得少长时掉电brownout拉低电压并保持几百微秒以上会让处理器复位或进入未知状态不一定能产生可定向利用的逻辑错误但适合干扰看门狗或电源管理状态机对RPi SBC来说最实用的是下拉毛刺因为它能直接影响数字逻辑而且实现起来最简单——一个MOSFET开关加上可调的负载电阻就能产生ns级跌落。实际测试中下拉毛刺的效果也最容易观察先让系统产生一个可预期的复位再逐步缩小毛刺寻找敏感区间这条路线走得通。2. 为什么选现代RPi SBC作为攻击研究对象2.1 电源架构攻击者的“机会地图”现代RPi的电源架构比早期型号复杂得多。以RPi 4BCM2711为例板载PMIC输出了多路电压轨3.3V用于IO和外围逻辑1.8V用于DDR和部分PHY还有1.0V左右的核心电压域。不同电压轨的敏感度不一样给核心供电的低压轨最敏感因为逻辑门电压余量小给外设接口的电压轨虽然不直接影响CPU核心逻辑但可能影响DMA访问或外设状态机也能做文章。RPi 5换了更新的电源方案引入了RP1接口芯片供电树更细意味着可干扰的边界更多。实际操作中我常在RPi主板背面找大容量的去耦电容把探针和注入头直接焊在电容两个焊盘上。这样做的原因是去耦电容本身正好位于供电网络的关键节点毛刺信号从这里注入能比较均匀地耦合到整个电源域同时避免在主板正面飞线时碰到散热片或接口。现代RPi多层PCB设计也让电源轨隐藏在内层通过电容焊盘接入是最省事、最可控的方法。2.2 安全体系演进与物理层突破口RPi从4代开始全面支持secure boot。树莓派4/5从BootROM开始就固化了一段代码校验第二阶段引导程序、签名固件再跳转到内核。每一步都卡签名检查。这些防护对纯软件攻击者是有效的但在电压故障注入面前关键校验指令只是一个物理上的时序窗口。这里值得展开一下安全启动再强也是跑在物理芯片上的一段逻辑。攻击者不需要理解所有密码算法只需要让“比较结果是否正确”这一步出错。安全启动构建的是软件层的护城河但对物理层的“硬件改判”基本没有免疫力。RPi BootROM本身是封闭的但第二阶段bootloader会从SD卡/eMMC读取如果在校验完成前通过毛刺跳过一条条件分支指令后续加载流程就会沿着非预期路径走下去。我们在实验中甚至观察到某些情况下校验函数本身返回了正确值但随后跳转指令被毛刺影响程序流仍然被劫持了。2.3 行业里SBC“暴雷”的现状与评估价值最近圈子里的“SBC暴雷”说法很形象。很多产品号称安全设备拆开一看就是标准RPi计算模块加一块定制载板系统里跑着没怎么改过的Linux发行版。这种产品做固件提取、安全审计时经常在硬件层被一步步剥开。电压故障注入之所以这几年被频繁讨论就是因为SBC这个层级的攻击面非常大而很少有产品真的做了有效防御。从安全研究角度讲RPi作为攻击研究对象有三大优势一是文档和原理图公开程度高不用逆向就能知道供电拓扑二是社区生态成熟各种硬件调试经验都能搜到遇到问题很容易找到参考三是成本低烧坏一两块板子不心疼对初学者特别友好。做安全评估选目标时RPi 4B、CM4、RPi 5这几款都值得准备其中CM4因为电源引脚排针化方便外接注入装置是我个人最推荐的教学和研究平台。3. 实操环境搭建从选型到飞线的完整过程3.1 故障注入硬件选型我当时用的是ChipWhisperer作为主控加上外置MOSFET开关电路。ChipWhisperer的好处是集成了触发输入、故障注入输出和UART通信省去很多接线和信号同步的麻烦。但自制方案也完全可以走通一个脉冲发生器、一个示波器、一个P-MOS管、几颗电阻电容就能搭出最小可用的注入链路。示波器至少需要100MHz带宽、1GS/s采样率才能精确看到ns级毛刺。电源准备一个可调直流电源用来给目标板和注入电路单独供电这样能控制电压基准也方便在注入时观察电流变化。MOSFET选型上推荐AO3401或AO3400这类小信号开关管开关速度快、导通电阻低栅极可以直接由注入器驱动。需要特别注意的是下拉毛刺的开关管接法一般用N-MOS做低端下拉源极接地漏极通过限流电阻接到目标节点——这个拓扑跟常规高端开关不一样接反了要么没效果要么直接把电源轨短路。3.2 触发、记录与控制软件侧准备串口触发在目标固件里设定特定打印字符串通过串口线的TX信号触发注入器。优点是精准缺点是要能改固件或至少能解析串口内容GPIO触发让目标程序在敏感计算开始前翻转某个GPIO注入器检测到这个边沿后再延迟一段时间注入。这是最常用的方式因为依赖关系简单而且RPi的GPIO引脚非常容易接出来我自己用的扫描脚本框架大致是这样import serial import time ser serial.Serial(/dev/ttyUSB0, 115200, timeout0.1) def inject(width, depth, offset): # 控制注入器硬件这一步取决于具体设备 pass for width in [200, 250, 300, 350, 400]: for offset in range(0, 10000, 200): inject(widthwidth, depth30, offsetoffset) time.sleep(0.05) resp ser.readline() if bBYPASS in resp: log_success(width, offset) elif bREBOOT in resp: log_fail(width, offset)实际扫描比这复杂需要记录每一次注入的完整参数和目标返回的状态还要处理目标卡死后的复位逻辑。成熟的扫描框架会把“注入”“等待”“判读”“复位”封装成一个状态机保证一次扫描能自动跑几万次而不需要人盯。数据记录建议统一存CSV或SQLite方便后面统计成功率、绘制热力图。3.3 七步搭建一套可复用的注入环境拆开RPi外壳找到主电源入口USB-C接口后P-FET后面的5V节点和目标电压轨的测试点/去耦电容焊两路飞线一路给示波器探头测量另一路接注入开关的输出。飞线越短越好过长会引入寄生电感毛刺波形会被严重变把MOSFET匹配到目标电压轨和地之间。用下拉毛刺时N-MOS的漏极接目标节点源极通过采样电阻接地栅极接注入器输出。开关导通瞬间目标节点被瞬间拉到低电位形成毛刺用示波器同时观察毛刺波形和触发信号确认跌落深度和宽度符合预期。这一步必须做不能凭感觉调参配置触发源并测试整条链路确认注入器能被GPIO边沿或串口数据稳定触发先用大毛刺比如宽度1us、深度50%确认系统能被稳定打复位证明注入链路有效再逐步减小毛刺宽度开始正式参数扫描。每次改动硬件连线后都要重新走一遍第4和第6步保证结果可重复3.4 安全注意事项用RPi做实验时建议在电流路径上串一个限流电阻我常用0.5到1欧姆或者用质量好一点的供电模块防止毛刺过大把SoC打死。我最初连续注入时烧掉过一块CM4不是SoC坏了而是电源电路里的钽电容被反复过压击穿后来每次扫描前都会先测一遍静态电流确保没有短路。另外实验环境要注意静电防护焊接飞线时断开电源插拔GPIO杜邦线时先放电。RPi的3.3V和5V引脚就在物理相邻位置错插在裸板上很容易造成损坏建议用带标识的排针区分。4. 参数扫描毛刺宽度、深度与相位怎么配合4.1 三个参数的本质glitch width毛刺宽度毛刺持续时间决定“饿”逻辑门多久。太短可能来不及突破时序裕量太长会把整个系统打成复位glitch depth跌落深度电压跌落到多低通常用相对正常工作电压的百分比表示。跌得越深越容易触发逻辑错误但超过阈值就直接掉电复位了trigger offset触发偏移从触发信号到毛刺注入的时间延迟用来把毛刺对准目标指令附近的时钟周期。这个参数最需要耐心扫因为它直接决定能不能影响特定比较操作三者之间不是独立的。宽度和深度共同决定注入的能量而offset决定这份能量砸在哪个时间点。很多新手只扫单参数发现没效果就放弃实际上故障注入实验的本质是在三维空间里找“敏感点”单参数盲扫大概率会漏掉。我通常先把深度固定在中档30%左右宽度粗扫定位可行区间再固定宽度和深度去精细扫描offset最后在两个最优参数附近做小范围三维网格细化。4.2 扫描策略先粗后细逐层缩小范围扫描的第一步是确定“可行区间”。固定深度在30%宽度从最小开始逐步加大每次注入后观察系统状态。如果出现复位说明毛刺能量打过头了宽度要减少如果毫无反应说明能量不够宽度要加大。这一步的目标不是达成攻击而是画出“没反应—异常—复位”的能量带。宽度可行区间通常很窄比如RPi 4的3.3V轨上300ns可能毫无反应350ns偶尔复位400ns稳定复位而250ns时有概率出现校验绕过——这个“绕过窗口”往往不在复位区间里而是在边界的灰色地带。确定宽度和深度的范围后再固定它们扫描offset。从0到触发点后几百微秒的范围先粗扫步长可以用10us甚至20us。找到现象窗口后再在窗口内以1us、100ns步长细化。RPi启动时安全校验阶段持续大约几十毫秒用示波器先测量GPIO拉高到校验完成之间的时间差能大幅缩小扫描范围。经验法则先用粗步长把整个敏感区间切成几十段找到1到3个有反应的点然后只在这些点附近做细扫比均匀细扫整个范围省时一个数量级。4.3 一次典型扫描记录我在一次针对RPi 4B 3.3V电源轨的扫描中记录了如下数据宽度(ns)深度(%)偏移(us)结果20030100无响应20030120复位20030135无响应20030140校验绕过30030140无响应40030140校验绕过25040140复位从表里能看出毛刺参数不是越大越好。宽度从200ns加到300ns同一offset反而从“绕过”变成“无响应”说明毛刺已经干扰到了更早阶段导致目标提前退出校验流程。这种非线性是正常现象参数扫描本质上是在“过冲”和“不足”之间找那个脆弱的平衡点。另外深度从30%加到40%时结果从“绕过”变成“复位”说明这个电源轨的敏感窗口对深度变化极其敏感后续精细扫描时深度步长要控制在1%左右。4.4 判断“注入成功”的三种标志目标复位或卡死说明毛刺影响到了关键逻辑但未必是可被利用的成功目标跳过了某一步校验输出了正常情况下不会出现的启动日志或解锁标志说明毛刺精准“改判”了分支条件目标输出了部分敏感数据比如内存被dump出来或密钥被打印出来说明跳过了某段访问控制只有第二种和第三种才是攻击成功。如果只出现复位说明方向是对的但需要调整参数让毛刺落在更精确的时间窗口。我在记录结果时会把“复位”“无响应”“绕过”分开归类并且对“绕过”的结果重复验证至少20次排除偶发因素。5. 实战案例绕过安全启动链中的签名校验5.1 构造一个可观测的测试目标我用一块RPi CM4做了一个自定义启动流程BootROM启动后第二阶段bootloader会对内核镜像做SHA256哈希和签名校验校验通过才跳转。为了观测方便在校验开始前将一个GPIO拉高校验完成后拉低。这个GPIO的翻转时间差就是我们要攻击的窗口窗口。整个测试流程运行在密闭的试验环境里固件由我们自己编写和签名完全不涉及第三方商业固件。这个设计的好处是攻击窗口可预测、可观测。实际安全评估中很多目标的校验在哪里执行并不透明但可以通过测量串口日志间隔、电流波形变化、甚至电磁辐射来反推敏感阶段。我们在RPi上还有一个技巧在bootloader里加一个调试用的GPIO翻转做一个“带标记”的测试版本扫描出参数后再回到正式固件上验证这样可以显著提高参数扫描效率。5.2 注入定位与参数微调通过GPIO触发注入器让注入器在检测到GPIO拉高边沿后延迟一定时间注入毛刺。先用示波器确认GPIO拉高到校验完成之间的时间差我实测CM4上大约12ms。然后在0到12ms之间扫描offset步长先1ms粗扫找到可能窗口后再用100us、10us逐步细分。最终成功的一组参数是宽度250ns深度30%偏移11.35ms。这个位置大约是校验函数即将返回最终比较结果的前几个时钟周期。有趣的是在偏移11.30ms做同样参数注入结果多半是复位11.35ms是“绕过”的高发区11.40ms之后又变成无响应。这说明比较结果的路径在时间轴上非常窄可能需要微调几十个时钟周期的量级。由于RPi的SoC主频在1.5GHz左右一个时钟周期大约0.67ns所以偏移精度实际上是通过多次尝试来弥补的而不是靠硬件精确定时。5.3 成功率、重复性与误报分析单次扫描成功率通常不高。我记录过一次扫描中尝试了2000多次注入成功绕过约40次成功率2%左右。但如果把参数固定后反复注入成功率能稳定在2%-5%之间。在实际攻击场景中2%的成功率完全够用因为一次绕过就足以拿到一个可执行任意代码的环境。这里要特别说明一下误报问题。有一种常见情况是毛刺导致校验值被破坏但校验函数错误地返回成功实际上是因为比较的双方都变了出现某种随机碰撞。这种偶然“成功”在分析时要小心最稳妥的办法是看最终行为而不只是看返回值。如果注入后目标执行了未授权代码或者打印了特定标志才算有效绕过。我在一次扫描里遇到过“假成功”UART输出了“verify pass”字符串但随后系统并没有正常启动最后发现是毛刺让UART驱动也出了错打印内容本身就是乱码。这类误报要靠最终行为判定来过滤。5.4 关于研究边界的说明做这类测试一定要明确边界目标设备必须是自有设备或获得了明确授权。我的所有实验都在实验室自建设备上完成不涉及任何第三方产品。了解电压故障注入一方面是为了做安全评估时能发现产品的物理层缺陷另一方面也是为了让产品设计者知道威胁是真实存在的。攻击者拥有物理接触能力时安全边界就不是软件逻辑能画出来的。6. 排查、防护与安全研究边界6.1 常见问题排查速查表现象可能原因解决方案注入后完全无反应毛刺没有达到目标节点检查MOSFET接线和示波器波形确认开关确实导通每次都是复位从不绕过毛刺能量过大或offset不对减小宽度深度细化offset扫描只有偶发不同现象目标在执行不同代码路径调整触发点位置缩小注入窗口电源轨被拉死毛刺电路接入方式不对串接限流电阻避免开关持续导通扫描几百次都没有“绕过”目标没有在校验期间被影响先标记目标校验窗口确认攻击时间对准成功率忽高忽低环境温度或电源基准漂移校准电源避免长时间连续扫描导致板温升高排查时最重要的一步是“分层验证”。先确认示波器能看到毛刺再确认毛刺确实引起了系统复位最后才追求“绕过”。这三层之间任何一层不通都说明前面的环节有问题。直接跨层跳到“绕过”目标很容易让人陷入盲扫浪费时间。6.2 从攻击反推防御三点实用思路电压监测在关键电压轨上加比较器检测到毛刺立即复位。但要注意如果毛刺比检测响应时间还短就需要用模拟域的快速比较电路通常响应时间要到ns级才有意义冗余校验对安全关键的分支做多次比较两次结果一致才继续。这个方案成本极低对提高攻击难度效果非常明显。我们测试中发现单次比较被绕过后如果再加一次随机延迟后的重复校验攻击成功率会下降一个数量级以上时序屏蔽在校验函数执行期间屏蔽外部中断或故障响应减少可利用窗口。因为故障注入需要精确的触发同步屏蔽外部输入事件能破坏攻击者对时机窗口的把握对于产品开发者来说更应该避免直接用裸板RPi做安全产品核心至少要加一层壳体破坏检测、胶封或者环氧树脂灌封让物理接触的成本大幅提高。安全设计的目标不是让攻击不可能而是让攻击成本大于目标价值。6.3 边界意识与研究者的基本功电压故障注入研究是硬件安全领域的基础技能也是评估设备物理安全性的必要环节。它既可以被用来发现产品固件防护的薄弱点也可以帮助我们理解计算芯片物理层面的信任边界。RPi这类SBC因为文档公开、社区活跃是学习这项技能最理想的平台比直接用昂贵的安全芯片或定制板卡友好得多。研究过程中还有一个容易被忽视的收获你会被迫去理解目标系统的启动流程、供电架构、密码学实现细节。哪怕最终没有成功绕过任何一个校验整套测试做下来你对SoC内部时序和系统信任链的理解也会比光读手册深入得多。这也是为什么我强烈建议做固件安全的同行一定要找个时间亲手搭一次电压故障注入实验。我个人从这些实验里最大的体会是一个系统真正的安全边界往往比文档上画的更靠外。你觉得固件加密了、安全启动开了、UART禁了就已经很安全但忽视了供电网络攻击者就有机会在电源线上做文章。RPi这类SBC性能强、生态好、文档公开既是绝佳的学习平台也是最容易被忽视的硬件暴露面。做安全评估时记得多问自己一句如果有人能物理接触这台设备它还能守住自己的秘密吗把这个问题想清楚至少能帮你挡住一半真实世界里的攻击者。