第四讲:比特流抓取实战
各位好,我是老周。今天咱们聊点硬核的——怎么把FPGA的比特流给“抓”出来。
你可能会问:比特流不就是个配置文件吗?有什么好抓的?
嗯,这么说吧。我当年第一次拆解某款工业控制板时,发现主控芯片被打磨了型号。但通过JTAG口,我硬是把它的配置流读了出来,反推出内部逻辑。说白了,比特流就是FPGA的“灵魂”,拿到它,你就能理解这个芯片到底在干什么。
4.1 JTAG读取FPGA配置
JTAG,全称是Joint Test Action Group。这玩意儿最初是用来做边界扫描测试的,但后来被玩出了花——调试、编程、读取配置,样样都行。
我个人习惯,第一步永远是先确认JTAG链是否正常。怎么确认?用下面这条命令:
# 检测JTAG链上的设备
jtag -list
# 输出示例:
# 1: Xilinx XC7A35T (IDCODE: 0x0362d093)
# 2: Unknown device (IDCODE: 0x00000000)
看到IDCODE了没?每个FPGA都有唯一的IDCODE。如果读到0x00000000或者全F,那基本可以断定——要么线接错了,要么芯片已经“死”了。
确认设备后,就可以读取配置了。以Xilinx 7系列为例:
# 读取整个配置空间
jtag -read -start 0x00000000 -length 0x1000000 -file config.bin
这里有个坑——有些FPGA会锁定读保护。我曾经拆过一块军用级的板子,JTAG口直接返回“Security Locked”。遇到这种情况,别硬来,后面我会讲怎么绕过。
4.2 通过SelectMAP接口抓取比特流
JTAG虽然方便,但速度慢。如果你需要抓取大容量的比特流(比如Kintex-7这种),JTAG能把你急死。
这时候就该SelectMAP上场了。SelectMAP是并行接口,8位或16位数据线,速度比JTAG快一个数量级。
SelectMAP的引脚定义如下:
| 引脚名 | 方向 | 功能 |
|---|---|---|
| D[7:0] | 双向 | 数据总线 |
| CS_B | 输入 | 片选信号(低有效) |
| RDWR_B | 输入 | 读写控制(1=读,0=写) |
| BUSY | 输出 | 忙标志 |
抓取流程其实不复杂:
- 拉低CS_B,选中设备
- 设置RDWR_B为高电平(读模式)
- 在时钟上升沿读取D[7:0]上的数据
- 如果BUSY拉高,说明FPGA还没准备好,等它释放
我当年用逻辑分析仪抓SelectMAP波形时,发现一个有趣的现象——有些FPGA在读取配置时,前几个字节会返回0xFF。这不是错误,而是FPGA内部还在初始化。你想想看,就像人刚睡醒,总得先眨眨眼吧。
4.3 分析原始比特流文件的头部与尾部标记
拿到原始比特流文件后,别急着分析。先看看它的“身份证”——头部和尾部标记。
以Xilinx的比特流为例,头部通常包含以下信息:
// 典型的Xilinx比特流头部
0xFFFFFFFF // 同步字(32个1)
0xAA995566 // 同步头(固定魔数)
0x20000000 // 类型标识(0x20表示配置数据)
0x30008001 // 长度字段
// ... 后续是配置数据
尾部标记则相对简单:
// 比特流尾部
0x20000000 // 类型标识
0x00000000 // 空数据填充
0x30008001 // 结束标记
// 最后通常有4个字节的CRC校验
这里有个关键点——同步头0xAA995566。这个魔数几乎在所有Xilinx比特流里都能找到。如果你在逆向别人的比特流时,先搜这个值,就能快速定位配置数据的起始位置。
我记得有一次,客户拿来一块砖头一样的FPGA板,说“读不出来”。我一看,JTAG能识别设备,但读出来的数据全是0。后来我手动抓SelectMAP波形,发现头部同步字被篡改了——0xFFFFFFFF变成了0xFFFFFFFE。这明显是有人故意加了反逆向措施。
- 头部同步字:0xFFFFFFFF(至少4字节)
- 魔数标记:0xAA995566(Xilinx)或0x0F0F0F0F(Altera)
- 尾部CRC:通常为4字节,算法为CRC-32
- 如果头部异常,先检查时钟域是否对齐
最后,我画了一张图,把整个流程串起来:
这张图把整个流程分成了四个阶段。注意中间那个菱形判断——如果设备被锁定,你就得走右侧的“绕过”分支。这不是常规操作,但有时候是唯一的路。
好了,这一讲的内容就到这里。记住:抓比特流不是目的,理解它背后的逻辑才是。下一讲我们会深入分析比特流的内部结构,看看那些0和1到底是怎么映射成LUT和触发器的。
公众号:蓝海资料掘金营,微信deep3321