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

code_validator.rs (7912B)

      1 use std::collections::HashSet;
      2 
      3 #[derive(Debug, Clone, PartialEq, Eq)]
      4 pub enum ValidationError {
      5     UnbalancedBraces { open: usize, close: usize },
      6     UnbalancedParentheses { open: usize, close: usize },
      7     MissingExternBlock,
      8     MissingUnsafeBlock,
      9     InvalidFunctionSignature(String),
     10     MissingRequiredImport(String),
     11     DuplicateExternDeclaration(String),
     12 }
     13 
     14 impl std::fmt::Display for ValidationError {
     15     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
     16         match self {
     17             ValidationError::UnbalancedBraces { open, close } => {
     18                 write!(f, "Unbalanced braces: {} open, {} close", open, close)
     19             }
     20             ValidationError::UnbalancedParentheses { open, close } => {
     21                 write!(f, "Unbalanced parentheses: {} open, {} close", open, close)
     22             }
     23             ValidationError::MissingExternBlock => {
     24                 write!(f, "Missing extern block for Windows API functions")
     25             }
     26             ValidationError::MissingUnsafeBlock => {
     27                 write!(f, "Windows API calls must be in unsafe block")
     28             }
     29             ValidationError::InvalidFunctionSignature(sig) => {
     30                 write!(f, "Invalid function signature: {}", sig)
     31             }
     32             ValidationError::MissingRequiredImport(import) => {
     33                 write!(f, "Missing required import: {}", import)
     34             }
     35             ValidationError::DuplicateExternDeclaration(name) => {
     36                 write!(f, "Duplicate extern declaration: {}", name)
     37             }
     38         }
     39     }
     40 }
     41 
     42 pub struct CodeValidator;
     43 
     44 impl CodeValidator {
     45     pub fn validate_technique_code(code: &str) -> Result<(), Vec<ValidationError>> {
     46         let mut errors = Vec::new();
     47 
     48         if let Err(e) = Self::check_balanced_braces(code) {
     49             errors.push(e);
     50         }
     51 
     52         if let Err(e) = Self::check_balanced_parentheses(code) {
     53             errors.push(e);
     54         }
     55 
     56         if let Some(e) = Self::check_unsafe_usage(code) {
     57             errors.push(e);
     58         }
     59 
     60         if let Some(e) = Self::check_duplicate_externs(code) {
     61             errors.push(e);
     62         }
     63 
     64         if errors.is_empty() {
     65             Ok(())
     66         } else {
     67             Err(errors)
     68         }
     69     }
     70 
     71     fn check_balanced_braces(code: &str) -> Result<(), ValidationError> {
     72         let open = code.matches('{').count();
     73         let close = code.matches('}').count();
     74         if open != close {
     75             Err(ValidationError::UnbalancedBraces { open, close })
     76         } else {
     77             Ok(())
     78         }
     79     }
     80 
     81     fn check_balanced_parentheses(code: &str) -> Result<(), ValidationError> {
     82         let open = code.matches('(').count();
     83         let close = code.matches(')').count();
     84         if open != close {
     85             Err(ValidationError::UnbalancedParentheses { open, close })
     86         } else {
     87             Ok(())
     88         }
     89     }
     90 
     91     fn check_unsafe_usage(code: &str) -> Option<ValidationError> {
     92         let has_windows_api_calls = code.contains("VirtualAlloc")
     93             || code.contains("VirtualProtect")
     94             || code.contains("CreateProcess")
     95             || code.contains("WriteProcessMemory")
     96             || code.contains("QueueUserAPC");
     97 
     98         let has_unsafe_block = code.contains("unsafe");
     99 
    100         if has_windows_api_calls && !has_unsafe_block {
    101             Some(ValidationError::MissingUnsafeBlock)
    102         } else {
    103             None
    104         }
    105     }
    106 
    107     fn check_duplicate_externs(code: &str) -> Option<ValidationError> {
    108         let mut extern_functions = HashSet::new();
    109         let lines: Vec<&str> = code.lines().collect();
    110         let mut in_extern_block = false;
    111 
    112         for line in lines {
    113             let trimmed = line.trim();
    114 
    115             if trimmed.starts_with("extern") && trimmed.contains('{') {
    116                 in_extern_block = true;
    117                 continue;
    118             }
    119 
    120             if in_extern_block && trimmed == "}" {
    121                 in_extern_block = false;
    122                 continue;
    123             }
    124 
    125             if in_extern_block && trimmed.starts_with("fn ") {
    126                 let fn_name = trimmed
    127                     .trim_start_matches("fn ")
    128                     .split('(')
    129                     .next()
    130                     .unwrap_or("");
    131                 if !fn_name.is_empty() && !extern_functions.insert(fn_name.to_string()) {
    132                     return Some(ValidationError::DuplicateExternDeclaration(
    133                         fn_name.to_string(),
    134                     ));
    135                 }
    136             }
    137         }
    138         None
    139     }
    140 
    141     pub fn validate_with_context(
    142         code: &str,
    143         context: &str,
    144     ) -> Result<(), Vec<(String, ValidationError)>> {
    145         Self::validate_technique_code(code).map_err(|errors| {
    146             errors
    147                 .into_iter()
    148                 .map(|e| (context.to_string(), e))
    149                 .collect()
    150         })
    151     }
    152 }
    153 
    154 #[cfg(test)]
    155 mod tests {
    156     use super::*;
    157 
    158     #[test]
    159     fn test_valid_code_passes() {
    160         let code = r#"
    161             unsafe {
    162                 let ptr = VirtualAlloc(ptr::null_mut(), 1024, MEM_COMMIT, PAGE_READWRITE);
    163             }
    164         "#;
    165         assert!(CodeValidator::validate_technique_code(code).is_ok());
    166     }
    167 
    168     #[test]
    169     fn test_unbalanced_braces_detected() {
    170         let code = r#"
    171             unsafe {
    172                 let ptr = VirtualAlloc(ptr::null_mut(), 1024, MEM_COMMIT, PAGE_READWRITE);
    173         "#;
    174         let result = CodeValidator::validate_technique_code(code);
    175         assert!(result.is_err());
    176         let errors = result.unwrap_err();
    177         assert!(matches!(
    178             errors[0],
    179             ValidationError::UnbalancedBraces { .. }
    180         ));
    181     }
    182 
    183     #[test]
    184     fn test_unbalanced_parentheses_detected() {
    185         let code = r#"
    186             unsafe {
    187                 let ptr = VirtualAlloc(ptr::null_mut(), 1024, MEM_COMMIT, PAGE_READWRITE;
    188             }
    189         "#;
    190         let result = CodeValidator::validate_technique_code(code);
    191         assert!(result.is_err());
    192         let errors = result.unwrap_err();
    193         assert!(matches!(
    194             errors[0],
    195             ValidationError::UnbalancedParentheses { .. }
    196         ));
    197     }
    198 
    199     #[test]
    200     fn test_missing_unsafe_block_detected() {
    201         let code = r#"
    202             {
    203                 let ptr = VirtualAlloc(ptr::null_mut(), 1024, MEM_COMMIT, PAGE_READWRITE);
    204             }
    205         "#;
    206         let result = CodeValidator::validate_technique_code(code);
    207         assert!(result.is_err());
    208         let errors = result.unwrap_err();
    209         assert!(matches!(errors[0], ValidationError::MissingUnsafeBlock));
    210     }
    211 
    212     #[test]
    213     fn test_duplicate_extern_detected() {
    214         let code = r#"
    215             extern "system" {
    216                 fn VirtualAlloc(addr: *mut c_void) -> *mut c_void;
    217                 fn VirtualAlloc(addr: *mut c_void) -> *mut c_void;
    218             }
    219         "#;
    220         let result = CodeValidator::validate_technique_code(code);
    221         assert!(result.is_err());
    222         let errors = result.unwrap_err();
    223         eprintln!("Errors found: {:?}", errors);
    224         assert!(errors
    225             .iter()
    226             .any(|e| matches!(e, ValidationError::DuplicateExternDeclaration(_))));
    227     }
    228 
    229     #[test]
    230     fn test_code_without_api_calls_doesnt_need_unsafe() {
    231         let code = r#"
    232             {
    233                 let x = 42;
    234                 let y = x + 1;
    235             }
    236         "#;
    237         assert!(CodeValidator::validate_technique_code(code).is_ok());
    238     }
    239 
    240     #[test]
    241     fn test_validation_with_context() {
    242         let code = r#"
    243             {
    244                 let ptr = VirtualAlloc(ptr::null_mut(), 1024, MEM_COMMIT, PAGE_READWRITE);
    245             }
    246         "#;
    247         let result = CodeValidator::validate_with_context(code, "CreateFiberGenerator");
    248         assert!(result.is_err());
    249         let errors = result.unwrap_err();
    250         assert_eq!(errors[0].0, "CreateFiberGenerator");
    251     }
    252 }