Skip to main content

Crate pers

Crate pers 

Source
Expand description

§PeRs

PeRs is a parser for Windows Portable Executable (PE) files. It started as a port of the C# library PeNet and is tested against PeNet’s test cases and test binaries. PeRs borrows the file data instead of copying it: fixed layout headers and tables are referenced in place, variable structures such as imports, version information, .NET tables and certificates are decoded into small owned values. The API uses typed addresses, real enums and Result<Option<T>> accessors that tell absent and malformed structures apart. It needs no native Windows APIs and works in no_std environments with alloc.

§Features

  • All native PE headers: DOS, NT, file and optional header (32 and 64 bit), sections, data directories
  • Imports, delay imports, exports (including forwarded exports), bound imports, relocations, exception directory with unwind information, debug directory (CodeView PDB info), TLS callbacks, load config, COM descriptor
  • Resources: version information and icons as .ico files
  • .NET metadata: metadata header, heaps and all metadata tables
  • Hashes: MD5, SHA-1, SHA-256, ImpHash, TypeRefHash, Authenticode hash
  • Authenticode: signing certificate, signature validation, signing time stamp, CRL URLs
  • Editing with PeEditor: add and remove sections, add imports

§Usage

use pers::PeFile;

let data = std::fs::read("firefox.exe")?;
let pe = PeFile::parse(&data)?;

let file_header = pe.nt_headers().file_header;
println!("Machine: {}", file_header.machine().description());
println!("SHA-256: {}", hex::encode(pe.sha256()));

for section in pe.section_headers()? {
    println!("{} {}", section.name(), section.virtual_address());
}

// Ok(None) if the file has no imports, Err if they are malformed.
if let Some(imports) = pe.imported_functions()? {
    for import in imports {
        println!("{} {:?}", import.dll, import.name);
    }
}

println!("Signed: {}", pe.is_signed());
println!("Valid signature: {}", pe.has_valid_signature());

Large files can be memory mapped:

use pers::{MmFile, PeFile};

let file = MmFile::open_read_only("large.dll")?;
let pe = PeFile::parse(&file)?;

Edits go through PeEditor, which owns the data:

use pers::editor::PeEditor;

let mut editor = PeEditor::new(std::fs::read("app.exe")?)?;
editor.add_import("gdi32.dll", "StartPage")?;
std::fs::write("app_patched.exe", editor.into_bytes())?;

§Cargo Features

FeatureDefaultContent
stdyesstd::error::Error, I/O helpers, thread safe caches
mmapyesMmFile for memory mapped files
resourcesyesVersion information and icons
dotnetyes.NET metadata
authenticodeyesAuthenticode signatures

Without std the crate is no_std and needs alloc.

§Comparison

CrateFormatsFocusCompared to PeRs
objectELF, Mach-O, PE/COFF, WasmUnified zero-copy reading and writingMulti-format and very mature; no .NET metadata or Authenticode validation
goblinELF, Mach-O, PECross-platform parser and loaderMulti-format; covers the PE basics, no .NET metadata or signature validation
pelitePELow allocation PE reader with resourcesSimilar borrowing design; no .NET metadata or signature validation
editpePEEditing resources such as icons and version infoStronger resource editing; less analysis
authenticodePEAuthenticode signatures, built on objectSignature parsing and hashing; PeRs also verifies signer and counter signer signatures
liefELF, Mach-O, PEBindings to the C++ library LIEFVery complete, but needs the native library

PeRs only reads PE files. It combines the native structures with .NET metadata, ImpHash, TypeRefHash, version information, icons and Authenticode validation in one no_std crate, and is tested against the cases and binaries of PeNet. For tools that need ELF or Mach-O as well, use object or goblin.

§Development

nix develop
just test
just clippy
just fmt
just features   # build every feature combination

The tests are ported from PeNet and use its test binaries in tests/Binaries. A fuzz target lives in fuzz/ (cargo +nightly fuzz run parse).

§License

Licensed under either of

at your option.

§Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Re-exports§

pub use address::FileOffset;
pub use address::Rva;
pub use address::Va;

Modules§

address
Typed addresses. Mixing a relative virtual address with a file offset is a type error.
authenticodeauthenticode
Authenticode signatures.
editor
Editing of PE files: adding and removing sections, adding imports.
imp_hash
Import hash (ImpHash) as defined by Mandiant: the MD5 of the normalized import list.
netdotnet
.NET (CLR) metadata: the metadata root, its heaps and the metadata tables.
pe
Native PE structures.
resourceresources
Resources: version information and icons.

Structs§

MmFilemmap
A memory mapped file, the fastest way to parse large files: PeFile::parse(&MmFile::open_read_only(path)?).
PeFile
A parsed Portable Executable file borrowing its data.

Enums§

Error
Errors of parsing and editing PE files. The variants carry no heap data, so errors are cheap to clone and work without std.

Type Aliases§

Result