LIEF - Library to Instrument Executable Formats
LIEF (Library to Instrument Executable Formats) is a robust, cross-platform library designed to parse, modify, and abstract various executable formats, including ELF, PE, Mach-O, OAT, DEX, VDEX, and ART. It provides a comprehensive, user-friendly API for C++, Python, Rust, and C, enabling detailed analysis, manipulation, and reconstruction of binaries without relying on disassemblers. Currently at version 0.17.6, LIEF maintains an active development and release cadence, with frequent updates addressing new features and bug fixes.
Warnings
- breaking LIEF v0.17.0 introduced a significant refactoring of the PE parser and builder API to align with ELF and Mach-O functionalities. Codebases processing PE binaries may require updates.
- gotcha When parsing PE files (especially from v0.17.0 onwards), certain in-depth metadata (e.g., ARM64X binaries, exceptions, exports, imports, relocations, resources, signatures) might not be parsed by default. These options must be explicitly enabled.
- breaking The `create_pe_from_scratch` feature (for building PE files from zero) was deprecated and removed in LIEF 0.17.0 due to significant bugs that often led to corrupted binaries.
- gotcha Prior to version 0.17.0, modifying PE files using the Python API was highly bugged and often resulted in unexpected file size increases or corrupted binaries, especially when modifying sections like `.rsrc`.
- deprecated LIEF v0.14.0 moved from Pybind11 to nanobind for its Python bindings. While this improved performance and compilation, internal changes for `setuptools` (replaced by `scikit-build-core`) could affect custom build processes.
Install
-
pip install lief
Imports
- lief
import lief
- lief.ELF
import lief elf_binary = lief.ELF.parse('/bin/ls') - lief.PE
import lief pe_binary = lief.PE.parse('C:\\Windows\\System32\\notepad.exe')
Quickstart
import lief
import sys
import os
def analyze_binary(filepath):
if not os.path.exists(filepath):
print(f"Error: File not found at {filepath}")
return
binary = lief.parse(filepath)
if binary is None:
print(f"Could not parse {filepath} as an executable.")
return
print(f"\nAnalyzing: {filepath}")
print(f" Format: {binary.format}")
print(f" Entrypoint: {hex(binary.entrypoint)}")
print(f" Number of sections: {len(binary.sections)}")
# Example: Accessing ELF-specific features
if binary.format == lief.Binary.FORMATS.ELF:
elf_binary = binary.as_elf()
if elf_binary and elf_binary.header:
print(f" ELF Machine Type: {elf_binary.header.machine_type}")
if elf_binary.dynamic_entries:
print(f" Number of dynamic entries: {len(elf_binary.dynamic_entries)}")
# Example: Accessing PE-specific features
elif binary.format == lief.Binary.FORMATS.PE:
pe_binary = binary.as_pe()
if pe_binary and pe_binary.header:
print(f" PE Machine Type: {pe_binary.header.machine_type}")
if pe_binary.imports:
print(f" Number of imported libraries: {len(pe_binary.imports)}")
# Try to analyze common executables based on OS
if sys.platform.startswith('linux'):
analyze_binary('/bin/ls')
elif sys.platform == 'win32':
analyze_binary('C:\\Windows\\System32\\notepad.exe')
elif sys.platform == 'darwin':
analyze_binary('/bin/ls') # macOS also uses ELF/MachO, /bin/ls is a good example
else:
print("Unsupported OS for quickstart example.")