PCIeHardwareMitM 精读:在 PCIe 链路上插一块 FPGA,读包、改包、复制包
把一块 FPGA 板插进 PCIe 链路中间、分别扮演链路两端的角色,它就能在不打断通信、不被两端察觉的前提下,把经过的每个包的类型、地址、长度记下来,还能在一个时钟周期里改写包的内容,或者把某个地址上的读请求替换成自己提前存好的副本——整套硬件逻辑占用不到 FPGA 资源的 1%,跑在 250 MHz、16 Gb/s 上。
论文:Toward a hardware man-in-the-middle attack on PCIe bus(Microprocessors and Microsystems 77 (2020) 103198)
作者:Mohamed Amine Khelif、Jordane Lorandel、Olivier Romain(CY Cergy Paris Université,ETIS 实验室)、Matthieu Regnery、Denis Baheux(法国宪兵刑事研究所 IRCGN)、Guillaume Barbu(IDEMIA)
在 PCIe 链路上做观测这件事,已有工作(比如 Hermes)已经证明能从明文流量里还原出整个模型,不过那些实验用的是商用协议分析仪(专门串在链路上抓包分析的仪器),回答的是能看见什么。这篇论文补的是另一半:如果要自己造一个能看见、能改写、还能复制的东西插进链路,它得满足什么条件,代价有多大。顺着这个问题的答案往下读,也能看清一件和后续所有 PCIe 侧信道有关的事——包头从来不加密。
📚 基本概念速读
| 名称 | 定义 | 记忆点 |
|---|---|---|
| PCIe | CPU 与 GPU、NVMe、网卡这类外设之间的高速串行总线 | 今天的显卡和 SSD 都挂在这条链路上 |
| Lane(通道) | PCIe 由若干条双向串行链路组成,每条 4 根线(两组差分线,一发一收) | x1、x4、x8、x16 说的就是 lane 的条数 |
| TLP(Transaction Layer Packet) | 事务层的包,真正承载读写请求与数据的那一类 | 有 header(包头,3 或 4 个 DW 长;1 DW 是个 32 位的字),可能带 payload(载荷,真正要传的数据) |
| DLLP(Data Link Layer Packet) | 数据链路层的包,负责流控、电源管理与确认 | 不承载用户数据 |
| IP core(IP 核) | FPGA 里可以拿来即用的现成硬件功能模块 | 论文的架构就是几块 IP 拼起来的 |
| MitM(Man-in-the-Middle,中间人) | 把自己插进两端通信之间,转发之余读写内容 | 对两端不可见是它的前提,不是附加要求 |
| Endpoint / Root Port | PCIe 的两个端点角色:设备侧与主机侧 | 做中间人要同时扮演这两个角色 |
| Shadow copy(影子拷贝) | 把数据另存一份,需要时用这份顶替真实数据 | 论文用它冻结密码尝试计数器 |
| Interposer(转接板) | 插在两个器件之间、把信号引出来的小板子 | 真机上做物理插入要靠它 |
| SignalTap / ILA | FPGA 内部抓信号的逻辑分析仪 | 论文用它验证中间人的时序 |
🎯 起点:为了一次能试一百万个密码
这篇论文的出发点不是攻击,而是取证。作者里有两位来自法国宪兵刑事研究所(IRCGN),他们要打交道的是加密手机。
手机的存储可以简化成两块芯片。一块是 SoC(System on Chip,片上系统),也就是那颗主控芯片,处理器、加密引擎、内存控制器都集成在里面;另一块是 NAND 闪存,用户数据、照片、聊天记录都写在它上面,可以把它理解成手机里的硬盘。两块芯片之间由一条 PCIe 链路连着——这正是这套方案要找的插入点。
iPhone 这类设备把两样东西看得很紧。一样是用户数据,由 SoC 里的加密引擎加密之后才落到 NAND 上;另一样是密码尝试计数器,通常存两份,一份在 SoC 内部,一份在一块独立的小存储芯片(EEPROM,断电也不丢数据的那类)里,两份对不上手机就自动锁死。按下几次错误密码之后,手机会把密钥材料一起抹掉,数据就再也取不出来了。
论文把正常情况和自己的方案摆在一起做了对比(§2.3):
| 尝试次数 | 正常情况的耗时 | 这套方案的耗时 |
|---|---|---|
| 4 次 | 320 ms | 320 ms |
| 5 次 | 1 分钟 | 400 ms |
| 6 次 | 6 分钟 | 480 ms |
| 8 次 | 36 分钟 | 640 ms |
| 9 次 | 1 小时 36 分 | 720 ms |
| 10 次 | 25 小时 36 分 | 800 ms |
| 11 次 | 数据已被抹掉 | 880 ms |
| 1,000,000 次 | 不可能 | 约 22 小时 5 分 |
扛住一百万次尝试,差不多等于把 6 位数字密码的整个空间跑一遍。做法并不复杂:把计数器冻住。中间人记住计数器的初值,之后主机每次来读,它就把初值重新递回去。主机那边看到计数器一直没变,也就不解锁、不抹数据。
这里有个关键点值得先记住:做这件事不需要破解任何加密。数据是不是密文无所谓,中间人只认地址——哪个地址被读了、读了多少次,这就足够把计数器找出来。
🧩 中间人能插在哪,为什么两端不容易发现
PCIe 是一条点对点的串行链路,两端直接相连,中间本来没有一条可供搭线的共享总线,这一点和早期的并行 PCI 不同。要插进去,就得物理上把链路断开,中间接一块板子,由这块板子分别扮演两端的角色。
1 | 原来的连接 |
板子要同时装成两个样子:对主机一侧,它就是那块 NAND;对 NAND 一侧,它就是主机。这样它才既能转发、又能顺手读写。
图里的 NAND 是手机上的存储芯片;换到 PC 上,这个位置一般是 NVMe 固态硬盘(NVMe 是跑在 PCIe 上的硬盘协议)。两端挂的是什么设备不影响做法,因为中间人利用的是链路本身。
论文给这套架构列了五条硬要求(§3.1):
- PCIe 3.0,传输率 8 GT/s(每秒 80 亿次信号跳变),单 lane 0.985 GB/s;
- 响应时间不超过 1 ms;
- 物理接口保持信号完整性(插进去的板子不能让信号波形变形),否则链路直接降速甚至断开;
- 对通信双方不可见,不能触发任何安全机制;
- 能访问经过的包。
第三条和第四条是真正卡人的地方。PCIe 是高速串行链路,板子插进去时自己引入的延迟必须小到让两端察觉不出来,论文反复讲的不可见就是指这件事。注意这里的不可见说的是不打断通信、不触发安全机制,而不是电学上查不到——作者后来在真机上干脆直接拿探头去量信号了。
🛠️ 中间人怎么搭:四块 IP 拼出来
论文没有直接在手机上动手,而是先做了一个 PC 规模的仿真器:用一台 PC 假装手机 SoC,用 FPGA 上的一个 IP(可以理解成一块在 FPGA 里现成可用的硬件功能模块)假装 NAND。这样验证成本低,调试也方便。
1 | ┌──────────────────────────────────────────┐ |
四块的分工是这样的:
- ST-Avalon:Intel 提供的 PCIe 接口 IP(底层基于 PIPE),负责和真实链路打交道。各种 DLLP 它自己就处理掉了,只把含有数据的 TLP 往上交;
- MitM IP:论文自己写的核心。它夹在 ST-Avalon 和 Xillybus 之间,所以经过的每一个包都得从它手里过;
- Xillybus:商业 IP,能把 FPGA 变成一个主机可以访问的外设。论文拿它来扮演那块要被读取的内存,它还自带驱动,省掉了自己写驱动的功夫;
- NIOS II:Stratix V 这块板子上没有现成的处理器,于是用 FPGA 的逻辑资源搭了一个软核(softcore)进去,用来把分析结果送到攻击者的电脑,以及在几种攻击模式之间切换(切换不用重新综合 FPGA,也就是不必把硬件设计重新编一遍)。
🔍 装上之后能做三件事
论文把能力拆成三个递进的场景,前一个的结果是后一个的输入。
① 分析过滤,先把敏感数据找出来
第一件事是定位。论文从头到尾强调同一个前提:包的内容可能是密文,但包头永远不加密。包头里有包大小、读地址、写地址这些元数据,于是中间人可以统计某个地址被访问了多少次、访问得多频繁,再把这些统计和正在发生的事件对上号,比如用户正在尝试解锁。密码计数器就是这么被找出来的。
1 | Host PC 发起一次 unlock |
② 在线改包,一个时钟周期就够
找到位置之后就能动手改。实测里,包从到达 MitM 到被改写后转发出去只花了一个时钟周期——芯片是按自己的节拍一跳一跳工作的,一跳就是一个时钟周期;它还会重新生成包的起止标记和有效信号,让改过的包在时序上看起来完全正常。
论文演示的是改 WMR(写内存请求)里的数据段,但同时说明能改的字段不止数据——包类型、写地址这些一样可以改。
③ 影子拷贝,把读请求截下来
第三个能力是为冻结计数器准备的。中间人把主机写往内存的数据全部复制一份存进自己的 shadow memory;之后主机每次来读那个地址,它就不再转发真实数据,而是把自己存的那一份递回去。
1 | 正常写:Host ── WMR ──→ Memory MitM 顺手存一份进 shadow memory |
结果就是主机以为计数器一直没变,实际上它早被改过很多次了。
(上面两张图里的缩写:WMR = 写内存请求,RMR = 读内存请求,Cpl = 内存把数据送回来的完成包。)
📊 实测:代价有多小
论文用 Xillybus 的 StreamWrite / StreamRead 驱动发随机数据(payload 从 1 DW 到 1024 DW),把三种模式的资源占用和时序测了一遍。测试平台是一块 Intel Stratix V DSP 开发板,带 PCIe 3.0 x8 的 endpoint 接口、1 GB DDR3、939k 寄存器与 622k 逻辑单元(ALUT——FPGA 里实现逻辑的基本单元,下面表里的占比都按它算)。
| 模式 | ALUT | 寄存器 | 频率 | 延迟 | 吞吐 |
|---|---|---|---|---|---|
| Logger(只记录与过滤) | 25(< 1%) | 137 | 250 MHz | 1 cycle | 16 Gb/s |
| MitM(可改包) | 56(< 1%) | 138 | 250 MHz | 1 cycle | 16 Gb/s |
| Shadow Copy | 187(< 1%) | 276 | 250 MHz | 9 cycles | 16 Gb/s |
几点值得留意:
- 资源占用都在 1% 以下。论文提到,省下来的空间可以拿去做更复杂的实时处理,比如完整的协议解码;
- 延迟小得惊人。改包只花 1 个时钟周期,影子拷贝只要 9 个周期,而这 9 个周期的来源很具体:要把 64 bit 的数据流用两个 FIFO(先进先出的数据缓冲队列)转成 32 bit 输出,好和 Xillybus 对接;
- 影子拷贝的测试零错误、不丢数据,一路做到 PCIe gen3 x2;
- 论文还测了不同 PCIe 配置下的资源占用(Gen1 x1 到 Gen3 x2),MitM 加上 ST-Avalon 一共用掉 601 到 821 个 ALUT,同样都在 1% 以下。
还有一条细节挺有意思:中间人的解码与记录比 Xillybus 自己还快 4 个时钟周期。也就是说,在真正的内存开始处理之前,它早就把数据存好了。
⚠️ 这篇论文的边界
读的时候要把已经做到的和打算做的分清楚,否则很容易高估它。
已经做到的:在一个 PC 加 FPGA 的仿真环境里,把 PCIe 3.0 上的中间人跑通了,三种场景都有实测数据,资源开销与延迟都量了出来。
还只是计划中的(§5 用 currently working on 开头):
- 真机上一个都没跑。作者的下一步是逆向智能手机主板,找到 SoC 与 NAND 之间最合适的插入位置;
- 真机上做插入要自制 interposer(转接板)。他们用有源差分探头(夹在信号线上把波形读出来的一种测量探头)测到了真实手机的时钟与数据信号,但论文说这些信号需要用更高频的探头来改进;
- 需要换成双口 PCIe 的 FPGA 板(Intel Arria 10,两个 gen3.0 x4 口,一个 endpoint、一个 root port),这样才能一侧接手机主板、一侧接内存。现在的板子只有一个 endpoint 口;
- 如果目标手机的存储走 NVMe,还要加一层 NVMe 解码 overlay(叠在现有处理之上、专门认 NVMe 包格式的一层逻辑),论文说这会抬高延迟;
- 对策研究没做。论文最后只说之后会研究。
还有几处口径要在引用时注意:
| 位置 | 要注意的地方 |
|---|---|
| 它是什么 | 这是一篇期刊论文(Microprocessors and Microsystems),不是安全四大那类会议论文;作者来自大学与法国宪兵取证机构,目标是验证取证工具的可行性 |
| PCIe 版本 | 只做到 3.0(8 GT/s)。今天 GPU、NVMe 常见的 PCIe 4.0 / 5.0 速率是它的 2 到 4 倍,这套设计能否直接沿用要重新评估 |
| 只到 x2 | 实测跑的是 gen3 x2 这个很窄的配置(论文说要贴近手机的接口),不是 x8 或 x16 |
| 不可见的含义 | 论文说的不可见指不打断通信、不触发安全机制,不是电学上无法察觉——作者自己就在真机上用探头量信号 |
| 加密无关的边界 | 论文明确的是包头不加密、可用来做访问模式统计;它并没有演示在密文通信里读出内容,只有定位 |
✅ 总结
把一块 FPGA 插进 PCIe 链路中间、扮演两端、转发之余读写经过的包,这件事在工程上是成立的:资源占用不到 1%,改包只花一个时钟周期,而且两端察觉不到。
三个可以带走的要点:
- 中间人的门槛在延迟与信号完整性,不在算力。论文反复强调不可见性,落实下来就是几项很具体的指标:8 GT/s、响应不超过 1 ms、每个包只许增加极少量的时钟周期;
- 包头不加密,这一条撑起了整个观测面。即使 payload 是密文,凭包头里的类型、地址、长度就能统计出访问模式,进而定位敏感数据;
- 它的价值在于把观测点做实。Hermes 那类工作证明的是链路上能拿到什么,这篇证明的是——用什么代价可以把观测点放进链路里,而且放进去不被发现。
从能看见什么,到能不能插进去还不被发现,这篇补的是后半句。