Skip to main content

Module emulation

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

§Value System

§Memory Model

§Execution Engine

§Hook System

§Result Capture

§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

§Value System

§Memory Model

§Execution Engine

§Hook System

§Result Capture

§Loading

§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:

ComponentUsage
CFGStructured control flow during emulation
SSAValue tracking and constant propagation
Data FlowIdentifying which values need emulation
Call GraphDeciding 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§

AddressSpace
Unified address space for an emulated .NET process.
AppDomainState
Simulated application domain state.
ArgumentStorage
Storage for method arguments.
CaptureConfig
Configuration for what to capture during emulation.
CaptureContext
Central context for capturing emulation results during .NET analysis.
CaptureSource
Source location identifying where a capture occurred during emulation.
CapturedAssembly
A .NET assembly captured during dynamic loading.
CapturedBuffer
A raw byte buffer captured during emulation.
CapturedMethodReturn
A return value captured from a monitored method call.
CapturedString
A string captured during emulation.
DataLoader
Raw data loader for mapping arbitrary data into the address space.
EmulationConfig
Comprehensive emulation configuration with fine-grained control.
EmulationContext
Context for emulation providing access to assembly metadata.
EmulationController
High-level controller for CIL method emulation.
EmulationLimits
Limits for emulation execution.
EmulationProcess
Central emulation process orchestrating .NET execution.
EmulationThread
Per-thread execution state for .NET emulation.
EvaluationStack
CIL evaluation stack with overflow protection.
EventState
State of an event synchronization primitive.
ExceptionHandler
Exception handler resolver for .NET exception handling.
ExceptionInfo
Information about a thrown exception.
FakeObjects
Pre-allocated fake BCL objects for consistent emulation behavior.
FileOperation
A file system operation captured during emulation.
FrameSearchInfo
Information about a single stack frame for exception handler search.
HandlerSearchState
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.
HookContext
Context passed to hooks during execution.
HookManager
Manager for registering and executing hooks.
HookPriority
Priority level for hooks, controlling evaluation order.
InstructionLocation
A location within a method’s IL instruction stream.
InstructionPointer
Tracks the current execution position during emulation.
InternalMethodMatcher
Matches only internal methods (MethodDef, not MemberRef).
Interpreter
Core CIL instruction interpreter.
LoadedAssemblyInfo
Information about a loaded assembly in the application domain.
LoadedImage
Metadata about a PE image that has been loaded into the address space.
LoadedSection
Metadata about an individual PE section that was loaded into memory.
LocalVariables
Storage for method local variables.
ManagedHeap
Simulated managed heap for CIL emulation.
ManagedPointer
Managed pointer for ref/out parameters and address-of operations.
MappedRegionInfo
Information about a mapped data region in the emulation address space.
MemoryConfig
Memory subsystem configuration.
MemoryProtection
Memory protection flags for address space regions.
MemoryRegion
A memory region in the emulated address space.
MemorySnapshot
A point-in-time snapshot of memory regions.
MonitorState
Monitor lock state for an object.
MutexState
Mutex synchronization primitive state.
NameMatcher
Matches methods by namespace, type name, and/or method name.
NativeMethodMatcher
Matches P/Invoke (native) method calls by DLL name and/or function name.
NetworkOperation
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.
PeLoaderConfig
Configuration options for PE image loading.
PendingFinally
A pending finally block queued for execution.
ProcessBuilder
Builder for creating and configuring emulation processes.
ProcessSummary
Summary snapshot of an emulation process state.
RuntimeMatcher
Matches methods based on runtime argument inspection.
RuntimeState
Central runtime state for .NET emulation.
RuntimeStateBuilder
Builder for constructing RuntimeState with custom configuration.
SectionInfo
Information about a PE section within a loaded image.
SemaphoreState
Counting semaphore synchronization primitive state.
SharedFakeObjects
Shared fake objects wrapper for process-wide sharing.
SharedHeap
Shared managed heap wrapper for thread-safe heap access.
SignatureMatcher
Matches methods by their parameter and return types.
StackTraceEntry
Stack trace entry for exception and error reporting.
StackUnwinder
Stack unwinder for exception propagation.
StaticFieldStorage
Storage for static fields in the emulated process.
StubConfig
Hook registration configuration.
SymbolicValue
A symbolic (unknown) value for partial emulation.
SyncState
Central state manager for all synchronization primitives.
ThreadCallFrame
A call frame on the thread’s call stack.
ThreadExceptionState
Per-thread exception handling state.
ThreadId
Unique identifier for a thread in the emulated process.
ThreadScheduler
Cooperative thread scheduler for managing emulation threads.
TraceWriter
A writer for trace events.
TracingConfig
Tracing and logging configuration.
UnmanagedMemory
Simulated unmanaged memory for tracking raw byte allocations.
UnmanagedRef
A handle to an unmanaged memory region.
UnwindSequenceBuilder
Builder for constructing ordered exception handler sequences.

Enums§

BinaryOp
Binary operations for CIL arithmetic and bitwise instructions.
BufferSource
Describes the origin of a captured buffer.
CompareOp
Comparison operations for CIL instructions.
ConversionType
Type conversion operations for CIL instructions.
EmValue
Runtime value during CIL emulation.
EmulationError
Errors that can occur during CIL emulation.
EmulationOutcome
Final outcome of emulation.
EncodingType
Text encoding type for System.Text.Encoding stubs.
ExceptionClause
An exception handling clause from .NET method metadata.
FileOpKind
The type of file system operation performed.
HandlerMatch
Result of searching for an exception handler.
HeapObject
Object stored on the managed heap.
HookOutcome
Outcome of hook execution via HookManager::execute.
LimitExceeded
Indicates which limit was exceeded.
LimitKind
Describes which execution limit was reached.
MethodHandlerResult
Result of searching for an exception handler within a single method.
NetworkOpKind
The type of network operation performed.
PointerTarget
Target of a managed pointer.
PostHookResult
Result of executing a post-hook.
PreHookResult
Result of executing a pre-hook.
SchedulerOutcome
Outcome of a scheduler step or run operation.
StepResult
Result of executing a single instruction.
SyncError
Errors from synchronization operations.
TaintSource
Source/origin of a symbolic value for taint tracking.
ThreadPriority
Thread priority levels for scheduling.
ThreadState
Thread execution state.
TraceEvent
A trace event recorded during emulation.
UnaryOp
Unary operations for CIL instructions.
UnknownMethodBehavior
Behavior when encountering methods without registered hooks.
UnwindStepResult
Result of a single step in the unwind process.
WaitReason
Reason a thread is blocked in the ThreadState::Waiting state.
WakeCondition
Condition for waking blocked threads.

Constants§

PAGE_SIZE
Standard page size (4KB).

Traits§

HookMatcher
Trait for implementing hook matchers.

Type Aliases§

PostHookFn
Type alias for post-hook functions.
PreHookFn
Type alias for pre-hook functions.