Struct File

pub struct File { /* private fields */ }
Expand description

Provides access to low-level file and memory parsing utilities.

The crate::Parser type is used for decoding CIL bytecode and metadata streams.

§Usage Examples

use dotscope::{Parser, disassembler::decode_instruction};
let code = [0x2A]; // ret
let mut parser = Parser::new(&code);
let instr = decode_instruction(&mut parser, 0x1000)?;
assert_eq!(instr.mnemonic, "ret");

The self-referencing struct.

Implementations§

§

impl File

pub fn from_file(file: &Path) -> Result<File>

Loads a PE file from the given path.

This method opens a file from disk and parses it as a .NET PE file. The file is memory-mapped for efficient access.

§Arguments
  • file - Path to the PE file on disk.
§Errors

Returns an error if:

  • The file cannot be read or opened
  • The file is not a valid PE format
  • The PE file does not contain .NET metadata (missing CLR runtime header)
  • The file is empty
§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
println!("Loaded {} bytes with {} sections",
         file.len(), file.sections().count());

// Access assembly metadata
let (clr_rva, clr_size) = file.clr();
println!("CLR runtime header: RVA=0x{:x}, size={}", clr_rva, clr_size);

pub fn from_mem(data: Vec<u8>) -> Result<File>

Loads a PE file from a memory buffer.

This method parses a PE file that’s already loaded into memory. Useful when working with embedded resources or downloaded files.

§Arguments
  • data - The bytes of the PE file.
§Errors

Returns an error if:

  • The buffer is empty
  • The data is not a valid PE format
  • The PE file does not contain .NET metadata (missing CLR runtime header)
§Examples
use dotscope::File;
use std::fs;

// Load from downloaded or embedded data
let data = fs::read("tests/samples/WindowsBase.dll")?;
let file = File::from_mem(data)?;

// Inspect the assembly
println!("Assembly size: {} bytes", file.len());
println!("Image base: 0x{:x}", file.imagebase());

// Find specific sections
for section in file.sections() {
    let name = std::str::from_utf8(&section.name)
        .unwrap_or("<invalid>")
        .trim_end_matches('\0');
    if name == ".text" {
        println!("Code section at RVA 0x{:x}", section.virtual_address);
        break;
    }
}

pub fn len(&self) -> usize

Returns the total size of the loaded file in bytes.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
println!("File size: {} bytes", file.len());

pub fn is_empty(&self) -> bool

Returns true if the file has a length of zero.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
assert!(!file.is_empty()); // Valid PE files are never empty

pub fn imagebase(&self) -> u64

Returns the image base address of the loaded PE file.

The image base is the preferred virtual address where the PE file should be loaded in memory. This is used for calculating virtual addresses.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let base = file.imagebase();
println!("Image base: 0x{:x}", base);

pub fn header(&self) -> &Header<'_>

Returns a reference to the PE header.

The PE header contains essential metadata about the executable, including the machine type, number of sections, and timestamp.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let header = file.header();
println!("Machine type: 0x{:x}", header.coff_header.machine);
println!("Number of sections: {}", header.coff_header.number_of_sections);

pub fn header_dos(&self) -> &DosHeader

Returns a reference to the DOS header.

The DOS header is the first part of a PE file and contains the DOS stub that displays the “This program cannot be run in DOS mode” message.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let dos_header = file.header_dos();
println!("DOS signature: 0x{:x}", dos_header.signature);
println!("Number of bytes on last page: {}", dos_header.bytes_on_last_page);

pub fn header_optional(&self) -> &Option<OptionalHeader>

Returns a reference to the optional header, if present.

This is always Some for valid .NET assemblies since they require an optional header to define data directories and other metadata.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let optional_header = file.header_optional().unwrap();
println!("Entry point: 0x{:x}", optional_header.standard_fields.address_of_entry_point);
println!("Subsystem: {:?}", optional_header.windows_fields.subsystem);

pub fn clr(&self) -> (usize, usize)

Returns the RVA and size (in bytes) of the CLR runtime header.

The CLR runtime header contains metadata about the .NET runtime, including pointers to metadata tables and other runtime structures.

§Returns

A tuple containing (rva, size) where:

  • rva is the relative virtual address of the CLR header
  • size is the size of the CLR header in bytes
§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let (clr_rva, clr_size) = file.clr();
println!("CLR header at RVA: 0x{:x}, size: {} bytes", clr_rva, clr_size);
§Panics

Panics if the CLR runtime header is missing (should not happen for valid .NET assemblies).

pub fn sections(&self) -> impl Iterator<Item = &SectionTable>

Returns an iterator over the section headers of the PE file.

Sections contain the actual code and data of the PE file, such as .text (executable code), .data (initialized data), and .rsrc (resources).

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
for section in file.sections() {
    let name = std::str::from_utf8(&section.name)
        .unwrap_or("<invalid>")
        .trim_end_matches('\0');
    println!("Section: {} at RVA 0x{:x}, size: {} bytes",
             name, section.virtual_address, section.virtual_size);
}

pub fn directories(&self) -> Vec<(DataDirectoryType, DataDirectory)>

Returns the data directories of the PE file.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
for (dir_type, directory) in file.directories() {
    println!("Directory: {:?}, RVA: 0x{:x}", dir_type, directory.virtual_address);
}
§Panics

Panics if the optional header is missing (should not happen for valid .NET assemblies).

pub fn data(&self) -> &[u8]

Returns the raw data of the loaded file.

This provides access to the entire PE file contents as a byte slice. Useful for reading specific offsets or when you need direct access to the binary data.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;
let data = file.data();

// Check DOS signature (MZ)
assert_eq!(&data[0..2], b"MZ");

// Access PE signature offset
let pe_offset = u32::from_le_bytes([data[60], data[61], data[62], data[63]]) as usize;
assert_eq!(&data[pe_offset..pe_offset + 4], b"PE\0\0");

pub fn data_slice(&self, offset: usize, len: usize) -> Result<&[u8]>

Returns a slice of the file data at the given offset and length.

This is a safe way to access specific portions of the PE file data with bounds checking to prevent buffer overflows.

§Arguments
  • offset - The offset to start the slice from.
  • len - The length of the slice.
§Errors

Returns an error if the requested range is out of bounds.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;

// Read the DOS header (first 64 bytes)
let dos_header = file.data_slice(0, 64)?;
assert_eq!(&dos_header[0..2], b"MZ");

// Read PE signature
let pe_offset = u32::from_le_bytes([dos_header[60], dos_header[61],
                                    dos_header[62], dos_header[63]]) as usize;
let pe_sig = file.data_slice(pe_offset, 4)?;
assert_eq!(pe_sig, b"PE\0\0");

pub fn va_to_offset(&self, va: usize) -> Result<usize>

Converts a virtual address (VA) to a file offset.

Virtual addresses are absolute addresses where the PE file would be loaded in memory. This method converts them to file offsets for reading data from the actual file.

§Arguments
  • va - The virtual address to convert.
§Errors

Returns an error if the VA is out of bounds or cannot be mapped.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;

// Convert entry point VA to file offset
let entry_point_va = file.header_optional().unwrap().standard_fields.address_of_entry_point as usize;
let image_base = file.imagebase() as usize;
let full_va = image_base + entry_point_va;

let offset = file.va_to_offset(full_va)?;
println!("Entry point at file offset: 0x{:x}", offset);

pub fn rva_to_offset(&self, rva: usize) -> Result<usize>

Converts a relative virtual address (RVA) to a file offset.

RVAs are addresses relative to the image base. This is the most common address format used within PE files for referencing data and code.

§Arguments
  • rva - The RVA to convert.
§Errors

Returns an error if the RVA cannot be mapped to a file offset.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;

// Convert CLR header RVA to file offset
let (clr_rva, _) = file.clr();
let clr_offset = file.rva_to_offset(clr_rva)?;

// Read CLR header data
let clr_data = file.data_slice(clr_offset, 72)?; // CLR header is 72 bytes
println!("CLR header starts with: {:02x?}", &clr_data[0..8]);

pub fn offset_to_rva(&self, offset: usize) -> Result<usize>

Converts a file offset to a relative virtual address (RVA).

This is the inverse of rva_to_offset(). Given a file offset, it calculates what RVA that offset corresponds to when the PE file is loaded in memory.

§Arguments
  • offset - The file offset to convert.
§Errors

Returns an error if the offset cannot be mapped to an RVA.

§Examples
use dotscope::File;
use std::path::Path;

let file = File::from_file(Path::new("tests/samples/WindowsBase.dll"))?;

// Find what RVA corresponds to file offset 0x1000
let rva = file.offset_to_rva(0x1000)?;
println!("File offset 0x1000 maps to RVA 0x{:x}", rva);

// Verify round-trip conversion
let back_to_offset = file.rva_to_offset(rva)?;
assert_eq!(back_to_offset, 0x1000);

Trait Implementations§

§

impl Drop for File

§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl Freeze for File

§

impl !RefUnwindSafe for File

§

impl Send for File

§

impl Sync for File

§

impl Unpin for File

§

impl !UnwindSafe for File

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.