myph-v2.git


myph-v2

~ rust shellcode loader with evasive capabilities
git clone https://github.com/djnnvx/myph-v2.git
log | files | refs | archive | README

validation.rs (6146B)

      1 use super::capabilities::{get_technique_metadata, Capability};
      2 
      3 #[derive(Debug, Clone)]
      4 pub struct ValidationError {
      5     pub message: String,
      6     pub suggestion: Option<String>,
      7 }
      8 
      9 impl ValidationError {
     10     pub fn new(message: String, suggestion: Option<String>) -> Self {
     11         Self {
     12             message,
     13             suggestion,
     14         }
     15     }
     16 }
     17 
     18 pub struct TechniqueValidator;
     19 
     20 impl TechniqueValidator {
     21     pub fn validate(
     22         technique_name: &str,
     23         with_dynamic_resolver: bool,
     24         requires_process_param: bool,
     25     ) -> Result<(), Vec<ValidationError>> {
     26         let mut errors = Vec::new();
     27 
     28         let meta = match get_technique_metadata(technique_name) {
     29             Some(m) => m,
     30             None => {
     31                 errors.push(ValidationError::new(
     32                     format!("Unknown technique: '{}'", technique_name),
     33                     Some("Use --help to see available techniques".to_string()),
     34                 ));
     35                 return Err(errors);
     36             }
     37         };
     38 
     39         if with_dynamic_resolver && !meta.supports_dynamic_resolver() {
     40             errors.push(ValidationError::new(
     41                 format!(
     42                     "Technique '{}' does not support dynamic function resolver",
     43                     technique_name
     44                 ),
     45                 Some(
     46                     "Remove --with-dynamic-function-resolver flag or choose a different technique"
     47                         .to_string(),
     48                 ),
     49             ));
     50         }
     51 
     52         if requires_process_param && !meta.requires_process() {
     53             errors.push(ValidationError::new(
     54                 format!(
     55                     "Technique '{}' is for local execution only and does not use the --process parameter",
     56                     technique_name
     57                 ),
     58                 Some("Remove --process flag or use a process injection technique (crt, crtx, process-hollowing, nt-create-thread-ex, thread-hijacking, early-bird)".to_string()),
     59             ));
     60         }
     61 
     62         if errors.is_empty() {
     63             Ok(())
     64         } else {
     65             Err(errors)
     66         }
     67     }
     68 
     69     pub fn display_technique_info(technique_name: &str) -> Option<String> {
     70         let meta = get_technique_metadata(technique_name)?;
     71         let mut output = String::new();
     72 
     73         output.push_str(&format!("Technique: {}\n", meta.name));
     74         output.push_str(&format!("Description: {}\n", meta.description));
     75         output.push_str(&format!(
     76             "Detection Difficulty: {}\n",
     77             meta.detection_difficulty.as_str()
     78         ));
     79         output.push_str("\nCapabilities:\n");
     80 
     81         for cap in &meta.capabilities {
     82             output.push_str(&format!("  - {}\n", cap.description()));
     83         }
     84 
     85         if meta.has_capability(Capability::RequiresAdmin) {
     86             output.push_str("\n[!] WARNING: This technique requires administrator privileges\n");
     87         }
     88 
     89         Some(output)
     90     }
     91 
     92     pub fn suggest_compatible_techniques(with_dynamic_resolver: bool) -> Vec<String> {
     93         let all_techniques = super::capabilities::list_all_techniques();
     94         all_techniques
     95             .into_iter()
     96             .filter(|meta| {
     97                 if with_dynamic_resolver {
     98                     meta.supports_dynamic_resolver()
     99                 } else {
    100                     true
    101                 }
    102             })
    103             .map(|meta| meta.name.to_string())
    104             .collect()
    105     }
    106 }
    107 
    108 #[cfg(test)]
    109 mod tests {
    110     use super::*;
    111 
    112     #[test]
    113     fn test_validate_crt_with_process() {
    114         let result = TechniqueValidator::validate("crt", false, true);
    115         assert!(result.is_ok());
    116     }
    117 
    118     #[test]
    119     fn test_validate_crt_without_process() {
    120         // CRT without process parameter should now pass since process param is auto-generated
    121         let result = TechniqueValidator::validate("crt", false, false);
    122         assert!(result.is_ok());
    123     }
    124 
    125     #[test]
    126     fn test_validate_create_fiber_without_process() {
    127         let result = TechniqueValidator::validate("create-fiber", false, false);
    128         assert!(result.is_ok());
    129     }
    130 
    131     #[test]
    132     fn test_validate_create_fiber_with_process() {
    133         let result = TechniqueValidator::validate("create-fiber", false, true);
    134         assert!(result.is_err());
    135         let errors = result.unwrap_err();
    136         assert_eq!(errors.len(), 1);
    137         assert!(errors[0].message.contains("local execution only"));
    138     }
    139 
    140     #[test]
    141     fn test_validate_unknown_technique() {
    142         let result = TechniqueValidator::validate("unknown-tech", false, false);
    143         assert!(result.is_err());
    144         let errors = result.unwrap_err();
    145         assert_eq!(errors.len(), 1);
    146         assert!(errors[0].message.contains("Unknown technique"));
    147     }
    148 
    149     #[test]
    150     fn test_validate_with_dynamic_resolver() {
    151         let result = TechniqueValidator::validate("crt", true, true);
    152         assert!(result.is_ok());
    153     }
    154 
    155     #[test]
    156     fn test_display_technique_info() {
    157         let info = TechniqueValidator::display_technique_info("crt");
    158         assert!(info.is_some());
    159         let info_str = info.unwrap();
    160         assert!(info_str.contains("crt"));
    161         assert!(info_str.contains("CreateRemoteThread"));
    162     }
    163 
    164     #[test]
    165     fn test_display_technique_info_fairy_law() {
    166         let info = TechniqueValidator::display_technique_info("fairy-law");
    167         assert!(info.is_some());
    168         let info_str = info.unwrap();
    169         assert!(info_str.contains("fairy-law"));
    170         assert!(info_str.contains("WARNING"));
    171         assert!(info_str.contains("administrator"));
    172     }
    173 
    174     #[test]
    175     fn test_display_technique_info_unknown() {
    176         let info = TechniqueValidator::display_technique_info("unknown");
    177         assert!(info.is_none());
    178     }
    179 
    180     #[test]
    181     fn test_suggest_compatible_techniques_all() {
    182         let techniques = TechniqueValidator::suggest_compatible_techniques(false);
    183         assert_eq!(techniques.len(), 17);
    184     }
    185 
    186     #[test]
    187     fn test_suggest_compatible_techniques_with_dynamic_resolver() {
    188         let techniques = TechniqueValidator::suggest_compatible_techniques(true);
    189         assert_eq!(techniques.len(), 17);
    190     }
    191 }