dnfile 0.3.0

.NET binary extractor
Documentation

dnfile

CI Crates.io Docs.rs License MSRV

A Rust crate for parsing .NET executable files — PE files containing CLR (Common Language Runtime) metadata, streams, tables, and CIL (Common Intermediate Language) bytecode.

It is a Rust port of malwarefrank/dnfile, the Python library used by tools like Mandiant capa for static analysis of .NET malware.

What it does

Given a .NET PE file (an executable, DLL, or assembly compiled to MSIL), dnfile lets you:

  • Parse the CLR header and verify .NET flags (IL-only, 32-bit-required, strong-named, etc.).
  • Walk the #~ / #- metadata stream and access its ~50 ECMA-335 tables (Module, TypeDef, MethodDef, MemberRef, Assembly, ManifestResource, etc.) by name or index.
  • Read #Strings, #US, #GUID, #Blob heaps.
  • Resolve coded indexes (TypeDefOrRef, MemberRefParent, HasCustomAttribute, …) into typed table rows.
  • Decode every CIL instruction in every method body, including operand types, branch targets, switch tables, and tokens.
  • Detect mixed-mode (managed + native) and P/Invoke functions.

Quick start

Add to your Cargo.toml:

[dependencies]
dnfile = "0.3"

Then:

use dnfile::DnPe;

fn main() -> dnfile::Result<()> {
    let pe = DnPe::new("MyAssembly.dll")?;
    let clr = pe.net()?;

    // CLR header flags
    println!("flags: {:?}", clr.flags);

    // Iterate decoded method bodies
    for func in clr.functions() {
        println!("function with {} instructions", func.instructions.len());
    }

    // Direct metadata-table access by name
    let methods = clr.md_table("MethodDef")?;
    println!("MethodDef rows: {}", methods.row_count());

    // Read a user string by RID
    if let Ok(s) = clr.get_us(1) {
        println!("first user string: {}", s);
    }

    Ok(())
}

Command-line tool: dndump

dndump is a Mandiant capa-style inspector for .NET binaries. It prints the CLR header, metadata streams, populated tables, and the first methods discovered. Pre-built binaries for Linux / macOS / Windows are attached to each GitHub release.

Install from source:

cargo install dnfile --features cli

Or build locally:

cargo build --release --features cli --bin dndump
./target/release/dndump path/to/Sample.exe

Other useful invocations:

dndump --methods 100 Sample.exe        # show first 100 methods
dndump --strings Sample.exe            # also dump #US user strings
dndump --json Sample.exe > parsed.json # machine-readable output

A minimal library-usage example lives in examples/dnfile.rs:

cargo run --example dnfile -- path/to/Sample.exe

Feature coverage

Metadata tables supported (with full parse implementations):

Module, TypeRef, TypeDef, Field, MethodDef, Param, InterfaceImpl, MemberRef, Constant, CustomAttribute, FieldMarshal, DeclSecurity, ClassLayout, FieldLayout, StandAloneSig, EventMap, Event, PropertyMap, Property, MethodSemantics, MethodImpl, ModuleRef, TypeSpec, ImplMap, FieldRva, EncLog, EncMap, Assembly, AssemblyProcessor, AssemblyOS, AssemblyRef, AssemblyRefProcessor, AssemblyRefOS, File, ExportedType, ManifestResource, NestedClass, GenericParam, GenericMethod, GenericParamConstraint, FieldPtr, MethodPtr, ParamPtr.

Stubbed (returns NotImplementedError, does not panic): EventPtr, PropertyPtr, Unused, MaxTable. These appear only in uncompressed #- streams or are reserved by ECMA-335.

CIL: all one- and two-byte opcodes from ECMA-335 §III.

Not currently implemented: managed-resource (ResourceSet) parsing — only the ManifestResource metadata-table entry is exposed; the resource blob itself is not parsed.

Requirements

  • Rust 1.85 or newer (2024 edition).

Why a Rust port?

dnfile-rs was originally written to power capa-rs, the Rust port of Mandiant's capability extractor. It is suitable for any Rust-side static analysis of .NET binaries that does not require executing the IL.

Used by

  • capa-rs — static capability extractor for PE / ELF / shellcode / .NET binaries.

License

Licensed under the Apache License, Version 2.0.

Acknowledgements

  • malwarefrank/dnfile — the original Python implementation that this port is modelled on.
  • ECMA-335 — Common Language Infrastructure (CLI) specification.