bitfiltrator

Bitfiltrator: A general approach for reverse-engineering Xilinx bitstream formats

https://github.com/epfl-vlsc/bitfiltrator

Science Score: 52.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
    Organization epfl-vlsc has institutional domain (vlsc.epfl.ch)
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (8.4%) to scientific vocabulary
Last synced: 6 months ago · JSON representation ·

Repository

Bitfiltrator: A general approach for reverse-engineering Xilinx bitstream formats

Basic Info
  • Host: GitHub
  • Owner: epfl-vlsc
  • License: mit
  • Language: Python
  • Default Branch: main
  • Size: 8.85 MB
Statistics
  • Stars: 38
  • Watchers: 4
  • Forks: 6
  • Open Issues: 0
  • Releases: 0
Created over 3 years ago · Last pushed almost 3 years ago
Metadata Files
Readme License Citation

README.md

Bitfiltrator

Introduction

Bitfiltrator is an automated bitstream parameter extraction tool for Xilinx UltraScale and UltraScale+ FPGAs.

Bitstream parameters are a series of device- and architecture-specific constants that are needed to locate the position of certain configuration bits in a bitstream. These parameters are the basis for implementing bitstream manipulation tools or open-source FPGA toolchains for modern US/US+ devices.

All extracted parameters are stored in human/machine-readable files. Pre-generated versions of these files are available in the resources directory.

Demo (locating configuration bits for named BELs)

The demo.py file contains a short program that prints out the SLR name, frame addresses, and frame offsets of any LUT, Flip-Flop, and 18K BRAM given as argument. The demo also prints out the byte offset of each FPGA configuration frame from the start of the bitstream (header included).

```python ╰─❯ python3 demo.py -h usage: demo.py [-h] [--luts [LUTS ...]] [--ffs [FFS ...]] [--brams [BRAMS ...]] bitstream

Demo application that locates a resource and prints its SLR name, frame addresses, and frame offsets.

positional arguments: bitstream Input bitstream (with header). Must be a full bitstream, not a partial one.

options: -h, --help show this help message and exit --luts [LUTS ...] Name of LUTs to locate in the form of SLICEX(\d+)Y(\d+)/[A-H]6LUT --ffs [FFS ...] Name of Flip-Flops to locate in the form of SLICEX(\d+)Y(\d+)/[A-H]FF2? --brams [BRAMS ...] Name of 18K BRAMs to locate in the form of RAMB18_X(\d+)Y(\d+)

╰─❯ python3 demo.py --luts SLICEX0Y13/B6LUT --ffs SLICEX0Y13/GFF2 --brams RAMB18X2Y12 SLICEX0Y13/B6LUT INIT[ 0] -> SLR0, frame address: 0x00000307 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 7), frame offset: 639, frame byte offset in bitstream: 26754897 INIT[ 1] -> SLR0, frame address: 0x00000306 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 6), frame offset: 639, frame byte offset in bitstream: 26754525 INIT[ 2] -> SLR0, frame address: 0x00000305 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 5), frame offset: 639, frame byte offset in bitstream: 26754153 INIT[ 3] -> SLR0, frame address: 0x00000304 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 4), frame offset: 639, frame byte offset in bitstream: 26753781 ... INIT[62] -> SLR0, frame address: 0x00000305 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 5), frame offset: 624, frame byte offset in bitstream: 26754153 INIT[63] -> SLR0, frame address: 0x00000304 (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINORADDR = 4), frame offset: 624, frame byte offset in bitstream: 26753781

SLICEX0Y13/GFF2 INIT -> SLR0, frame address: 0x0000030c (BLOCKTYPE = CLBIOCLK, ROWADDR = 0, COLADDR = 3, MINOR_ADDR = 12), frame offset: 668, frame byte offset in bitstream: 26756757

RAMB18X2Y12 INIT[ 0] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1536, frame byte offset in bitstream: 47909421 INIT[ 1] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1548, frame byte offset in bitstream: 47909421 INIT[ 2] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1560, frame byte offset in bitstream: 47909421 INIT[ 3] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1572, frame byte offset in bitstream: 47909421 ... INIT[16382] -> SLR0, frame address: 0x010002ff (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 255), frame offset: 1631, frame byte offset in bitstream: 48004281 INIT[16383] -> SLR0, frame address: 0x010002ff (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 255), frame offset: 1643, frame byte offset in bitstream: 48004281 INITP[ 0] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1584, frame byte offset in bitstream: 47909421 INITP[ 1] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1590, frame byte offset in bitstream: 47909421 INITP[ 2] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1587, frame byte offset in bitstream: 47909421 INITP[ 3] -> SLR0, frame address: 0x01000200 (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 0), frame offset: 1593, frame byte offset in bitstream: 47909421 ... INITP[ 2046] -> SLR0, frame address: 0x010002ff (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINORADDR = 255), frame offset: 1589, frame byte offset in bitstream: 48004281 INITP[ 2047] -> SLR0, frame address: 0x010002ff (BLOCKTYPE = BRAMCONTENT, ROWADDR = 0, COLADDR = 2, MINOR_ADDR = 255), frame offset: 1595, frame byte offset in bitstream: 48004281 ```

Installation

Bitfiltrator is implemented entirely in Python (3.10) and Tcl. The easiest way to use Bitfiltrator is to set up its dependencies using a virtual environment. The instructions below use conda as the virtual environment management system, but you can easily perform a similar setup with other environment managers (venv, etc.) if you prefer.

Bitfiltrator was developed and tested on Ubuntu 20.04 and Vivado 2021.1 / 2022.1. It should run without issues on any machine that has Vivado and Python installed (using conda below).

```bash

Download and install conda

wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x8664.sh -O ~/Downloads/Miniconda3-latest-Linux-x8664.sh cd ~/Downloads ./Miniconda3-latest-Linux-x86_64.sh

Set up python 3.10 virtual environment called "bitfiltrator" and install dependencies

conda create -n bitfiltrator python=3.10 conda activate bitfiltrator pip install joblib more_itertools numpy pandas ```

You must ensure the virtual environment is active and Vivado is available in your PATH before calling Bitfiltrator. This can generally be performed by executing the following commands:

bash conda activate bitfiltrator source <xilinx-install-dir>/Vivado/<vivado-version>/settings64.sh

Usage

FPGA part files

Bitfiltrator reverse-engineers device/architecture parameters by creating carefully-crafted designs, generating bitstreams for them, and analyzing their binary structure. Since bitstreams are generated, Bitfiltrator needs to supply an FPGA part number to Vivado in each experiment. A pre-generated list of part numbers exists in parts_all.json and parts_webpack.json. Bitfiltrator queries these lists internally.

However, these pre-generated lists may contain more devices than your local Vivado install supports. Running Bitfiltrator with these pre-generated lists may therefore cause Vivado to crash if it is asked to generate a bitstream for an unknown part number.

Deleting the pre-generated part files and generating them from your local Vivado installation solves the problem.

bash ╰─❯ rm -f ./resources/parts_all.json ╰─❯ rm -f ./resources/parts_webpack.json ╰─❯ python3 src/generate_fpga_part_files.py

Extracting device parameters (i.e., a device summary)

Use createdevicesummary.py to extract device parameters for a given FPGA part number.

For example, the following command creates a device summary for the xcu200 FPGA and places it in the default resources/devices directory. A copy of the device summary is also written to the user-requested file (xcu200_summary.json below).

bash ╰─❯ python3 src/create_device_summary.py xcu200-fsgd2104-2-e <working-dir> <xcu200_summary.json>

Please consider creating a pull request with your device parameter file if it is not part of the pre-generated list. In particular, we welcome PRs for non-WebPack devices :)

Extracting architecture parameters (i.e., an architecture summary)

Use createarchsummary.py to extract architecture parameters for a given FPGA part number.

Note that all US FPGAs have the same architecture summary, and that all US+ FPGAs have the same architecture summary (which is different from US FPGAs). It is therefore recommended to use the smallest FPGA that has the given target architecture when calling this program to reduce the size of bitstreams that are generated and analyzed. This significantly speeds up the processes of extracting architecture parameters.

For example, the following command creates an architecture summary for the xazu2eg-sbva484-1-i FPGA and places it in the default resources/archs directory. A copy of the architecture summary is also written to the user-requested file (usplus_summary.json below).

bash ╰─❯ python3 src/create_arch_summary.py xazu2eg-sbva484-1-i <working-dir> <usplus_summary.json>

Running all steps above automatically

The run_all.py program creates device and architecture summaries for all WebPack-enabled US/US+ FPGAs available in your local Vivado installation. Note that this requires several GBs of disk space and takes several hours to complete depending on the speed and parallelism of your machine.

bash ╰─❯ rm -f ./resources/parts_all.json ╰─❯ rm -f ./resources/parts_webpack.json ╰─❯ rm -f ./resources/archs/ ╰─❯ rm -f ./resources/devices/ ╰─❯ python3 src/run_all.py --verify <working-dir>

For reference, the end-to-end runtime for reverse-engineering 34 WebPack-enabled US/US+ FPGAs available in Vivado 2021.1 is ~3h on a Xeon E5-2680 v3 CPU (using --process_cnt 24). Disk space use for this process was ~16 GB.

License

All code in this repository is available under the MIT license. Please refer to the full license for details.

About

This project was created by Sahand Kashani, Mahyar Emami, and James Larus at EPFL.

A top-down explanation of how Bitfiltrator reverse-engineers the configuration bits of various cells can be found in the following paper and presentation.

Sahand Kashani, Mahyar Emami, and James R. Larus. Bitfiltrator: A general approach for reverse-engineering Xilinx bitstream formats. In Proceedings of the 32nd International Conference on Field‐Programmable Logic and Applications, Belfast, August 2022.

If you'd like to cite this software, please use the following Bibtex entry:

@software{bitfiltrator, author = {Kashani, Sahand and Emami, Mahyar and R. Larus, James}, license = {MIT}, month = {8}, title = {{Bitfiltrator: A general approach for reverse-engineering Xilinx bitstream formats}}, url = {https://github.com/epfl-vlsc/bitfiltrator}, version = {1.0}, year = {2022} }

Owner

  • Name: EPFL VLSC
  • Login: epfl-vlsc
  • Kind: organization
  • Location: Lausanne, CH

http://vlsc.epfl.ch/

Citation (CITATION.cff)

# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!

cff-version: 1.2.0
title: >-
  Bitfiltrator: A general approach for
  reverse-engineering Xilinx bitstream formats
message: >-
  If you use this software, please cite it using the
  metadata from this file.
type: software
authors:
  - given-names: Sahand
    email: sahand.kashani@epfl.ch
    affiliation: EPFL
    family-names: Kashani
  - given-names: Mahyar
    family-names: Emami
    email: mahyar.emami@epfl.ch
    affiliation: EPFL
  - given-names: James
    name-particle: R.
    family-names: Larus
    email: james.larus@epfl.ch
    affiliation: EPFL
repository-code: 'https://github.com/epfl-vlsc/bitfiltrator'
abstract: >-
  As the usage of FPGAs spreads, engineers will
  inevitably employ them in ways unforeseen—or
  unwanted—by their manufacturers.

  Xilinx’s toolchains offer multiple points for
  customizing the FPGA compilation flow, but all
  flows must end with Vivado as it is the only tool
  capable of generating the bitstream to program an
  FPGA.

  Xilinx does not document its bitstream format, so
  users who wish to bypass Vivado and modify a
  bitstream directly must reverse-engineer it to
  discover the location and format of cells.


  Prior work has reverse-engineered parts of the
  bitstream format for security or
  debugging/instrumentation activities, but no paper
  has explained how to do this reverse engineering
  systematically! Code from prior efforts (when
  available) is hard- coded to reverse engineer a
  specific device and is difficult or impossible to
  use for another one.

  These efforts—focused on applications instead of
  reverse-engineering—compel engineers who need to
  modify a bitstream to rediscover unwritten
  practice.

  Our work bridges this gap by explaining: (1) the
  various parameters needed to navigate a bitstream
  correctly, (2) the experiments to obtain them, and
  (3) the many pitfalls and erroneous assumptions to
  avoid while undertaking this endeavor.

  We demonstrate our technique by using it to extract
  the bitstream format of initial LUT equations,
  LUTRAM contents, BRAM contents, and register values
  in Xilinx UltraScale and UltraScale+ FPGAs.

  Our methods are implemented in an open-source tool,
  Bitfiltrator [1], that can extract device layouts
  and architecture- specific bitstream formats for
  these cells automatically and without physical
  access to an FPGA.
keywords:
  - Xilinx
  - Bitstream
  - Reverse Engineering
  - UltraScale
  - UltraScale+
license: MIT
version: '1.0'
date-released: '2022-08-30'

GitHub Events

Total
  • Watch event: 18
  • Fork event: 4
Last Year
  • Watch event: 18
  • Fork event: 4