Module emulation
Expand description
CIL emulation engine for .NET bytecode execution.
This module provides a controlled execution environment for .NET CIL bytecode. The emulation engine is essential for deobfuscation as many obfuscators rely on runtime computation of values and dynamic string decryption.
§Key Components
§Process Model
emulation::ProcessBuilder- Fluent API for configuring emulation processesemulation::EmulationProcess- Central coordinator for emulation executionemulation::EmulationConfig- Configuration with presets (for_extraction,for_analysis, etc.)
§Value System
emulation::EmValue- Runtime value representation for all CIL typesemulation::SymbolicValue- Tracks unknown/unresolved values during partial emulationemulation::HeapRef- Reference to heap-allocated objects
§Memory Model
emulation::EvaluationStack- CIL evaluation stack with overflow protectionemulation::LocalVariables- Method local variable storageemulation::ManagedHeap- Simulated managed heap for object allocationemulation::AddressSpace- Unified address space for heap, statics, and mapped regions
§Execution Engine
emulation::Interpreter- Core CIL instruction interpreteremulation::EmulationController- High-level execution control with limitsemulation::StepResult- Result of executing a single instructionemulation::EmulationOutcome- Final result of method execution (return value, exception, or limit)
§Hook System
emulation::Hook- Builder for creating method hooks with matching criteriaemulation::HookManager- Registry for method interception hooksemulation::PreHookResult- Pre-hook result:ContinueorBypass(value)emulation::PostHookResult- Post-hook result:KeeporReplace(value)
§Result Capture
emulation::CaptureContext- Automatic result collection during emulationemulation::CapturedAssembly- CapturedAssembly.Loaddataemulation::CapturedString- Captured decrypted strings
§Usage Examples
§Value Arithmetic
use dotscope::emulation::{EmValue, BinaryOp};
use dotscope::metadata::typesystem::{CilFlavor, PointerSize};
// CIL values follow ECMA-335 widening rules (I1/I2 → I32)
let a = EmValue::I32(10);
let b = EmValue::I32(3);
let sum = a.binary_op(&b, BinaryOp::Add, PointerSize::Bit64).unwrap();
assert_eq!(sum, EmValue::I32(13));
assert_eq!(sum.cil_flavor(), CilFlavor::I4);
// Division, bitwise, and comparison operations
let div = a.binary_op(&b, BinaryOp::Div, PointerSize::Bit64).unwrap();
assert_eq!(div, EmValue::I32(3));
let xor = a.binary_op(&b, BinaryOp::Xor, PointerSize::Bit64).unwrap();
assert_eq!(xor, EmValue::I32(10 ^ 3));§Building an Emulation Process
emulation::ProcessBuilder provides a fluent API with configuration presets:
use dotscope::emulation::ProcessBuilder;
use dotscope::CilObject;
use std::path::Path;
let assembly = CilObject::from_path(Path::new("target.exe"))?;
let pe_bytes = std::fs::read("target.exe")?;
let process = ProcessBuilder::new()
.assembly(assembly)
.map_pe_image(&pe_bytes, "target.exe")
.for_extraction() // 50M instruction limit, capture enabled
.capture_assemblies() // Capture Assembly.Load calls
.capture_strings() // Capture decrypted strings
.with_timeout_ms(30_000) // 30 second wall-clock timeout
.build()?;Available presets: for_extraction() (unpacking), for_analysis() (symbolic tracking),
for_full_emulation() (100M instructions), for_minimal() (constant folding, 10K instructions).
§Executing a Method and Reading Results
use dotscope::emulation::{EmValue, EmulationOutcome, ProcessBuilder};
use dotscope::CilObject;
use std::path::Path;
// Find and execute a specific method by type/method name
if let Some(token) = process.find_method("MyNamespace.MyClass", "Decrypt") {
let outcome = process.execute_method(token, vec![EmValue::I32(42)])?;
match outcome {
EmulationOutcome::Completed { return_value, instructions } => {
println!("Completed in {} instructions", instructions);
if let Some(value) = return_value {
println!("Returned: {:?}", value);
}
}
EmulationOutcome::LimitReached { limit, .. } => {
println!("Hit limit: {:?}", limit);
}
EmulationOutcome::UnhandledException { exception, .. } => {
println!("Exception: {:?}", exception);
}
_ => {}
}
}
// Retrieve captured data after execution
for asm in process.captured_assemblies() {
if let Some(name) = &asm.name {
std::fs::write(name, &asm.data)?;
}
}
for s in process.captured_strings() {
println!("Decrypted string: {}", s.value);
}§Method Hooks
Hooks intercept method calls during emulation. Use them to stub BCL methods, bypass protection checks, or capture intermediate values:
use dotscope::emulation::{Hook, PreHookResult, EmValue, ProcessBuilder};
use dotscope::CilObject;
use std::path::Path;
let process = ProcessBuilder::new()
.assembly(assembly)
.for_analysis()
// Bypass Environment.Exit so emulation continues
.hook(
Hook::new("bypass-exit")
.match_name("System", "Environment", "Exit")
.pre(|_ctx, _thread| PreHookResult::Bypass(None))
)
// Intercept DateTime.Now to return a fixed value
.hook(
Hook::new("fixed-time")
.match_name("System", "DateTime", "get_Now")
.pre(|_ctx, _thread| PreHookResult::Bypass(Some(EmValue::I64(0))))
)
.build()?;§Process Forking
Forking creates a lightweight copy of the emulation state, allowing you to run the same setup against many different inputs efficiently:
use dotscope::emulation::{EmValue, ProcessBuilder};
use dotscope::CilObject;
use std::path::Path;
// Expensive setup: load assembly, map PE, configure hooks
let base_process = ProcessBuilder::new()
.assembly(assembly)
.map_pe_image(&pe_bytes, "target.exe")
.for_extraction()
.build()?;
// Cheap forks share the base state via copy-on-write
if let Some(decryptor) = base_process.find_method("Decryptor", "Decrypt") {
for key in 0..100i32 {
let fork = base_process.fork();
let outcome = fork.execute_method(decryptor, vec![EmValue::I32(key)])?;
// Each fork runs independently without affecting the base process
}
}CIL emulation engine for .NET bytecode execution.
This module provides a controlled execution environment for .NET CIL (Common Intermediate Language) bytecode. The emulation engine is essential for deobfuscation as many obfuscators rely on runtime computation of values, dynamic string decryption, and control flow obfuscation that can only be resolved through execution.
§Architecture
The emulation engine is organized into several sub-modules:
- Runtime value representation with type safety and symbolic tracking
- Memory model including evaluation stack, locals, and managed heap
- Core interpreter and execution controller
- Hook system for intercepting method calls and providing custom behavior
- Process model for coordinating emulation
- Capture context for collecting results
§Key Components
§Process Model
crate::emulation::EmulationProcess- Central emulation process coordinating all componentscrate::emulation::ProcessBuilder- Fluent API for configuring emulation processescrate::emulation::EmulationConfig- Comprehensive configuration with presets
§Value System
crate::emulation::EmValue- Runtime value representation for all CIL typescrate::emulation::SymbolicValue- Tracks unknown/unresolved values during partial emulationcrate::emulation::HeapRef- Reference to heap-allocated objects
§Memory Model
crate::emulation::EvaluationStack- CIL evaluation stack with overflow protectioncrate::emulation::LocalVariables- Local variable storage with type-aware initializationcrate::emulation::ManagedHeap- Simulated managed heap with memory limitscrate::emulation::AddressSpace- Process-wide memory management
§Execution Engine
crate::emulation::Interpreter- Core instruction interpretercrate::emulation::EmulationController- High-level execution control with limitscrate::emulation::StepResult- Result of executing a single instruction
§Hook System
crate::emulation::HookManager- Registry for method interception hookscrate::emulation::Hook- Builder for creating method hookscrate::emulation::HookContext- Information passed to hook handlers
§Result Capture
crate::emulation::CaptureContext- Automatic result collectioncrate::emulation::CapturedAssembly- Captured Assembly.Load datacrate::emulation::CapturedString- Captured decrypted strings
§Loading
crate::emulation::PeLoader- PE image loader for memory mappingcrate::emulation::DataLoader- Raw data loader
§Usage Examples
§Process-Based Emulation
use dotscope::emulation::ProcessBuilder;
use dotscope::CilObject;
let assembly = CilObject::from_path("sample.exe")?;
let pe_bytes = std::fs::read("sample.exe")?;
let mut process = ProcessBuilder::new()
.assembly(assembly)
.map_pe_image(&pe_bytes, "sample.exe")
.for_extraction()
.capture_assemblies()
.build()?;
// Get captured assemblies after execution
for (idx, asm) in process.captured_assemblies().iter().enumerate() {
std::fs::write(format!("extracted_{}.dll", idx), &asm.data)?;
}§String Decryption Pattern
use dotscope::emulation::ProcessBuilder;
let process = ProcessBuilder::new()
.assembly(assembly)
.for_analysis()
.capture_strings()
.build()?;
// After emulation
for string in process.captured_strings() {
println!("Decrypted: {}", string.value);
}§Integration with Analysis
The emulation engine integrates with the analysis infrastructure:
| Component | Usage |
|---|---|
| CFG | Structured control flow during emulation |
| SSA | Value tracking and constant propagation |
| Data Flow | Identifying which values need emulation |
| Call Graph | Deciding method inlining/hooking |
§Execution Limits
The emulator enforces several limits to prevent runaway execution:
- Instruction limit: Maximum instructions to execute
- Call depth limit: Maximum call stack depth
- Memory limit: Maximum heap allocation
- Timeout: Wall-clock time limit
§Thread Safety
The emulation types are designed for single-threaded use within an analysis
context. The crate::emulation::EmulationProcess and related types are Send but not
Sync, as emulation inherently involves mutable state.
Modules§
- synthetic_
exception - Synthetic exception type tokens for CLR exceptions.
Structs§
- Address
Space - Unified address space for an emulated .NET process.
- AppDomain
State - Simulated application domain state.
- Argument
Storage - Storage for method arguments.
- Capture
Config - Configuration for what to capture during emulation.
- Capture
Context - Central context for capturing emulation results during .NET analysis.
- Capture
Source - Source location identifying where a capture occurred during emulation.
- Captured
Assembly - A .NET assembly captured during dynamic loading.
- Captured
Buffer - A raw byte buffer captured during emulation.
- Captured
Method Return - A return value captured from a monitored method call.
- Captured
String - A string captured during emulation.
- Data
Loader - Raw data loader for mapping arbitrary data into the address space.
- Emulation
Config - Comprehensive emulation configuration with fine-grained control.
- Emulation
Context - Context for emulation providing access to assembly metadata.
- Emulation
Controller - High-level controller for CIL method emulation.
- Emulation
Limits - Limits for emulation execution.
- Emulation
Process - Central emulation process orchestrating .NET execution.
- Emulation
Thread - Per-thread execution state for .NET emulation.
- Evaluation
Stack - CIL evaluation stack with overflow protection.
- Event
State - State of an event synchronization primitive.
- Exception
Handler - Exception handler resolver for .NET exception handling.
- Exception
Info - Information about a thrown exception.
- Fake
Objects - Pre-allocated fake BCL objects for consistent emulation behavior.
- File
Operation - A file system operation captured during emulation.
- Frame
Search Info - Information about a single stack frame for exception handler search.
- Handler
Search State - State for multi-frame exception handler search across the call stack.
- HeapRef
- Reference to a heap-allocated object.
- Hook
- A configurable hook for method interception.
- Hook
Context - Context passed to hooks during execution.
- Hook
Manager - Manager for registering and executing hooks.
- Hook
Priority - Priority level for hooks, controlling evaluation order.
- Instruction
Location - A location within a method’s IL instruction stream.
- Instruction
Pointer - Tracks the current execution position during emulation.
- Internal
Method Matcher - Matches only internal methods (MethodDef, not MemberRef).
- Interpreter
- Core CIL instruction interpreter.
- Loaded
Assembly Info - Information about a loaded assembly in the application domain.
- Loaded
Image - Metadata about a PE image that has been loaded into the address space.
- Loaded
Section - Metadata about an individual PE section that was loaded into memory.
- Local
Variables - Storage for method local variables.
- Managed
Heap - Simulated managed heap for CIL emulation.
- Managed
Pointer - Managed pointer for ref/out parameters and address-of operations.
- Mapped
Region Info - Information about a mapped data region in the emulation address space.
- Memory
Config - Memory subsystem configuration.
- Memory
Protection - Memory protection flags for address space regions.
- Memory
Region - A memory region in the emulated address space.
- Memory
Snapshot - A point-in-time snapshot of memory regions.
- Monitor
State - Monitor lock state for an object.
- Mutex
State - Mutex synchronization primitive state.
- Name
Matcher - Matches methods by namespace, type name, and/or method name.
- Native
Method Matcher - Matches P/Invoke (native) method calls by DLL name and/or function name.
- Network
Operation - A network operation captured during emulation.
- Page
- A memory page with copy-on-write semantics.
- PeLoader
- PE image loader for mapping Windows executables into the emulation address space.
- PeLoader
Config - Configuration options for PE image loading.
- Pending
Finally - A pending finally block queued for execution.
- Process
Builder - Builder for creating and configuring emulation processes.
- Process
Summary - Summary snapshot of an emulation process state.
- Runtime
Matcher - Matches methods based on runtime argument inspection.
- Runtime
State - Central runtime state for .NET emulation.
- Runtime
State Builder - Builder for constructing
RuntimeStatewith custom configuration. - Section
Info - Information about a PE section within a loaded image.
- Semaphore
State - Counting semaphore synchronization primitive state.
- Shared
Fake Objects - Shared fake objects wrapper for process-wide sharing.
- Shared
Heap - Shared managed heap wrapper for thread-safe heap access.
- Signature
Matcher - Matches methods by their parameter and return types.
- Stack
Trace Entry - Stack trace entry for exception and error reporting.
- Stack
Unwinder - Stack unwinder for exception propagation.
- Static
Field Storage - Storage for static fields in the emulated process.
- Stub
Config - Hook registration configuration.
- Symbolic
Value - A symbolic (unknown) value for partial emulation.
- Sync
State - Central state manager for all synchronization primitives.
- Thread
Call Frame - A call frame on the thread’s call stack.
- Thread
Exception State - Per-thread exception handling state.
- Thread
Id - Unique identifier for a thread in the emulated process.
- Thread
Scheduler - Cooperative thread scheduler for managing emulation threads.
- Trace
Writer - A writer for trace events.
- Tracing
Config - Tracing and logging configuration.
- Unmanaged
Memory - Simulated unmanaged memory for tracking raw byte allocations.
- Unmanaged
Ref - A handle to an unmanaged memory region.
- Unwind
Sequence Builder - Builder for constructing ordered exception handler sequences.
Enums§
- Binary
Op - Binary operations for CIL arithmetic and bitwise instructions.
- Buffer
Source - Describes the origin of a captured buffer.
- Compare
Op - Comparison operations for CIL instructions.
- Conversion
Type - Type conversion operations for CIL instructions.
- EmValue
- Runtime value during CIL emulation.
- Emulation
Error - Errors that can occur during CIL emulation.
- Emulation
Outcome - Final outcome of emulation.
- Encoding
Type - Text encoding type for
System.Text.Encodingstubs. - Exception
Clause - An exception handling clause from .NET method metadata.
- File
OpKind - The type of file system operation performed.
- Handler
Match - Result of searching for an exception handler.
- Heap
Object - Object stored on the managed heap.
- Hook
Outcome - Outcome of hook execution via
HookManager::execute. - Limit
Exceeded - Indicates which limit was exceeded.
- Limit
Kind - Describes which execution limit was reached.
- Method
Handler Result - Result of searching for an exception handler within a single method.
- Network
OpKind - The type of network operation performed.
- Pointer
Target - Target of a managed pointer.
- Post
Hook Result - Result of executing a post-hook.
- PreHook
Result - Result of executing a pre-hook.
- Scheduler
Outcome - Outcome of a scheduler step or run operation.
- Step
Result - Result of executing a single instruction.
- Sync
Error - Errors from synchronization operations.
- Taint
Source - Source/origin of a symbolic value for taint tracking.
- Thread
Priority - Thread priority levels for scheduling.
- Thread
State - Thread execution state.
- Trace
Event - A trace event recorded during emulation.
- UnaryOp
- Unary operations for CIL instructions.
- Unknown
Method Behavior - Behavior when encountering methods without registered hooks.
- Unwind
Step Result - Result of a single step in the unwind process.
- Wait
Reason - Reason a thread is blocked in the
ThreadState::Waitingstate. - Wake
Condition - Condition for waking blocked threads.
Constants§
- PAGE_
SIZE - Standard page size (4KB).
Traits§
- Hook
Matcher - Trait for implementing hook matchers.
Type Aliases§
- Post
Hook Fn - Type alias for post-hook functions.
- PreHook
Fn - Type alias for pre-hook functions.