Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/pyblish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Build and Publish to PyPI

on:
push:
tags:
- "v*"
release:
types: [published]

permissions:
contents: read
id-token: write # Required for OIDC
actions: read

jobs:
build:
name: Build sdist and wheel
runs-on: ubuntu-latest

steps:
- name: Check out source
uses: actions/checkout@v4
with:
fetch-depth: 0 # Important for setuptools_scm

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"

- name: Install build tool
run: |
python -m pip install --upgrade pip
pip install build setuptools_scm

- name: Build package
run: python -m build --sdist --wheel --outdir dist

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/

publish:
name: Publish to PyPI (OIDC)
needs: build
runs-on: ubuntu-latest
environment: pypi


permissions:
id-token: write # Required for trusted publishing
contents: read

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Publish to PyPI via OIDC
uses: pypa/gh-action-pypi-publish@release/v1
with:
verbose: true
skip-existing: false
97 changes: 92 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,98 @@
# Byte-compiled python files
.DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/

# Emacs autosaves.
*.py[cod]
*$py.class
*~

# Python Packaging
# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
uefi_parser.egg-info/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# PyBuilder
target/

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock

# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
*venv/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/
_version.py
node_modules
tmp
74 changes: 43 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,60 @@
CERT UEFI Parser
================

The CERT UEFI Parser is a tool for inspecting firmware ROM images,
installers, and related files, especially those related to UEFI. It
pulls together information from the UEFI standards and a variety of
independent research (e.g. Igor Skochinsky's Intel ME work).

Written in Python version 3 and using the Construct module, it's less
rigid than the EDK2 UEFI reference implementation and more easily
extended to support proprietary and experimental data structure
parsing. CERT UEFI Parser is intended to parse all related data
formats including standard file formats commonly found inside UEFI
ROMs such as Portable Executables (PEs) and images. CERT UEFI Parser
is free from NDAs or other restrictions on proprietary formats, having
been reverse engineered from widely available public information and
original work.
The CERT UEFI Parser is a Python-based tool for inspecting firmware ROM
images, installers, and related files, especially those associated with UEFI.
It combines information from the UEFI specifications with insights from
independent firmware research (for example, Igor Skochinsky’s Intel ME work).

Written for Python 3 and built on the Construct parsing framework, the parser
is more flexible than the EDK2 reference implementation and is easier to extend
to proprietary or experimental data structures. CERT UEFI Parser aims to
support all data formats commonly found inside UEFI ROMs, including Portable
Executables (PEs) and image structures. The project is free of NDAs or other
restrictions; all proprietary formats have been reverse engineered from public
information and original analysis.

Installation
------------

To install CERT UEFI Parser, you'll need a Python virtual environment
containing not only this repository, but also a related support
library, CERT UEFI support. The commands to install everything are:
The parser depends on the **cert-uefi-support** package, which provides
lower-level decompression and binary utilities. Both packages are now
available on PyPI.

### Basic installation:

```
$ python3 -m venv cert-venv
$ ./cert-venv/bin/pip install cert-uefi-support cert-uefi-parser
```

You may activate the cert-venv if you choose.
### Optional GUI Support (Qt)

Then install the packages directly from GitHub:
GUI support is optional and provided via the PySide6 package. It is a
large dependency, so it is not installed by default. To install with the GUI
extras:

```
$ ./cert-venv/bin/pip install git+https://github.com/cmu-sei/cert-uefi-support
$ ./cert-venv/bin/pip install git+https://github.com/cmu-sei/cert-uefi-parser
$ python3 -m venv cert-venv
$ ./cert-venv/bin/pip install cert-uefi-support cert-uefi-parser[qt]
```

### Installing from the Official Git Repositories

```
$ python3 -m venv cert-venv
$ ./cert-venv/bin/pip install \
git+https://github.com/cmu-sei/cert-uefi-support \
"cert-uefi-parser[qt] @ git+https://github.com/cmu-sei/cert-uefi-parser.git"
```

Usage
-----

CERT UEFI Parser has four primary modes: a GUI display, an ASCII text
display (including ANSI color codes by default), a JSON format, and a
filtered JSON format containing fields that might be interest for
generating an SBOM.
CERT UEFI Parser provides four primary output modes: a graphical interface, an
ASCII text display (with ANSI color output enabled by default), a full JSON
representation, and a filtered JSON representation containing fields that are
useful for generating a Software Bill of Materials (SBOM).

```
$ ./cert-venv/bin/cert-uefi-parser --gui {firmware-related-file}
Expand All @@ -51,9 +63,9 @@ generating an SBOM.
$ ./cert-venv/bin/cert-uefi-parser --sbom {firmware-related-file} >output.json
```

For sample input files, we recommend downloading the BIOS updater
executable that your hardware vendor ships through their normal
support channels. While not all models are guaranteed to be
supported, many of the popular vendors are, and examining a ROM update
is a good place to get started exploring on exploring the capabilities
of CERT UEFI Parser.
Sample firmware files can typically be obtained by downloading the BIOS or
UEFI update tools from your system vendor’s support site. While not all
models are guaranteed to be fully supported, many common vendor formats parse
successfully, and examining these update files is a good way to begin exploring
the parser’s capabilities.

42 changes: 28 additions & 14 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,42 +1,56 @@
[build-system]
requires = ["setuptools"]
requires = ["setuptools>=61.0", "setuptools_scm[toml]>=6.0", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
packages = [ "uefi_parser" ]

[project]
name = "uefi_parser"
version = "1.0"
authors = [
{ name = "Cory Cohen", email = "cfc@cert.org"},
{ name = "Michael Duggan", email = "mwd@cert.org"}
]
name = "cert-uefi-parser"
authors = [{ name = "CERT Threat Analysis Team", email = "cert@cert.org"}]
description = "Various data structures and parsing tools for UEFI firmware."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"typing_extensions",
"construct>=2.10.70",
"pefile",
"PySide6",
"asn1crypto",
"uswid",
"uefi_support",
"cert-uefi-support",
]
license-files = [ "LICENSE.md" ]
keywords = [ "security", "uefi", "firmware", "parsing", "bios" ]
readme = {file="README.md", content-type="text/markdown"}
dynamic = ["version"]
keywords = ["uefi", "firmware", "uefi-parser", "support-tools", "security", "bios"]
license = { file = "LICENSE.md" }
classifiers = [
# https://pypi.python.org/pypi?%3Aaction=list_classifiers
'Development Status :: 4 - Beta',
'Intended Audience :: System Administrators',
'Topic :: Security',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3',
'License :: Other/Proprietary License',
'Topic :: Security',
'Topic :: System :: Hardware'
]

[project.optional-dependencies]
qt = [
"PySide6"
]


[project.urls]
Homepage = "https://github.com/cmu-sei/cert-uefi-parser"
Repository = "https://github.com/cmu-sei/cert-uefi-parser"
Issues = "https://github.com/cmu-sei/cert-uefi-parser/issues"


[project.scripts]
cert-uefi-parser = "uefi_parser.cmds:cert_uefi_parser"

[tool.setuptools.package-dir]
uefi_parser = "uefi_parser"

#version control using git tags
[tool.setuptools_scm]
version_scheme = "guess-next-dev"
local_scheme = "node-and-date"
8 changes: 7 additions & 1 deletion uefi_parser/cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from .base import FirmwareStructure
from .auto import AutoObject, BruteForceFinder
from .utils import red, blue
from .gui import run_gui
from .gui_helper import ensure_gui_environment

# Hacky solution for --no-color option, using a global.
nocolor = False
Expand Down Expand Up @@ -79,6 +79,7 @@ def process_file(filename: str, args: argparse.Namespace) -> bool:
elif args.sbom:
emit(json.dumps(result.sbom(), indent=2))
elif args.gui:
from .gui import run_gui
run_gui(result, args)
else:
result.report()
Expand Down Expand Up @@ -161,6 +162,11 @@ def cert_uefi_parser() -> None:
if args.json or args.sbom:
nocolor = True

# If we're going to open the GUI the environment needs to be correct.
# Do this check before parsing the file to avoid wasted effort.
if args.gui and not ensure_gui_environment(argparser):
sys.exit(3)

try:
succeeded = process_file(args.file, args)
# Continue processing the next file, even if this one failed, but remeber thet
Expand Down
Loading