第3章:比特流逆向技术入门

各位同学,今天咱们聊点硬核的——比特流逆向。说实话,这玩意儿在FPGA安全领域里,属于那种「看着简单,做起来全是坑」的技术。我入行那会儿,第一次拿到一个加密的比特流文件,愣是盯着十六进制看了三天,啥也没看出来。后来才明白,方向不对,努力白费。

3.1 比特流抓取方法

想逆向,先得有数据。比特流怎么抓?主流就两条路:JTAG和SelectMAP。我个人的习惯是,能用JTAG就用JTAG,毕竟接口通用,调试方便。但有些场景下,SelectMAP才是王道。

3.4.1 JTAG抓取

JTAG这玩意儿,说白了就是IEEE 1149.1标准定义的边界扫描接口。FPGA上电后,通过TCK、TMS、TDI、TDO这四根线,就能把配置数据读出来。嗯,这里要注意:不是所有FPGA都允许你读配置存储器。

我曾经在某个项目里,遇到一块Xilinx的Kintex-7芯片。JTAG链路上明明能识别到器件,但读配置数据时总是返回全0。折腾了两天,最后发现是芯片的Security位被置1了。说白了,厂家把读保护打开了,JTAG接口被锁死。

⚠️ 避坑指南:我曾经以为JTAG是万能的,直到被Security位教做人。抓取前,先确认芯片的安全状态。否则你忙活半天,全是白费。

JTAG抓取的基本流程是这样的:

  1. 连接JTAG调试器(比如Xilinx Platform Cable USB)
  2. 识别链路上的器件IDCODE
  3. 切换到配置数据读取模式
  4. 通过SHIFT-DR状态机逐位读出比特流

代码层面,用OpenOCD或者UrJTAG都能实现。我贴一段用UrJTAG抓取的小例子:

# 初始化JTAG链
jtag newtap xc7k325t tap -irlen 6
jtag tapenable xc7k325t.tap

# 切换到配置寄存器
irscan xc7k325t.tap 0x05
drscan xc7k325t.tap 32 0x00000000

# 读取配置数据
for {set i 0} {$i < 1000} {incr i} {
    drscan xc7k325t.tap 32 0x00000000
    puts [format "0x%08X" $drscan_result]
}

这段代码只是示意,实际项目中,比特流长度可能是几MB甚至几十MB。你想想看,逐位读出来再拼成文件,那效率...嗯,我建议用工具自动完成。

3.4.2 SelectMAP抓取

SelectMAP是另一种配置方式,说白了就是并行接口。它比JTAG快得多,但需要更多的引脚。我记得有一次,客户要求在不影响FPGA正常工作的前提下抓取比特流。JTAG被锁了,只能用SelectMAP。

SelectMAP抓取的核心思路是:利用FPGA的回读(Readback)功能。通过CCLK、D[0:7]、CS_B、RDWR_B这些信号,把配置数据从FPGA内部读出来。

💡 个人经验:SelectMAP抓取时,时序要求很严格。我曾经因为PCB走线长了2厘米,导致数据错位。后来加了等长布线,问题才解决。细节决定成败啊。

两种抓取方式的对比,我整理了个表格:

特性 JTAG SelectMAP
接口引脚数 4根 10-12根
最大速度 ~30 MHz ~100 MHz
安全性 易被Security位锁定 依赖配置模式设置
适用场景 调试、小规模抓取 批量生产、高速抓取

3.2 比特流解析工具介绍

数据抓到了,接下来就是解析。说实话,早期做逆向全靠手撕二进制,那叫一个痛苦。现在好了,有现成的工具可以用。我个人最常用的是Project X-Rayicestorm

3.2.1 Project X-Ray

Project X-Ray是Xilinx 7系列FPGA的逆向工程工具集。它把比特流里的配置数据,映射回FPGA内部的资源(LUT、BRAM、DSP等)。说白了,就是帮你把比特流「翻译」成网表。

这个工具的核心是数据库(Database)。Xilinx的比特流格式是不公开的,Project X-Ray通过大量实验,逆向出了配置位的含义。我参与过这个项目的一些早期工作,那会儿为了确定一个LUT的配置位,要反复测试几百次。

使用Project X-Ray解析比特流,基本命令如下:

# 解压比特流
xray_bitstream -f design.bit -o design.xdc

# 提取配置数据
xray_fasm -f design.xdc -o design.fasm

# 生成网表
xray_netlist -f design.fasm -o design.net

输出的.fasm文件,就是FPGA汇编格式。它描述了每个配置位的具体含义。比如:

SLICE_X0Y0.LUT_A.INIT = 0x8000
SLICE_X0Y0.FF_A.SRINIT = 0

这表示在坐标(0,0)的SLICE里,LUT_A的初始值被设为0x8000。嗯,看到这里你应该明白了,逆向的本质就是把这些配置位还原成逻辑功能。

3.2.2 icestorm

icestorm是Lattice iCE40系列FPGA的逆向工具链。相比Project X-Ray,它更轻量,也更易上手。我建议初学者先从icestorm开始练手。

icestorm的核心工具是iceunpackicebox。前者负责解包比特流,后者负责解析配置数据。

# 解包比特流
iceunpack design.bin design.txt

# 查看配置数据
icebox_vlog -p design.txt > design.v

输出的design.v,就是一个Verilog网表文件。虽然可读性一般,但已经能看出基本的逻辑结构了。

🔑 关键点:icestorm和Project X-Ray都依赖社区维护的数据库。数据库的完整度,直接决定了逆向的精度。我建议你定期更新工具版本,获取最新的配置位信息。

3.3 网表提取基础原理

网表提取,说白了就是把比特流里的配置位,还原成逻辑门和触发器的连接关系。这是逆向工程中最核心的一步。

为什么会这样?因为FPGA内部的结构是固定的——LUT、FF、BRAM、DSP这些资源,通过可编程互连网络连接。比特流的作用,就是配置这些资源的功能和连接关系。

网表提取的基本流程:

  1. 解析配置位:从比特流中提取每个配置位的值
  2. 映射资源:根据配置位,确定每个LUT的查找表内容、每个FF的初始值等
  3. 提取互连:分析配置位,确定资源之间的连接关系
  4. 生成网表:输出标准格式的网表文件(如EDIF、Verilog)

我举个例子。假设一个LUT6的配置位是0x8000,那它的真值表就是:

输入: A6 A5 A4 A3 A2 A1
输出: 只有A6=1且其他为0时,输出1
功能: 等价于一个6输入与门

你看,一个简单的配置位,就能还原出一个逻辑门。整个FPGA里,这样的LUT可能有几万个。把它们全部还原出来,再连起来,就是完整的网表。

⚠️ 注意:网表提取不是100%准确的。有些配置位是冗余的,有些是厂商保留的。我遇到过几次,提取出的网表里出现了「幽灵节点」——在物理上不存在,但网表里却有。这需要人工校验。

最后,我画了一张图,帮你梳理本章的知识体系:

比特流逆向技术知识体系 数据获取 JTAG抓取(四线串行) SelectMAP抓取(并行高速) 工具解析 Project X-Ray(Xilinx 7系列) icestorm(Lattice iCE40) 网表提取 解析配置位 映射资源 提取互连 生成网表

这张图把本章的三个核心环节串起来了。从数据获取,到工具解析,再到网表提取,每一步都有对应的技术和工具。你想想看,掌握了这些,你就能从比特流里「读」出FPGA的设计了。

好了,这一章的内容就到这里。比特流逆向是个实践性很强的领域,光看理论没用。我建议你找个开发板,自己动手抓一次比特流,再用工具解析看看。遇到问题很正常,我当年也是这么过来的。


公众号:蓝海资料掘金营,微信deep3321