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, assembly::decode_instruction};
let code = [0x2A]; // ret
let mut parser = Parser::new(&code);
let instr = decode_instruction(&mut parser, 0x1000)?;
assert_eq!(instr.mnemonic, "ret");Represents a loaded PE file.
This struct can load both .NET assemblies and native PE files. Use File::is_clr()
to check if the file contains .NET metadata.
This struct contains the parsed PE information and provides methods for accessing headers, sections, data directories, and for converting between address spaces. It supports loading from both files and memory buffers.
The File struct is the main entry point for working with PE files. It can load both
.NET assemblies and native PE executables/DLLs. Use File::is_clr() or File::clr()
to determine if the loaded file contains .NET metadata.
§Examples
§Loading and checking for .NET metadata
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.exe"))?;
println!("Loaded PE with {} sections", file.sections().len());
// Check if it's a .NET assembly
if file.is_clr() {
let (clr_rva, clr_size) = file.clr().unwrap();
println!(".NET assembly - CLR at RVA 0x{:x}, size={}", clr_rva, clr_size);
} else {
println!("Native PE executable (no .NET metadata)");
}§Loading from memory
use dotscope::File;
use std::fs;
let data = fs::read("assembly.dll")?;
let file = File::from_mem(data)?;
// Access CLR metadata if present
if let Some((clr_rva, clr_size)) = file.clr() {
println!("CLR header at RVA 0x{:x}, {} bytes", clr_rva, clr_size);
}§Working with addresses
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("tests/samples/WindowsBase.dll"))?;
// Convert between address spaces
let entry_rva = file.header_optional().as_ref().unwrap()
.standard_fields.address_of_entry_point as usize;
let entry_offset = file.rva_to_offset(entry_rva)?;
// Read entry point code
let entry_code = file.data_slice(entry_offset, 16)?;
println!("Entry point bytes: {:02x?}", entry_code);Implementations§
§impl File
impl File
pub fn from_path(path: impl AsRef<Path>) -> Result<File>
pub fn from_path(path: impl AsRef<Path>) -> Result<File>
Loads a PE file from the given path.
This method opens a file from disk and parses it as a PE file. The file is memory-mapped for efficient access. Works with both .NET assemblies and native PE executables/DLLs.
§Arguments
path- Path to the PE file on disk. Accepts&Path,&str,String, orPathBuf.
§Errors
Returns an error if:
- The file cannot be read or opened
- The file is not a valid PE format
- The file is empty
§Examples
use dotscope::File;
use std::path::Path;
// With Path
let file = File::from_path(Path::new("tests/samples/WindowsBase.dll"))?;
// With string slice
let file = File::from_path("tests/samples/WindowsBase.dll")?;
println!("Loaded {} bytes with {} sections",
file.len(), file.sections().len());
// Check if it's a .NET assembly
if let Some((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>
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. Works with both .NET assemblies and native PE executables/DLLs.
§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
§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 = section.name.trim_end_matches('\0');
if name == ".text" {
println!("Code section at RVA 0x{:x}", section.virtual_address);
break;
}
}pub fn from_std_file(file: File) -> Result<File>
pub fn from_std_file(file: File) -> Result<File>
Creates a File from an opened std::fs::File.
The file is memory-mapped for efficient access. This is useful when you already have a file handle open with specific permissions or from a special location.
§Arguments
file- An opened file handle
§Errors
Returns an error if:
- The file cannot be memory-mapped
- The data is not a valid PE format
§Examples
use dotscope::File;
use std::fs::File as StdFile;
let std_file = StdFile::open("assembly.dll")?;
let pe_file = File::from_std_file(std_file)?;pub fn from_reader<R: Read>(reader: R) -> Result<File>
pub fn from_reader<R: Read>(reader: R) -> Result<File>
Creates a File from any type implementing Read.
This is the most flexible constructor, allowing Files to be created from network streams, archives, cursors, or any custom reader.
§Arguments
reader- Any type implementing Read
§Errors
Returns an error if:
- The reader cannot be read
- The data is not a valid PE format
§Examples
use dotscope::File;
use std::io::Cursor;
let data = vec![/* PE bytes */];
let cursor = Cursor::new(data);
let file = File::from_reader(cursor)?;pub fn len(&self) -> usize
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
println!("File size: {} bytes", file.len());pub fn is_empty(&self) -> bool
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
assert!(!file.is_empty()); // Valid PE files are never emptypub fn imagebase(&self) -> u64
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
let base = file.imagebase();
println!("Image base: 0x{:x}", base);pub fn header(&self) -> &CoffHeader
pub fn header(&self) -> &CoffHeader
Returns a reference to the COFF header.
The COFF 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_path(Path::new("tests/samples/WindowsBase.dll"))?;
let header = file.header();
println!("Machine type: 0x{:x}", header.machine);
println!("Number of sections: {}", header.number_of_sections);pub fn header_dos(&self) -> &DosHeader
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_path(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>
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
let optional_header = file.header_optional().as_ref().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) -> Option<(usize, usize)>
pub fn clr(&self) -> Option<(usize, usize)>
Returns the RVA and size of the CLR runtime header, if present.
The CLR runtime header contains metadata about the .NET runtime, including pointers to metadata tables and other runtime structures.
§Returns
Some((rva, size))if this PE file contains a CLR runtime header (.NET assembly)Noneif this is a native PE file without .NET metadata
§Examples
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.exe"))?;
match file.clr() {
Some((rva, size)) => println!(".NET assembly: CLR at RVA 0x{:x}", rva),
None => println!("Native PE file (no .NET metadata)"),
}pub fn is_clr(&self) -> bool
pub fn is_clr(&self) -> bool
Returns true if this PE file contains a CLR runtime header (.NET assembly).
This is a convenience method equivalent to file.clr().is_some().
§Examples
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.exe"))?;
if file.is_clr() {
println!("This is a .NET assembly");
} else {
println!("This is a native PE file");
}pub fn sections(&self) -> &[SectionTable]
pub fn sections(&self) -> &[SectionTable]
Returns a slice of 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_path(Path::new("tests/samples/WindowsBase.dll"))?;
for section in file.sections() {
println!("Section: {} at RVA 0x{:x}, size: {} bytes",
section.name, section.virtual_address, section.virtual_size);
}pub fn directories(&self) -> Vec<(DataDirectoryType, DataDirectory)>
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
for (dir_type, directory) in file.directories() {
println!("Directory: {:?}, RVA: 0x{:x}", dir_type, directory.virtual_address);
}pub fn get_data_directory(
&self,
dir_type: DataDirectoryType,
) -> Option<(u32, u32)>
pub fn get_data_directory( &self, dir_type: DataDirectoryType, ) -> Option<(u32, u32)>
Returns the RVA and size of a specific data directory entry.
This method provides unified access to PE data directory entries by type.
It returns the virtual address and size if the directory exists and is valid,
or None if the directory doesn’t exist or has zero address/size.
§Arguments
dir_type- The type of data directory to retrieve
§Returns
Some((rva, size))if the directory exists with non-zero address and sizeNoneif the directory doesn’t exist or has zero address/size
§Examples
use dotscope::File;
use dotscope::DataDirectoryType;
use std::path::Path;
let file = File::from_path(Path::new("example.dll"))?;
// Check for import table
if let Some((import_rva, import_size)) = file.get_data_directory(DataDirectoryType::ImportTable) {
println!("Import table at RVA 0x{:x}, size: {} bytes", import_rva, import_size);
}
// Check for export table
if let Some((export_rva, export_size)) = file.get_data_directory(DataDirectoryType::ExportTable) {
println!("Export table at RVA 0x{:x}, size: {} bytes", export_rva, export_size);
}pub fn imports(&self) -> Option<&Vec<Import>>
pub fn imports(&self) -> Option<&Vec<Import>>
Returns the parsed import data from the PE file.
Uses goblin’s PE parsing to extract import table information including DLL dependencies and imported functions. Returns the parsed import data if an import directory exists.
§Returns
Some(imports)if import directory exists and was successfully parsedNoneif no import directory exists or parsing failed
§Examples
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.dll"))?;
if let Some(imports) = file.imports() {
for import in imports {
println!("DLL: {}", import.dll);
if let Some(ref name) = import.name {
if !name.is_empty() {
println!(" Function: {}", name);
}
} else if let Some(ordinal) = import.ordinal {
println!(" Ordinal: {}", ordinal);
}
}
}pub fn exports(&self) -> Option<&Vec<Export>>
pub fn exports(&self) -> Option<&Vec<Export>>
Returns the parsed export data from the PE file.
Uses goblin’s PE parsing to extract export table information including exported functions and their addresses. Returns the parsed export data if an export directory exists.
§Returns
Some(exports)if export directory exists and was successfully parsedNoneif no export directory exists or parsing failed
§Examples
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.dll"))?;
if let Some(exports) = file.exports() {
for export in exports {
if let Some(name) = &export.name {
println!("Export: {} -> 0x{:X}", name, export.rva);
}
}
}pub fn data(&self) -> &[u8] ⓘ
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_path(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]>
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_path(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>
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
// Convert entry point VA to file offset
let entry_point_va = file.header_optional().as_ref().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>
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_path(Path::new("tests/samples/WindowsBase.dll"))?;
// Convert CLR header RVA to file offset
let Some((clr_rva, _)) = file.clr() else {
panic!("No CLR header found");
};
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>
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_path(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);pub fn section_contains_metadata(&self, section_name: &str) -> bool
pub fn section_contains_metadata(&self, section_name: &str) -> bool
Determines if a section contains .NET metadata by checking the actual metadata RVA.
This method reads the CLR runtime header to get the metadata RVA and checks if it falls within the specified section’s address range. This is more accurate than name-based heuristics since metadata can technically be located in any section.
§Arguments
section_name- The name of the section to check (e.g., “.text”)
§Returns
Returns true if the section contains .NET metadata, false otherwise.
§Examples
use dotscope::File;
use std::path::Path;
let file = File::from_path(Path::new("example.dll"))?;
if file.section_contains_metadata(".text") {
println!("The .text section contains .NET metadata");
}pub fn file_alignment(&self) -> Result<u32>
pub fn file_alignment(&self) -> Result<u32>
Gets the file alignment value from the PE header.
This method extracts the file alignment value from the PE optional header. This is typically 512 bytes for most .NET assemblies.
§Returns
Returns the file alignment value in bytes.
§Errors
Returns crate::Error::LayoutFailed if the PE header cannot be accessed.
pub fn section_alignment(&self) -> Result<u32>
pub fn section_alignment(&self) -> Result<u32>
Gets the section alignment value from the PE header.
This method extracts the section alignment value from the PE optional header. This is typically 4096 bytes (page size) for most .NET assemblies.
§Returns
Returns the section alignment value in bytes.
§Errors
Returns crate::Error::LayoutFailed if the PE header cannot be accessed.
pub fn is_pe32_plus_format(&self) -> Result<bool>
pub fn is_pe32_plus_format(&self) -> Result<bool>
Determines if this is a PE32+ format file.
Returns true for PE32+ (64-bit) format, false for PE32 (32-bit) format.
This affects the size of ILT/IAT entries and ordinal import bit positions.
§Returns
Returns true if PE32+ format, false if PE32 format.
§Errors
Returns crate::Error::LayoutFailed if the PE format cannot be determined.
pub fn text_section_rva(&self) -> Result<u32>
pub fn text_section_rva(&self) -> Result<u32>
Gets the RVA of the .text section.
Locates the .text section (or .text-prefixed section) which typically contains .NET metadata and executable code.
§Returns
Returns the RVA (Relative Virtual Address) of the .text section.
§Errors
Returns crate::Error::LayoutFailed if no .text section is found.
pub fn text_section_file_offset(&self) -> Result<u64>
pub fn text_section_file_offset(&self) -> Result<u64>
Gets the file offset of the .text section.
This method finds the .text section in the PE file and returns its file offset. This is needed for calculating absolute file offsets for metadata components.
§Returns
Returns the file offset of the .text section.
§Errors
Returns crate::Error::LayoutFailed if no .text section is found.
pub fn text_section_raw_size(&self) -> Result<u32>
pub fn text_section_raw_size(&self) -> Result<u32>
Gets the raw size of the .text section.
This method finds the .text section and returns its raw data size. This is needed for calculating metadata expansion requirements.
§Returns
Returns the raw size of the .text section in bytes.
§Errors
Returns crate::Error::LayoutFailed if no .text section is found.
pub fn file_size(&self) -> u64
pub fn file_size(&self) -> u64
Gets the total size of the file.
Returns the size of the underlying file data in bytes.
§Returns
Returns the file size in bytes.
pub fn pe_signature_offset(&self) -> Result<u64>
pub fn pe_signature_offset(&self) -> Result<u64>
Gets the PE signature offset from the DOS header.
Reads the PE offset from the DOS header at offset 0x3C to locate the PE signature (“PE\0\0”) within the file.
§Returns
Returns the file offset where the PE signature is located.
§Errors
Returns crate::Error::LayoutFailed if the file is too small to contain
a valid DOS header.
pub fn pe_headers_size(&self) -> Result<u64>
pub fn pe_headers_size(&self) -> Result<u64>
Calculates the size of PE headers (including optional header).
Computes the total size of PE signature, COFF header, and optional header by reading the optional header size from the COFF header.
§Returns
Returns the total size in bytes of all PE headers.
§Errors
Returns crate::Error::LayoutFailed if the file is too small or
headers are malformed.
pub fn align_to_file_alignment(&self, offset: u64) -> Result<u64>
pub fn align_to_file_alignment(&self, offset: u64) -> Result<u64>
Aligns an offset to this file’s PE file alignment boundary.
PE files require data to be aligned to specific boundaries for optimal loading. This method uses the actual file alignment value from the PE header rather than assuming a hardcoded value.
§Arguments
offset- The offset to align
§Returns
Returns the offset rounded up to the next file alignment boundary.
§Errors
Returns crate::Error::LayoutFailed if the PE header cannot be accessed.
pub fn pe(&self) -> &Pe
pub fn pe(&self) -> &Pe
Returns a reference to the internal Pe structure.
This provides access to the owned PE data structures for operations that need to work directly with PE components, such as size calculations and header updates during write operations.
§Returns
Reference to the internal Pe structure
pub fn pe_mut(&mut self) -> &mut Pe
pub fn pe_mut(&mut self) -> &mut Pe
Returns a mutable reference to the internal Pe structure.
This provides mutable access to the owned PE data structures, enabling direct modifications to PE headers, sections, and data directories. Use this when you need to modify the PE structure in-place rather than creating copies.
§Returns
Mutable reference to the internal Pe structure
§Examples
// Add a new section to the PE file
let mut file = File::from_path(path)?;
let new_section = SectionTable::from_layout_info(
".meta".to_string(),
0x4000,
0x1000,
0x2000,
0x1000,
0x40000040,
)?;
file.pe_mut().add_section(new_section);
// Update CLR data directory
file.pe_mut().update_clr_data_directory(0x4000, 72)?;pub fn into_data(self) -> Vec<u8> ⓘ
pub fn into_data(self) -> Vec<u8> ⓘ
Consumes the File and returns the underlying data as an owned Vec
For files loaded via from_mem(), this transfers ownership without copying.
For files loaded via from_file(), this makes a complete copy of the memory-mapped data.
§Examples
use dotscope::File;
use std::path::Path;
// From memory - no copy
let original_data = vec![/* PE bytes */];
let file = File::from_mem(original_data)?;
let recovered_data = file.into_data();
// From file - copies the data
let file = File::from_path(Path::new("assembly.dll"))?;
let data_copy = file.into_data();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> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
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