9 releases (4 breaking)
| new 0.5.2 | Dec 31, 2025 |
|---|---|
| 0.5.1 | Nov 29, 2025 |
| 0.4.0 | Aug 19, 2025 |
| 0.3.2 | Jun 17, 2025 |
| 0.1.0 | Jun 8, 2025 |
#419 in Parser implementations
10MB
125K
SLoC
Contains (Windows DLL, 2.5MB) tests/samples/WindowsBase.dll, (Windows DLL, 1MB) tests/samples/mono_4.8/System.Web.dll, (Windows DLL, 755KB) System.Windows.Forms.dll, (Windows DLL, 1MB) tests/samples/mono_4.8/mscorlib.dll, (Windows DLL, 325KB) RabbitMQ.Client.dll, (Windows DLL, 525KB) System.ServiceModel.dll and 237 more.
dotscope
A high-performance, cross-platform framework for analyzing, reverse engineering, and modifying .NET PE executables. Built in pure Rust, dotscope provides comprehensive tooling for parsing CIL (Common Intermediate Language) bytecode, metadata structures, disassembling .NET assemblies, and creating modified assemblies without requiring Windows or the .NET runtime.
Features
- Efficient memory access - Memory-mapped file access with minimal allocations and reference-based parsing
- Complete metadata analysis - Parse all ECMA-335 metadata tables and streams
- Assembly modification - Edit metadata tables, heaps, and PE structures with validation and integrity checking
- Method injection - Add new methods, classes, and metadata to existing assemblies with high-level builders
- High-performance disassembly - Fast CIL instruction decoding with control flow analysis
- CIL encoding - Generate CIL bytecode with label-based exception handling for method modification
- Native PE operations - Manage imports, exports, and native interoperability features
- Cross-platform - Works on Windows, Linux, macOS, and any Rust-supported platform
- Memory safe - Built in Rust with comprehensive error handling and fuzzing
- Rich type system - Full support for generics, signatures, and complex .NET types
- Extensible architecture - Modular design for custom analysis and tooling
Quick Start
Add dotscope to your Cargo.toml:
[dependencies]
dotscope = "0.5.1"
Raw Access Example
use dotscope::prelude::*;
fn main() -> dotscope::Result<()> {
// Load assembly for raw access
let view = CilAssemblyView::from_path("MyAssembly.dll".as_ref())?;
// Direct access to metadata tables
if let Some(tables) = view.tables() {
let typedef_count = tables.table_row_count(TableId::TypeDef);
println!("TypeDef rows: {}", typedef_count);
}
// Direct heap access
if let Some(strings) = view.strings() {
for (index, string) in strings.iter().take(5) {
println!("String {}: {}", index, string);
}
}
Ok(())
}
Analysis Example
use dotscope::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Load assembly for high-level analysis
let assembly = CilObject::from_path("MyAssembly.dll".as_ref())?;
// Access resolved information
if let Some(module) = assembly.module() {
println!("Module: {}", module.name);
}
// Iterate through resolved methods with type information
let methods = assembly.methods();
println!("Found {} methods", methods.len());
// Examine resolved imports and exports
let imports = assembly.imports();
let exports = assembly.exports();
println!("Imports: {}, Exports: {}", imports.len(), exports.len());
Ok(())
}
Assembly Modification Example
use dotscope::prelude::*;
fn main() -> dotscope::Result<()> {
// Load assembly for modification
let view = CilAssemblyView::from_path("input.dll".as_ref())?;
let mut assembly = CilAssembly::new(view);
// Add strings to metadata heaps
let string_index = assembly.string_add("Hello from dotscope!")?;
let user_string_index = assembly.userstring_add("Modified assembly")?;
// Add native imports
assembly.add_native_import_dll("kernel32.dll")?;
assembly.add_native_import_function("kernel32.dll", "GetProcessId")?;
// Validate and write modified assembly
assembly.validate_and_apply_changes()?;
assembly.write_to_file("output.dll".as_ref())?;
Ok(())
}
Method Builder Example
use dotscope::prelude::*;
fn main() -> dotscope::Result<()> {
// Load assembly and create builder context
let view = CilAssemblyView::from_path("input.dll".as_ref())?;
let assembly = CilAssembly::new(view);
let mut context = BuilderContext::new(assembly);
// Add a user string
let msg_index = context.userstring_add("Hello World!")?;
let msg_token = Token::new(0x70000000 | msg_index);
// Create method with CIL instructions
let method_token = MethodBuilder::new("MyNewMethod")
.public()
.static_method()
.returns(TypeSignature::Void)
.implementation(|body| {
body.implementation(|asm| {
asm.ldstr(msg_token)?
.pop()? // Simple example: load string then pop it
.ret()
})
})
.build(&mut context)?;
// Save the modified assembly
let mut assembly = context.finish();
assembly.write_to_file("output.dll".as_ref())?;
Ok(())
}
Project Loader Example
use dotscope::project::ProjectLoader;
fn main() -> dotscope::Result<()> {
// Load assembly with automatic dependency resolution
let result = ProjectLoader::new()
.primary_file("MyApp.exe")?
.with_search_path("/usr/lib/mono/4.5")?
.auto_discover(true)
.build()?;
println!("Loaded {} assemblies", result.success_count());
// Access the project with all loaded assemblies
let project = &result.project;
// Get the primary assembly
if let Some(primary) = project.get_primary() {
println!("Types: {}", primary.types().len());
println!("Methods: {}", primary.methods().len());
}
// Cross-assembly type lookup
if let Some(string_type) = project.get_type_by_name("System.String") {
println!("Found System.String with {} methods", string_type.methods.count());
}
// Find type definitions across all assemblies
let object_types = project.find_type_definitions("Object");
println!("Found {} types matching 'Object'", object_types.len());
Ok(())
}
Documentation
- API Documentation - Complete API reference
- Examples - Working examples for common use cases
- Contributing Guide - How to contribute to the project
- Security Policy - Security reporting and policy
Architecture
dotscope is organized into several key modules:
Core Components
prelude- Convenient re-exports of commonly used typesmetadata- Complete ECMA-335 metadata parsing and type systemcilassembly- Assembly modification with copy-on-write semantics and high-level buildersassembly- CIL instruction encoding/decoding, control flow analysis, and method body constructionErrorandResult- Comprehensive error handling
Raw Access (CilAssemblyView)
Low-level access to assembly structures provides:
- Direct PE parsing: Raw access to PE headers, sections, and data directories
- Metadata streams: Direct heap access without object resolution
- Table iteration: Raw table row access with manual index resolution
- Memory-mapped data: Efficient access to assembly contents
- Foundation layer: Base for both analysis and modification operations
Analysis (CilObject)
High-level analysis with resolved objects provides:
- Resolved references: Automatic cross-reference resolution and object graphs
- Type system: Rich representation of .NET types, generics, and inheritance
- Method bodies: Parsed IL instructions with operand resolution
- Import/export analysis: Resolved dependency and export information
- Convenience APIs: Easy-to-use interfaces for common analysis tasks
Modification (CilAssembly)
Mutable assembly editing provides:
- Heap operations: Add, update, remove items from all metadata heaps
- Table operations: Add, update, delete metadata table rows with validation
- PE operations: Manage native imports, exports, and forwarders
- Builder APIs: High-level builders for adding classes, methods, properties, events, and enums to existing assemblies
- CIL Generation: Full CIL instruction encoding with label resolution and exception handling for method modification
- Validation: Comprehensive integrity checking and reference resolution
Assembly Engine
The assembly module provides comprehensive CIL processing:
Decoding & Analysis:
- Instruction Decoding: Parse individual CIL opcodes with full operand support
- Control Flow Analysis: Build basic blocks and control flow graphs
- Stack Analysis: Track stack effects and type flow
- Exception Handling: Parse and analyze try/catch/finally regions
Encoding & Generation:
- Instruction Encoding: Generate CIL bytecode from high-level instructions
- Label Resolution: Automatic branch target and exception handler resolution
- Method Body Construction: Build complete method bodies with local variables and exception handling
- Assembly Modification: Fluent API for adding new components to existing .NET assemblies
Examples
Check out the examples directory for complete working examples with comprehensive documentation:
- Basic Usage - Start here! Simple assembly loading and inspection with error handling
- Assembly Modification - Complete guide to editing assemblies with heap and table operations
- Metadata Analysis - Deep dive into assembly metadata and dependency tracking
- Disassembly - CIL instruction disassembly and method body analysis
- Type System - Working with .NET types, generics, and inheritance
- Comprehensive Analysis - Full-featured analysis combining all capabilities
- Method Analysis - Exhaustive single-method inspection
- Low-Level API - Understanding dotscope internals and raw parsing
- Control Flow - Basic block construction and flow analysis
- Code Injection - Injecting new methods into existing assemblies with MethodBuilder
- Raw Assembly View - Direct access to PE headers, metadata streams, and heaps
- Project Loader - Loading assemblies with automatic dependency resolution
Each example includes detailed documentation explaining:
- What it teaches - Key learning objectives and concepts
- When to use - Practical applications and use cases
- Prerequisites - Required background knowledge
- API patterns - Consistent, production-ready code examples
See the examples README for a recommended learning path.
Use Cases
dotscope is perfect for:
- Reverse Engineering: Analyze .NET malware and vulnerable software
- Security Research: Find vulnerabilities and security issues
- Assembly Patching: Modify assemblies for instrumentation, hooking, or enhancement
- Code Analysis: Static analysis and quality metrics
- Decompilation: Build decompilers and analysis tools
- Development Tools: Create assembly editors, analyzers, and build tools
- Educational: Learn about .NET internals and PE format
- Forensics: Examine .NET assemblies in digital forensics
Security
Security is a top priority:
- Memory Safety: Built on Rust's memory safety guarantees
- Fuzzing: Continuous fuzzing with cargo-fuzz
- Input Validation: Strict validation of all inputs
- Audit Trail: Regular dependency auditing
See our Security Policy for more information.
Standards Compliance
dotscope implements the ECMA-335 specification (6th edition) for the Common Language Infrastructure. All metadata structures, CIL instructions, and type system features conform to this standard.
References
- ECMA-335 Standard - Official CLI specification
- .NET Runtime - Microsoft's reference implementation
Testing and Quality
We maintain high code quality through:
- Comprehensive Test Suite: Unit, integration, and fuzz testing
- Continuous Integration: Automated testing on multiple platforms
- Code Coverage: >90% test coverage target
- Static Analysis: Clippy, rustfmt, and audits
- Performance Testing: Regular benchmarking and regression detection
Running Tests
# Development cycle (recommended for frequent use)
make dev # Format, lint, and test
# Full CI simulation
make ci # Complete CI checks
# Security and quality
make audit # Security audit
make coverage # Generate coverage report
Extended Testing
# Local fuzzing (60 seconds)
make fuzz
# Extended fuzzing (manual)
cd fuzz && cargo +nightly fuzz run cilobject --release -- -max_total_time=1800
# All quality checks
make check-all
Future Features
We're continuously working to improve dotscope and add new capabilities. Here are features we'd like to implement in the future:
Core Improvements
- Handling U/I (compilation dependend 64bit or 32bit) properly
- Improve correctness and API design
- Improve documentation and examples
- Add protections against large allocations (e.g. maliciously crafted files that aim to exhaust system memory)
- Improve type system hash calculations for deduplication
- Standard trait implementations (Debug, Display, Clone, etc.)
- Debug logging infrastructure
- Ecosystem integration improvements
Enhanced Parsing and Security
- String/Blob caching infrastructure
- Non-embedded resource support
Performance and Scalability
- Assembly linking and merging
- Store and load full Assembly to/from JSON
Advanced Analysis
- Control flow graph generation
- Data flow analysis
- Call graph construction
- Emulation engine
Deobfuscation
- SSA (Static Single Assignment) generation
- Compiler optimizations applied to IL (dead code elimination, opaque predicate removal, etc.)
- String decryption capabilities
Contributing
We welcome contributions! Please see our Contributing Guide for details.
Quick Contribution Checklist
- Check existing issues and PRs
- Write tests for new features
- Update documentation
- Ensure CI passes
- Follow commit message conventions
License
This project is licensed under the Apache License, Version 2.0.
See LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0 for details.
Acknowledgments
- The Rust community for excellent tooling and libraries
- Microsoft for the ECMA-335 specification
- The goblin project for PE parsing inspiration
Support
- Bug Reports: GitHub Issues
- Feature Requests: GitHub Issues
- Questions: GitHub Discussions
- Security Issues: admin{at}binflip.rs
Dependencies
~9–14MB
~280K SLoC