use std::{ops::Range, path::PathBuf};
use crate::metadata::typesystem::PointerSize;
#[derive(Clone, Debug)]
pub struct EmulationConfig {
pub limits: EmulationLimits,
pub unknown_method: UnknownMethodBehavior,
pub symbolic_tracking: bool,
pub threading_enabled: bool,
pub thread_quantum: usize,
pub max_threads: usize,
pub exception_handling: bool,
pub allow_unwind: bool,
pub pointer_size: PointerSize,
pub memory: MemoryConfig,
pub stubs: StubConfig,
pub tracing: TracingConfig,
}
#[derive(Clone, Debug)]
pub struct EmulationLimits {
pub max_instructions: u64,
pub max_call_depth: usize,
pub max_heap_objects: usize,
pub max_heap_bytes: usize,
pub max_unmanaged_bytes: usize,
pub timeout_ms: u64,
}
#[derive(Clone, Debug)]
pub struct MemoryConfig {
pub initial_heap_size: usize,
pub max_heap_size: usize,
pub stack_size: usize,
pub address_space_bits: u8,
pub track_access: bool,
pub zero_init: bool,
}
#[derive(Clone, Debug)]
pub struct StubConfig {
pub bcl_stubs: bool,
pub pinvoke_stubs: bool,
pub reflection_stubs: bool,
pub crypto_stubs: bool,
pub io_stubs: bool,
pub threading_stubs: bool,
pub strict_mode: bool,
}
#[derive(Clone, Debug)]
pub struct TracingConfig {
pub trace_instructions: bool,
pub trace_calls: bool,
pub trace_heap: bool,
pub trace_exceptions: bool,
pub trace_stubs: bool,
pub trace_array_ops: bool,
pub max_trace_entries: usize,
pub output_path: Option<PathBuf>,
pub context_prefix: Option<String>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UnknownMethodBehavior {
#[default]
Emulate,
Symbolic,
Fail,
Default,
Skip,
}
#[derive(Clone, Debug, Default)]
pub struct CaptureConfig {
pub assemblies: bool,
pub memory_regions: Vec<Range<u64>>,
pub strings: bool,
pub file_operations: bool,
pub network_operations: bool,
}
impl Default for EmulationConfig {
fn default() -> Self {
Self {
limits: EmulationLimits::default(),
unknown_method: UnknownMethodBehavior::Symbolic,
symbolic_tracking: false,
threading_enabled: true,
thread_quantum: 1000,
max_threads: 16,
exception_handling: true,
allow_unwind: true,
pointer_size: PointerSize::Bit64,
memory: MemoryConfig::default(),
stubs: StubConfig::default(),
tracing: TracingConfig::default(),
}
}
}
impl Default for EmulationLimits {
fn default() -> Self {
Self {
max_instructions: 10_000_000,
max_call_depth: 1000,
max_heap_objects: 100_000,
max_heap_bytes: 256 * 1024 * 1024, max_unmanaged_bytes: 64 * 1024 * 1024, timeout_ms: 60_000, }
}
}
impl Default for MemoryConfig {
fn default() -> Self {
Self {
initial_heap_size: 16 * 1024 * 1024, max_heap_size: 256 * 1024 * 1024, stack_size: 1024 * 1024, address_space_bits: 32,
track_access: false,
zero_init: true,
}
}
}
impl Default for StubConfig {
fn default() -> Self {
Self {
bcl_stubs: true,
pinvoke_stubs: true,
reflection_stubs: true,
crypto_stubs: true,
io_stubs: true,
threading_stubs: true,
strict_mode: false,
}
}
}
impl TracingConfig {
#[must_use]
pub fn full_trace<P: Into<PathBuf>>(path: P) -> Self {
Self {
trace_instructions: true,
trace_calls: true,
trace_heap: true,
trace_exceptions: true,
trace_stubs: true,
trace_array_ops: true,
max_trace_entries: 0, output_path: Some(path.into()),
context_prefix: None,
}
}
#[must_use]
pub fn call_trace<P: Into<PathBuf>>(path: P) -> Self {
Self {
trace_instructions: false,
trace_calls: true,
trace_heap: false,
trace_exceptions: true,
trace_stubs: true,
trace_array_ops: false,
max_trace_entries: 0,
output_path: Some(path.into()),
context_prefix: None,
}
}
#[must_use]
pub fn with_context(mut self, prefix: impl Into<String>) -> Self {
self.context_prefix = Some(prefix.into());
self
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.trace_instructions
|| self.trace_calls
|| self.trace_heap
|| self.trace_exceptions
|| self.trace_stubs
|| self.trace_array_ops
}
#[must_use]
pub fn has_output_file(&self) -> bool {
self.output_path.is_some()
}
}
impl Default for TracingConfig {
fn default() -> Self {
Self {
trace_instructions: false,
trace_calls: false,
trace_heap: false,
trace_exceptions: true,
trace_stubs: false,
trace_array_ops: false,
max_trace_entries: 10_000,
output_path: None,
context_prefix: None,
}
}
}
impl EmulationConfig {
#[must_use]
pub fn extraction() -> Self {
Self {
limits: EmulationLimits {
max_instructions: 50_000_000,
..Default::default()
},
unknown_method: UnknownMethodBehavior::Default,
symbolic_tracking: false,
threading_enabled: true,
exception_handling: true,
stubs: StubConfig {
strict_mode: false,
..Default::default()
},
..Default::default()
}
}
#[must_use]
pub fn analysis() -> Self {
Self {
limits: EmulationLimits {
max_instructions: 1_000_000,
..Default::default()
},
unknown_method: UnknownMethodBehavior::Symbolic,
symbolic_tracking: true,
threading_enabled: false,
exception_handling: false,
stubs: StubConfig {
strict_mode: false,
..Default::default()
},
..Default::default()
}
}
#[must_use]
pub fn full() -> Self {
Self {
limits: EmulationLimits {
max_instructions: 100_000_000,
timeout_ms: 300_000, ..Default::default()
},
unknown_method: UnknownMethodBehavior::Fail,
symbolic_tracking: false,
threading_enabled: true,
exception_handling: true,
stubs: StubConfig {
strict_mode: true,
..Default::default()
},
tracing: TracingConfig {
trace_exceptions: true,
..Default::default()
},
..Default::default()
}
}
#[must_use]
pub fn minimal() -> Self {
Self {
limits: EmulationLimits {
max_instructions: 10_000,
max_call_depth: 10,
..Default::default()
},
unknown_method: UnknownMethodBehavior::Skip,
symbolic_tracking: false,
threading_enabled: false,
exception_handling: false,
stubs: StubConfig {
bcl_stubs: true,
pinvoke_stubs: false,
reflection_stubs: false,
crypto_stubs: false,
io_stubs: false,
threading_stubs: false,
strict_mode: false,
},
..Default::default()
}
}
}
impl EmulationLimits {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_max_instructions(mut self, max: u64) -> Self {
self.max_instructions = max;
self
}
#[must_use]
pub fn with_max_call_depth(mut self, max: usize) -> Self {
self.max_call_depth = max;
self
}
#[must_use]
pub fn with_max_heap_objects(mut self, max: usize) -> Self {
self.max_heap_objects = max;
self
}
#[must_use]
pub fn with_max_heap_bytes(mut self, max: usize) -> Self {
self.max_heap_bytes = max;
self
}
#[must_use]
pub fn with_timeout_ms(mut self, ms: u64) -> Self {
self.timeout_ms = ms;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = EmulationConfig::default();
assert_eq!(config.limits.max_instructions, 10_000_000);
assert!(config.threading_enabled);
assert!(config.exception_handling);
}
#[test]
fn test_extraction_preset() {
let config = EmulationConfig::extraction();
assert_eq!(config.limits.max_instructions, 50_000_000);
assert_eq!(config.unknown_method, UnknownMethodBehavior::Default);
}
#[test]
fn test_analysis_preset() {
let config = EmulationConfig::analysis();
assert!(config.symbolic_tracking);
assert!(!config.threading_enabled);
}
#[test]
fn test_full_preset() {
let config = EmulationConfig::full();
assert_eq!(config.unknown_method, UnknownMethodBehavior::Fail);
assert!(config.stubs.strict_mode);
}
#[test]
fn test_minimal_preset() {
let config = EmulationConfig::minimal();
assert_eq!(config.limits.max_instructions, 10_000);
assert!(!config.stubs.pinvoke_stubs);
}
#[test]
fn test_limits_builder() {
let limits = EmulationLimits::new()
.with_max_instructions(5000)
.with_max_call_depth(50)
.with_timeout_ms(30_000);
assert_eq!(limits.max_instructions, 5000);
assert_eq!(limits.max_call_depth, 50);
assert_eq!(limits.timeout_ms, 30_000);
}
}