ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

树莓派电压故障注入实战:从硬件搭接到参数扫描全流程解析

树莓派电压故障注入实战:从硬件搭接到参数扫描全流程解析 电压故障注入Voltage Fault Injection这类技术过去总被看成实验室里才玩得转的东西好像得先配齐一堆昂贵设备才敢碰。但在现代树莓派SBC上实际跑过一轮之后我的结论完全不同这套方法的门槛远没有想象中高树莓派本身就是一个相当理想的研究靶子——它便宜、原理图公开、引导链路里有明确的安全校验点而且就算搞挂了一张TF卡就能重新来过。这篇文章会围绕“把电压毛刺打进现代树莓派SoC”的完整流程展开从为什么选树莓派、硬件怎么搭、电源轨怎么接到时序定位、参数扫描和结果判定一步步说清楚其中的逻辑和坑。适合对硬件安全评估、嵌入式可信启动感兴趣以及已经玩过ChipWhisperer想换更复杂目标练手的朋友。1. 为什么拿树莓派当靶子引导链路与故障注入的契合点1.1 树莓派的安全引导到底保护了什么树莓派并非从一开始就带安全引导。早期的树莓派2、3B之前的机型从SD卡读取的第二阶段引导程序几乎不做签名校验用户想改引导流程非常自由。但从树莓派3B/3B这一代开始Broadcom在BootROM里加入了由OTP熔丝状态控制的引导逻辑一旦设置了安全位BootROM只加载通过RSA签名验证的第二阶段引导程序后续再由二级引导程序逐级校验U-Boot和内核镜像。到树莓派4和5上这套机制进一步加强密钥管理和证书链都完整了很多。从评估者角度看到的“切入点”也随之清晰二级引导程序验证U-Boot签名那一段代码是整个信任链的咽喉。如果能在CPU执行签名比较或条件跳转的瞬间让比较结果或跳转条件被翻转就有机会跳过校验让一个签名并不合法的镜像继续往下走。这正是电压故障注入最经典的“跳过安全检查”用法也是产业界和学术界在SBC安全评估里反复走的一条路。1.2 为什么电压跌落能让CPU“算错”电压故障注入能生效本质上是因为数字芯片的时序余量是按标称电压留好的。当VDD_CORE电压在几十纳秒内跌落几百毫伏电路内部逻辑门的传播延迟会显著增加。如果跌落窗口刚好覆盖某个关键寄存器的采样沿或者覆盖某条关键组合逻辑路径CPU就可能把“1”读成“0”把“等于”判成“不等于”。这个错误完全是物理层面的不依赖任何软件漏洞所以单纯更新固件无法修复——除非增加硬件检测或者重新设计。有个容易被忽略的点毛刺深度不是越深越好。太浅的毛刺影响不到逻辑太深的毛刺会让芯片直接进入欠压复位硬件锁定在复位状态毛刺结束后虽然能重新运行但你的目标指令早就过去了。有效故障窗口通常很窄而这个窗口正是参数扫描要寻找的。1.3 树莓派在故障注入中的“甜点”我接触过不少SBC树莓派3B和4B在故障注入研究里的口碑相当好。原因很直接官方公开原理图和PCB文件电源轨位置容易反查SoC供电集中在少数几个电源轨不用做太精细的电源域分离引导链路时序相对固定方便用示波器抓取和做触发对齐一张普通TF卡就能刷新固件搞坏了也不心疼。 树莓派5作为更新平台引入了更复杂的电源管理和电源轨划分瞬时响应明显变快不太适合作为第一步练手目标。建议先把3B或4B的整个流程走顺再挑战更高难度的平台。2. 硬件搭建把毛刺送进SoC供电轨的连接方案2.1 故障注入器的三条路线故障注入器的本质是一个能按外部触发信号、在精确时间点把目标电源轨拉低的可控开关。目前研究者常用的有三条路径ChipWhisperer系列比如CW-Pro、CW-Husky自带可编程毛刺生成器、ADC和触发输入接口配套Python库把毛刺参数设置得清清楚楚。对于刚接触故障注入的人这套设备可以省掉大量重复造轮子的时间缺点是价格不便宜。自制MOSFET开关用一片高速MOSFET接在目标电源轨和地之间栅极由外部脉冲源驱动。优点是便宜、灵活缺点是对驱动信号要求很高MOS管的开关速度、振铃都需要反复调试新手容易把目标板搞出各种莫名其妙的复位。任意波形发生器加功放一些实验室会用高带宽信号发生器输出负脉冲再叠加到电源轨控制精度高但设备成本不低阻抗匹配处理也麻烦。我在树莓派上做评估时最终固定用的是ChipWhisperer加一块自制电平转换小板的组合。理由很简单CW库已经把毛刺参数的重复性和触发对齐处理好了我可以把精力集中在树莓派引导时序的定位上不用每次失败都怀疑是脉冲没出来。2.2 定位电源轨用原理图说话对树莓派3B/4B这类板子直接看正面找不到“VDD_CORE”标识得靠原理图反查。树莓派基金会官方发布了每一代主板的原理图PDF打开后查SoC的电源节点名例如BCM2837B0的VDD_CORE、BCM2711的VDD_CORE等。找到后在PCB上对应的滤波电容焊盘或测试点做引出。实际操作中我习惯找离SoC尽量近的陶瓷电容电容一脚是电源轨另一脚是地。用万用表蜂鸣档确认好极性再用极细的漆包线或飞线焊上去。如果目标电容太小先在电容两端堆一点锡用镊子把漆包线压住浸锡焊接。焊好后点少量热熔胶或UV胶固定否则后续反复插拔、接示波器时很容易把焊盘拽掉。电源轨选择上很多人第一反应是干扰5V入口但其实更常见也更有效的做法是对SoC核心电压轨VDD_CORE做故障注入。这个轨直接决定CPU逻辑的时序余量复现性较好。3.3V外设轨和1.8V内存轨也可以测但一般只用来制造特定外设故障不是解锁引导链路的主要路径。2.3 触发信号与示波器连接故障注入没有触发就相当于盲射。这里需要给故障注入器一个“开始”信号从目标板上取触发最稳。树莓派的GPIO引脚可以在U-Boot阶段驱动某个引脚翻转或者直接观测二级引导程序访问SD卡时CS引脚的下降沿。我试过的两种触发源GPIO软件触发在U-Boot里通过GPIO寄存器把某引脚拉高然后在目标指令窗口前几百纳秒触发注入。精确度取决于软件执行时间的稳定性树莓派启动过程中时钟相对固定实测抖动可以控制在几微秒内。SD卡CS引脚触发更贴近硬件层不依赖修改引导代码。把示波器或注入器接在SD卡座的CS引脚上每次读取阶段性数据时都会给出低电平脉冲用这个脉冲做触发相当于把所有启动流程锁到了一个稳定参考点上。示波器建议至少200MHz带宽、1GSa/s采样率。连接探头时尽量直接夹在目标电容两端不要通过长飞线否则毛刺信号会被探头线缆的寄生参数磨圆看到的是失真后的跌落波形。2.4 上电保护与防损坏措施每次注入都有机会把树莓派“打死”最常见的现象是电压跌落太深后PMIC进入闩锁保护表现为上电无输出必须断电重插USB才能恢复。为避免反复烧板我建议在5V入口串一个可恢复保险丝电流限制在500mA到1A之间。毛刺深度一开始不要超过0.8V脉宽不要超过1µs先把系统跑稳定再逐步加压。这个习惯形成之后已经帮我省下好几块PMIC。3. 时序定位怎么找到该打断的那条指令3.1 目标是“窗口”不是“时刻”故障注入有效的前提是毛刺到达电源轨的时刻必须恰好落在芯片执行目标指令前后几十纳秒内。这个窗口远小于一次函数调用的执行时间更不用说一次完整引导了。所以需要外部参考信号把毛刺触发时刻换算成“从某个已知事件开始偏置的延迟”。难点在于不能简单靠猜。最有效的做法是先通过UART串口输出观察二级引导程序的执行痕迹再用示波器把每个引导日志打印点和起始事件之间的时间差量出来。比如树莓派在串口上输出一行启动日志这一行的上升沿就是一个良好参考点然后可以推算校验函数大概在日志之后的几百微秒执行。把时间轴记录下来再结合反编译的引导程序代码就能估算目标校验代码在时间轴上的大致位置。3.2 用示波器建立引导时间轴第一次实测时我先把示波器触发设为GPIO触发的上升沿用单次模式抓取从触发点到串口TX引脚输出有效数据的完整波形。这样能测出树莓派从触发到串口开始打印的大致时间以及后续各行日志之间的间隔。记录下时间轴后配合引导代码的反汇编结果签名校验的位置就不会太模糊。在树莓派3B上BootROM把二级引导程序从SD卡加载到SRAM二级引导程序先打印初始化信息然后读取内核镜像校验签名。整个过程大约几十毫秒签名校验部分可能只占几毫秒。你需要进一步把“几毫秒”缩小到“几十微秒”。缩小方法很简单把触发延迟设成从某个日志打印点后N微秒N在合理范围内步进比如每次步进10µs重复注入。如果某个N值开始出现系统异常说明已经碰到敏感区再从异常点附近用1µs甚至更细的步长精扫。3.3 抖动来源与压制方法时序抖动是这套系统里最磨人的问题。树莓派启动链路里SD卡读取时间会受TF卡坏块、缓存策略波动影响串口初始化也会占用不稳定周期所以即使在“同一事件”触发目标指令实际执行时刻相对触发点仍有几十微秒的抖动。压制抖动我总结出三个实用手段固定TF卡使用同一张写好的卡格式化后不要再写入数据避免FAT表访问时间变化。用硬件触发代替软件触发如果板载某个信号比如SD卡CS是时序抖动最小的参考源优先用这个。多次重复扫描同一个参数组合至少重复50次统计成功概率而不是寄希望于单次命中。电压故障注入本身就是概率性攻击用统计眼光看结果会让你少走很多弯路。4. 参数扫描电压、脉宽、延迟的三维空间4.1 三个维度的初始范围电压故障注入的效果由三个主要参数决定注入延迟触发点到毛刺起点的偏置、毛刺宽度、毛刺深度电压跌落幅度。另一个不可忽略但较少被主动调整的参数是毛刺的下降沿和上升沿斜率但在实验阶段可以忽略。基于常见经验建议初始范围参数初始范围说明注入延迟参考事件后0~2000µs步长10µs粗扫找到敏感区后改1µs或更小毛刺宽度100ns~1µs太窄打不进逻辑太宽容易把系统压复位毛刺深度0.3V~0.9V视目标电源轨标称而定不要超过标称的50%我一般把延迟放在外层循环因为它的敏感窗口最大宽度和深度放内层因为这两者决定毛刺是否“够劲”。4.2 自动化扫描脚本的设计手工在ChipWhisperer的GUI里点参数不仅累而且容易看漏。我的做法是写一个Python脚本把参数组合列表化每次循环配置毛刺参数、触发目标板复位、启动目标外部电源记录UART日志结果。伪代码如下import chipwhisperer as cw scope cw.scope() target cw.target(scope, cw.targets.SimpleSerial) # 配置毛刺参数范围 durations_ns [100, 250, 500, 750, 1000] extents_mv [300, 500, 700, 900] with open(results.csv, w) as log: log.write(delay_us,duration_ns,extent_mv,boot_log\n) for delay_us in range(0, 2000, 10): for duration_ns in durations_ns: for extent_mv in extents_mv: # 设置毛刺参数 scope.glitch.delay delay_us scope.glitch.width duration_ns # 根据具体毛刺生成方式设置深度 # ... # 触发目标复位并采集UART输出 # target.reset() # boot_log target.read() # 保存原始日志离线分类 # log.write(f{delay_us},{duration_ns},{extent_mv},{boot_log}\n)关键点在于脚本不要介入每一轮的参数解释而要把原始UART日志全部保存下来后续离线分类。因为单次“看起来没变化”的故障可能在日志里隐藏着有用的部分失败离线分析比即时判断更可靠。4.3 从“毛刺无反应”到“系统崩溃”到“检测跳过”每一次注入后系统可能有三种结果无反应引导输出和正常启动完全一致说明毛刺没有落在有效窗口或者强度不够。崩溃或复位UART输出中断或出现乱码说明芯片内部状态已被打乱可能影响了无关代码也可能属于后文要分类的异常故障。受控异常UART输出出现“签名不合法却继续引导”之类的现象这是你想要的故障模型。三种结果的比例会随参数变化。理想参数区往往是“崩溃率不高但偶尔出现受控异常”的区域。如果某个参数组合下崩溃率100%说明毛刺太强可以降低深度或缩短脉宽再试。4.4 敏感区精扫的实战节奏粗扫一般只需要几百次注入就能圈出大概敏感区。此时不要急着高兴还要做一轮精扫把延迟范围缩小到粗扫发现的异常区两侧各50µs步长改成1µs宽度和深度在异常区附近各取3个候选值排列组合。这样一轮下来最多几百次实验很可能便找到成功率最高的参数组合。我实测下来树莓派3B上找到的成功区经常只有几十个微秒宽深度在0.5V到0.7V之间脉宽在300ns到600ns之间。不同板子的电源特性有差异关键还是要以扫描为准。5. 结果判定把“偶发现象”变成可复现的故障5.1 判定标准不是“能启动”就算成功很多人第一次跑出目标板正常启动时会怀疑注入是否有用。关键不是看它是否启动而是看它是否带着异常镜像“非法启动”。我的做法是准备一张包含经过篡改但结构合法的镜像的TF卡然后看U-Boot是否接受它。如果系统用篡改镜像继续运行了说明校验确实被跳过。另一种间接判定是观察串口输出的哈希值或签名日志如果引导程序打印“bad signature”后仍然继续加载说明比较逻辑被翻转如果打印“bad signature”然后停机说明故障没有生效或作用到了其他路径。这里要特别提醒别把“系统偶尔跑飞”误判成“校验绕过”。跑飞一般伴随UART乱码、异常寄存器内容而校验绕过的特征是流程突然跳过某一步日志内容整体仍然可读。只有看到连续多次可控的“跳过校验日志”才值得庆祝。5.2 故障模型分类与稳定性分析在持续扫描中我很少只关心“跳过”这一结果而是记录每次故障的类型。常见的几类指令破坏CPU执行了一条被翻转编码的指令可能触发未定义指令异常或跳到错误地址分支误判条件分支被翻转程序流程走向错误路径数据比较错误比较结果错误但指令流本身未乱总线事务异常读取到的内存内容被破坏但不一定是寄存器出错。判断故障类型的方法是在UART日志中加上引导过程的阶段标记观察故障发生在哪个阶段。如果发生在签名验证阶段且日志显示“continue after verify”大概率是分支误判或比较错误如果发生在更早的初始化阶段说明毛刺打到了无关指令。为了提高复现性我会把“比较错误”对应参数区的结果用100次注入重复试验统计成功率。通常最优参数区的成功率在百分之几到百分之二三十之间这已经足够后续利用因为实际攻击可以反复重启目标设备直到成功。5.3 从故障到利用需要警惕的边界当稳定拿下一个校验绕过点后就可以进一步做更细的毛刺参数微调争取把故障位置精确到“固定某条比较指令”。这一步的价值在于得到的不是“看运气才成功”的结论而是一个可复现的攻击原语。在这个原语基础上可以实现更复杂的效果比如跳过验签后直接引导自定义内核、修改安全世界和普通世界之间的切换逻辑、在引导阶段插入持久化调试后门等。这些都是硬件安全评估和漏洞挖掘中的典型工作流。走到这一步时请务必只在自己的设备上操作评估结束后恢复好原始引导配置。故障注入是一种物理攻击手段论文和公开研究里讨论它目的是帮助芯片和系统设计者意识到风险而不是鼓励破坏他人设备。这个边界一定要守住。6. 防故障注入的加固思路攻击之后聊防御6.1 硬件层面的对策既然树莓派可以被电压故障注入影响防御思路同样分硬件和软件两层。硬件上最直接的手段是让核心电压轨更难被外部拉偏加大去耦电容和磁珠隔离或者在PMIC输出端增加高速电压比较器检测到异常跌落立即复位SoC。很多工业级安全芯片会内置电源毛刺探测器当检测到毫秒级以下的异常跌落时触发安全事件阻止敏感操作继续执行。对树莓派这类通用SBC来说PCB上没有专门的反毛刺电路所以它在面对物理攻击时并不特别坚固。如果你是拿它做产品原型但后续要过安全认证那应该在最终方案中增加一个独立的电压监控芯片并让监控输出和SoC的复位脚直连。这样即便攻击者尝试拉低电源轨监控电路也会抢在故障生效前把SoC安全复位掉。6.2 固件加固从软件上抹平物理漏洞软件层对策的作用不是防止毛刺产生而是让毛刺即使在物理层面生效也无法带来安全后果。常见做法包括双重校验签名验证不只做一次而是在不同阶段重复验证任何一次失败都不允许继续引导故障检测变量在安全关键函数前后写入两个互相校验的随机变量执行完函数后检查变量是否被意外改写冗余比较将关键比较结果保存在多个寄存器并逐一比较引入随机延迟在签名验证代码中插入随机长度的等待循环迫使攻击者无法精确锁定毛刺时隙使能ARM的调试保护和异常处理把故障导致的异常直接带入安全处置流程。6.3 树莓派官方措施的演进树莓派并非完全没有防御。从3B引入安全引导和OTP密钥开始后续型号在引导验证、密钥管理和安全调试方面逐步加强。树莓派4和5的BootROM对电源异常更敏感PMIC的瞬态响应也更快这在一定程度上压缩了有效故障窗口但并没有从原理上根除电压故障注入。这也提醒我们任何依赖单点校验的信任链面对物理层故障注入时都值得重新审视。6.4 工程实践中的最后建议我个人的体会是电压故障注入并不神秘它最讲究的不是昂贵的设备而是对目标板时序的耐心和对参数空间的系统化理解。如果你准备开始尝试先把树莓派的供电时序图画清楚把触发信号稳定好再谈“一击命中”。每次实验后记录参数组合与日志时间久了会形成一套属于自己的扫描策略那比任何厂商宣传的设备参数都管用。最后再分享一个小技巧在扫描脚本里加一条“连续N次注入无结果就自动扩大脉宽”的逻辑能让整个扫描过程更快收敛。刚开始我手动调参经常在同一个无效区间浪费时间加入这个自动调节后一轮扫描从两小时缩短到了半小时以内。故障注入的成败很多时候就藏在这些不起眼的工程细节里。
返回列表