use std::time::Duration;
use crate::emulation::TracingConfig;
#[derive(Debug, Clone)]
pub struct EngineConfig {
pub max_iterations: usize,
pub stable_iterations: usize,
pub max_phase_iterations: usize,
pub detection_threshold: u32,
pub target_obfuscator: Option<String>,
pub emulation_max_instructions: u64,
pub emulation_timeout: Duration,
pub enable_inlining: bool,
pub inline_threshold: usize,
pub enable_string_decryption: bool,
pub enable_constant_propagation: bool,
pub enable_dead_code_elimination: bool,
pub enable_opaque_predicate_removal: bool,
pub enable_copy_propagation: bool,
pub enable_strength_reduction: bool,
pub enable_control_flow_simplification: bool,
pub enable_interprocedural: bool,
pub string_decryptor_max_instructions: usize,
pub string_decryptor_max_params: usize,
pub string_decryptor_min_score: u32,
pub unflattening_min_switch_cases: usize,
pub unflattening_max_states_per_case: usize,
pub unflattening_max_trace_iterations: usize,
pub unflattening_large_constant_threshold: i64,
pub verify_semantics: bool,
pub resolution_strategies: Vec<ResolutionStrategy>,
pub cleanup: CleanupConfig,
pub tracing: Option<TracingConfig>,
}
#[derive(Debug, Clone)]
pub struct CleanupConfig {
pub remove_decryptors: bool,
pub remove_protection_methods: bool,
pub remove_empty_types: bool,
pub remove_orphan_metadata: bool,
pub remove_artifact_sections: bool,
pub rename_obfuscated_names: bool,
pub remove_unused_methods: bool,
}
impl Default for CleanupConfig {
fn default() -> Self {
Self {
remove_decryptors: true,
remove_protection_methods: true,
remove_empty_types: true,
remove_orphan_metadata: true,
remove_artifact_sections: true,
rename_obfuscated_names: true,
remove_unused_methods: false, }
}
}
impl CleanupConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn disabled() -> Self {
Self {
remove_decryptors: false,
remove_protection_methods: false,
remove_empty_types: false,
remove_orphan_metadata: false,
remove_artifact_sections: false,
rename_obfuscated_names: false,
remove_unused_methods: false,
}
}
#[must_use]
pub fn any_enabled(&self) -> bool {
self.remove_decryptors
|| self.remove_protection_methods
|| self.remove_empty_types
|| self.remove_orphan_metadata
|| self.remove_artifact_sections
|| self.rename_obfuscated_names
|| self.remove_unused_methods
}
#[must_use]
pub fn aggressive() -> Self {
Self {
remove_unused_methods: true,
..Self::default()
}
}
}
impl Default for EngineConfig {
fn default() -> Self {
Self {
max_iterations: 20,
stable_iterations: 2,
max_phase_iterations: 10,
detection_threshold: 50,
target_obfuscator: None,
emulation_max_instructions: 1_000_000,
emulation_timeout: Duration::from_secs(5),
enable_inlining: false,
inline_threshold: 20,
enable_string_decryption: true,
enable_constant_propagation: true,
enable_dead_code_elimination: true,
enable_opaque_predicate_removal: true,
enable_copy_propagation: true,
enable_strength_reduction: true,
enable_control_flow_simplification: true,
enable_interprocedural: true,
string_decryptor_max_instructions: 200,
string_decryptor_max_params: 3,
string_decryptor_min_score: 45, unflattening_min_switch_cases: 4,
unflattening_max_states_per_case: 15,
unflattening_max_trace_iterations: 500,
unflattening_large_constant_threshold: 100_000,
verify_semantics: false,
resolution_strategies: vec![
ResolutionStrategy::Static,
ResolutionStrategy::Pattern,
ResolutionStrategy::Emulation,
],
cleanup: CleanupConfig::default(),
tracing: None,
}
}
}
impl EngineConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn fast() -> Self {
Self {
max_iterations: 5,
stable_iterations: 1,
emulation_max_instructions: 100_000,
emulation_timeout: Duration::from_millis(500),
enable_inlining: false,
enable_interprocedural: false,
resolution_strategies: vec![ResolutionStrategy::Static, ResolutionStrategy::Pattern],
..Self::default()
}
}
#[must_use]
pub fn aggressive() -> Self {
Self {
max_iterations: 50,
stable_iterations: 3,
emulation_max_instructions: 10_000_000,
emulation_timeout: Duration::from_secs(30),
enable_inlining: true,
inline_threshold: 50,
enable_interprocedural: true,
resolution_strategies: vec![
ResolutionStrategy::Static,
ResolutionStrategy::Pattern,
ResolutionStrategy::Emulation,
ResolutionStrategy::Symbolic,
],
cleanup: CleanupConfig::aggressive(),
..Self::default()
}
}
#[must_use]
pub fn with_max_iterations(mut self, max: usize) -> Self {
self.max_iterations = max;
self
}
#[must_use]
pub fn with_detection_threshold(mut self, threshold: u32) -> Self {
self.detection_threshold = threshold;
self
}
#[must_use]
pub fn with_target_obfuscator(mut self, obfuscator: impl Into<String>) -> Self {
self.target_obfuscator = Some(obfuscator.into());
self
}
#[must_use]
pub fn with_emulation_limits(mut self, max_instructions: u64, timeout: Duration) -> Self {
self.emulation_max_instructions = max_instructions;
self.emulation_timeout = timeout;
self
}
#[must_use]
pub fn with_inlining(mut self, enable: bool, threshold: usize) -> Self {
self.enable_inlining = enable;
self.inline_threshold = threshold;
self
}
#[must_use]
pub fn with_interprocedural(mut self, enable: bool) -> Self {
self.enable_interprocedural = enable;
self
}
#[must_use]
pub fn with_strategies(mut self, strategies: Vec<ResolutionStrategy>) -> Self {
self.resolution_strategies = strategies;
self
}
#[must_use]
#[allow(clippy::fn_params_excessive_bools)]
pub fn with_passes(
mut self,
string_decryption: bool,
constant_propagation: bool,
dead_code: bool,
opaque_predicates: bool,
) -> Self {
self.enable_string_decryption = string_decryption;
self.enable_constant_propagation = constant_propagation;
self.enable_dead_code_elimination = dead_code;
self.enable_opaque_predicate_removal = opaque_predicates;
self
}
#[must_use]
pub fn all_passes_enabled(&self) -> bool {
self.enable_string_decryption
&& self.enable_constant_propagation
&& self.enable_dead_code_elimination
&& self.enable_opaque_predicate_removal
&& self.enable_copy_propagation
&& self.enable_strength_reduction
&& self.enable_control_flow_simplification
}
#[must_use]
pub fn with_tracing(mut self, tracing: TracingConfig) -> Self {
self.tracing = Some(tracing);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ResolutionStrategy {
Static,
Pattern,
Emulation,
Symbolic,
}
impl ResolutionStrategy {
#[must_use]
pub fn name(&self) -> &'static str {
match self {
Self::Static => "Static Analysis",
Self::Pattern => "Pattern Matching",
Self::Emulation => "Emulation",
Self::Symbolic => "Symbolic Execution",
}
}
#[must_use]
pub fn is_safe(&self) -> bool {
matches!(self, Self::Static | Self::Pattern | Self::Symbolic)
}
#[must_use]
pub fn requires_emulation(&self) -> bool {
matches!(self, Self::Emulation)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = EngineConfig::default();
assert_eq!(config.max_iterations, 20);
assert!(config.enable_string_decryption);
assert!(config.enable_interprocedural);
assert!(!config.enable_inlining); assert!(!config.cleanup.remove_unused_methods); }
#[test]
fn test_fast_config() {
let config = EngineConfig::fast();
assert_eq!(config.max_iterations, 5);
assert!(!config.enable_interprocedural);
assert_eq!(config.resolution_strategies.len(), 2);
}
#[test]
fn test_aggressive_config() {
let config = EngineConfig::aggressive();
assert_eq!(config.max_iterations, 50);
assert!(config.enable_inlining);
assert!(config.enable_interprocedural);
assert_eq!(config.resolution_strategies.len(), 4);
assert!(config.cleanup.remove_unused_methods); }
#[test]
fn test_builder_pattern() {
let config = EngineConfig::new()
.with_max_iterations(100)
.with_target_obfuscator("confuserex");
assert_eq!(config.max_iterations, 100);
assert_eq!(config.target_obfuscator, Some("confuserex".to_string()));
}
#[test]
fn test_resolution_strategy() {
assert!(ResolutionStrategy::Static.is_safe());
assert!(ResolutionStrategy::Pattern.is_safe());
assert!(!ResolutionStrategy::Emulation.is_safe());
assert!(ResolutionStrategy::Emulation.requires_emulation());
assert!(!ResolutionStrategy::Static.requires_emulation());
}
}