第二章:FPGA基础与网表结构

各位同学,今天我们来聊聊FPGA逆向工程中最基础、也最关键的一环——理解FPGA内部到底长什么样,以及网表文件是怎么描述这些结构的。

说实话,我刚入行那会儿,觉得逆向就是拿着二进制文件硬啃。后来发现,不懂FPGA内部架构,你连网表里那些LUT、FF的编号都看不懂。嗯,这章我们就把它彻底讲透。

2.1 FPGA内部架构:你手里的积木盒

FPGA说白了就是一个巨大的积木盒。里面摆满了各种可配置的逻辑单元。我个人习惯把FPGA内部想象成一座城市:

  • LUT(查找表)——城市里的“小商店”,能实现任意逻辑功能
  • FF(触发器)——城市的“记忆单元”,存储状态
  • BRAM(块RAM)——城市的“仓库”,存大量数据
  • DSP(数字信号处理单元)——城市的“计算中心”,做乘加运算
  • IO(输入输出)——城市的“港口”,与外界通信

你想想看,逆向工程其实就是拿到一张城市地图,然后反推当初设计师是怎么摆放这些积木的。

2.1.1 LUT:最灵活的积木

LUT是FPGA里最基础的逻辑单元。一个4输入LUT,本质上就是一个16×1的SRAM。你给它输入4位地址,它输出1位数据。为什么说它灵活?因为任何4输入的逻辑函数,都能用这16个bit存下来。

关键点:逆向时,我们经常需要从比特流中提取LUT的配置值。这些值直接决定了逻辑功能。我曾经在一个项目中,通过比对不同版本的LUT配置,找到了芯片设计中的隐藏后门。

举个例子,一个简单的与门(AND),用LUT实现就是:

// 4输入LUT实现 Y = A & B & C & D
// LUT配置值(16进制):0x8000
// 二进制:1000 0000 0000 0000
// 只有输入全为1时,输出才为1

2.1.2 FF:时序的节拍器

触发器负责存储状态。每个FF通常跟LUT配对出现,组成一个Slice。我记得有一次逆向一个通信协议芯片,就是靠分析FF的级联关系,才理清了数据流的时序。

FF有几个关键属性:

  • 时钟边沿(上升沿/下降沿触发)
  • 复位方式(同步/异步)
  • 初始值(上电后的状态)

小技巧:逆向时,如果发现某个FF的初始值跟其他不一样,多半是个状态机的起始状态。我曾经靠这个线索,成功还原了一个加密算法的状态机。

2.1.3 BRAM:数据的大本营

BRAM是FPGA里真正的“大块头”。一个BRAM通常有18Kb或36Kb。它可以配置成单端口、双端口,甚至FIFO。

为什么逆向时要关注BRAM?因为很多关键数据(比如查找表、系数、密钥)都藏在里面。我见过一个案例,攻击者把AES的S盒藏在BRAM里,结果被我们通过分析BRAM的初始化内容直接提取出来了。

2.1.4 DSP:算力的核心

DSP单元专门做乘法、加法、乘加运算。现代FPGA的DSP还能支持浮点运算。逆向时,如果看到大量DSP被使用,基本可以断定这个设计在做信号处理或神经网络推理。

DSP的配置通常包括:

  • 操作模式(乘法/加法/乘加)
  • 数据位宽(8/16/32位)
  • 流水线级数

2.1.5 IO:与外界的桥梁

IO单元负责芯片与外部通信。每个IO可以配置成输入、输出或双向。高速IO还支持SerDes(串并转换)。

逆向时,IO配置能告诉我们:

  • 芯片用了哪些外部接口(SPI、I2C、DDR等)
  • 信号的电平标准(LVCMOS、LVDS等)
  • 时序约束(建立时间、保持时间)

2.2 网表文件格式解析

网表文件,说白了就是FPGA设计的“电路图文本版”。它用文本描述所有逻辑单元和它们之间的连接关系。常见的格式有EDIF、NGD、ASCII网表。

2.2.1 EDIF格式

EDIF(Electronic Design Interchange Format)是一种标准化的网表格式。它长得有点像Lisp语言,全是括号嵌套括号。

一个典型的EDIF片段:

(cell AND2
  (cellType GENERIC)
  (view netlist
    (interface
      (port A (direction INPUT))
      (port B (direction INPUT))
      (port Y (direction OUTPUT))
    )
    (implement
      (net A (joined (portRef A)))
      (net B (joined (portRef B)))
      (net Y (joined (portRef Y)))
    )
  )
)

看着是不是有点晕?我刚开始接触EDIF时也这样。后来发现,只要抓住三个核心要素就行:

  • cell:逻辑单元(LUT、FF等)
  • net:连线
  • port:端口

注意:EDIF文件通常很大,一个中等规模的FPGA设计,EDIF文件可能有几十MB。手动解析不现实,建议用脚本处理。我曾经用Python写了个解析器,把EDIF转成图结构,方便后续分析。

2.2.2 NGD格式

NGD(Native Generic Database)是Xilinx(现AMD)的专有格式。它比EDIF更紧凑,包含了更多物理信息,比如:

  • 逻辑单元在芯片上的具体位置
  • 布线资源的使用情况
  • 时序约束信息

NGD文件是二进制格式,不能直接文本查看。但Xilinx提供了ngd2edif工具,可以把它转成EDIF。我个人习惯先用这个工具转一下,再用EDIF解析器处理。

2.2.3 ASCII网表

有些厂商(比如Lattice、Microchip)使用ASCII格式的网表。这种格式可读性最好,但信息量相对较少。

一个ASCII网表示例:

.inputs A B
.outputs Y
.names A B Y
11 1
.end

这个例子描述了一个与门:当A和B都是1时,Y输出1。简单明了。

2.3 知识体系总览

为了让大家更直观地理解本章的知识结构,我画了一张图:

FPGA网表逆向知识体系 FPGA内部架构 LUT FF BRAM DSP IO 网表文件格式 EDIF(标准格式) NGD(Xilinx专有) ASCII(可读性最好) 逆向应用 逻辑功能提取 时序分析 隐藏功能发现

这张图展示了本章的核心逻辑:从FPGA内部架构出发,理解各种逻辑单元;然后掌握网表文件的格式,知道如何解析它们;最后把这些知识应用到逆向工程中。

2.4 实战建议

学完这章,我建议你动手做两件事:

  1. 找一个简单的FPGA设计(比如LED闪烁),用厂商工具生成EDIF网表,然后用文本编辑器打开看看。别怕,慢慢看,你会发现规律。
  2. 写一个简单的脚本,统计网表里各种单元的数量。这能帮你快速了解设计的规模。

我的经验:刚开始逆向时,别想着一步到位。先从小设计入手,熟悉了LUT、FF这些基本单元后,再挑战复杂的。我曾经花了一周时间,就为了搞清楚一个4输入LUT的配置位是怎么映射的。但搞懂之后,后面就顺了。

好了,这章的内容就到这里。记住,FPGA逆向的核心就是理解“积木”和“图纸”的关系。下一章我们会深入讨论如何从比特流中提取这些信息。

专注资料整理