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

mod.rs (16659B)

      1 use clap::{Parser, ValueEnum};
      2 use rand::Rng;
      3 
      4 #[allow(clippy::upper_case_acronyms)]
      5 #[derive(Debug, Clone, ValueEnum)]
      6 pub enum EncryptionKind {
      7     AES,
      8     #[value(name = "aes256")]
      9     AES256,
     10     #[value(name = "chacha20")]
     11     Chacha20,
     12     XOR,
     13     #[value(name = "blowfish")]
     14     Blowfish,
     15     #[value(name = "twofish")]
     16     Twofish,
     17     #[value(name = "camellia")]
     18     Camellia,
     19 }
     20 
     21 #[allow(clippy::upper_case_acronyms)]
     22 #[derive(Debug, Clone, ValueEnum)]
     23 pub enum Technique {
     24     #[value(name = "crt")]
     25     CRT,
     26     #[value(name = "crtx")]
     27     CRTx,
     28     #[value(name = "create-fiber")]
     29     CreateFiber,
     30     #[value(name = "process-hollowing")]
     31     ProcessHollowing,
     32     #[value(name = "create-thread")]
     33     CreateThread,
     34     #[value(name = "nt-create-thread-ex")]
     35     NtCreateThreadEx,
     36     #[value(name = "syscall")]
     37     Syscall,
     38     #[value(name = "etwp")]
     39     Etwp,
     40     #[value(name = "set-timer")]
     41     SetTimer,
     42     #[value(name = "enum-calendar")]
     43     EnumCalendar,
     44     #[value(name = "enum-windows")]
     45     EnumWindows,
     46     #[value(name = "enum-system-locales")]
     47     EnumSystemLocales,
     48     #[value(name = "image-get-digest-stream")]
     49     ImageGetDigestStream,
     50     #[value(name = "queue-user-apc")]
     51     QueueUserAPC,
     52     #[value(name = "fairy-law")]
     53     FairyLaw,
     54     #[value(name = "module-stomping")]
     55     ModuleStomping,
     56     #[value(name = "thread-hijacking")]
     57     ThreadHijacking,
     58     #[value(name = "early-bird")]
     59     EarlyBird,
     60 }
     61 
     62 #[derive(Debug, Clone, ValueEnum)]
     63 pub enum BinaryFormat {
     64     #[value(name = "exe")]
     65     Exe,
     66     #[value(name = "dll")]
     67     Dll,
     68     #[value(name = "lnk")]
     69     Lnk,
     70 }
     71 
     72 #[derive(Debug, Parser)]
     73 #[command(name = "myph")]
     74 #[command(author, version, about, long_about = None)]
     75 pub struct Options {
     76     /// Binary format (exe,dll)
     77     #[arg(short = 'b', long = "binary-format", default_value = "exe")]
     78     pub binary_format: BinaryFormat,
     79 
     80     /// Builds binary with debug symbols
     81     #[arg(short = 'd', long = "debug")]
     82     pub debug: bool,
     83 
     84     /// Encryption method (aes, aes256, chacha20, xor, blowfish, twofish, camellia)
     85     #[arg(short = 'e', long = "encryption", default_value = "aes")]
     86     pub encryption: EncryptionKind,
     87 
     88     /// Encryption key, auto-generated if empty (if used by --encryption)
     89     #[arg(short = 'k', long = "key")]
     90     pub key: Option<String>,
     91 
     92     /// Output name
     93     #[arg(short = 'f', long = "out", default_value = "out.exe")]
     94     pub out: String,
     95 
     96     /// Target process to inject shellcode to
     97     #[arg(short = 'p', long = "process", default_value = "cmd.exe")]
     98     pub process: String,
     99 
    100     /// Shellcode path
    101     #[arg(short = 's', long = "shellcode", default_value = "msf.raw")]
    102     pub shellcode: String,
    103 
    104     /// Shellcode-loading technique
    105     #[arg(short = 't', long = "technique", default_value = "crt")]
    106     pub technique: Technique,
    107 
    108     /// Enable API Hammering (adds random CPU/IO operations between API calls)
    109     #[arg(long = "with-api-hammering")]
    110     pub with_api_hammering: bool,
    111 
    112     /// Enable AMSI patching to bypass AMSI before executing shellcode
    113     #[arg(long = "with-amsi-patch")]
    114     pub with_amsi_patch: bool,
    115 
    116     /// Enable string encryption at compile-time with runtime decryption
    117     #[arg(long = "with-string-encryption")]
    118     pub with_string_encryption: bool,
    119 
    120     /// Enable dynamic function resolver (PEB walk) with encrypted function names
    121     #[arg(long = "with-dynamic-function-resolver")]
    122     pub with_dynamic_function_resolver: bool,
    123 
    124     /// Enable anti-VM detection checks before executing shellcode
    125     #[arg(long = "with-anti-vm")]
    126     pub with_anti_vm: bool,
    127 
    128     /// Enable anti-sandbox detection checks before executing shellcode
    129     #[arg(long = "with-anti-sandbox")]
    130     pub with_anti_sandbox: bool,
    131 
    132     /// Enable anti-debugger detection checks before executing shellcode
    133     #[arg(long = "with-anti-debugger")]
    134     pub with_anti_debugger: bool,
    135 
    136     /// Enable call stack spoofing using UWD library for evasion
    137     #[arg(long = "with-callstack-spoofing")]
    138     pub with_callstack_spoofing: bool,
    139 
    140     /// Check compatibility of technique with selected features and display information
    141     #[arg(long = "check-compatibility")]
    142     pub check_compatibility: bool,
    143 
    144     /// Use low-resource build profile (reduced memory/CPU usage, larger binary)
    145     #[arg(long = "low-resources")]
    146     pub low_resources: bool,
    147 }
    148 
    149 impl Options {
    150     /// Parse command-line arguments and auto-generate key if not provided
    151     pub fn parse_and_generate() -> Self {
    152         let mut opts = Self::parse();
    153 
    154         // Auto-generate key if not provided
    155         if opts.key.is_none() {
    156             opts.key = Some(generate_random_key());
    157         }
    158 
    159         // Automatically enable string encryption when dynamic function resolver is enabled
    160         if opts.with_dynamic_function_resolver {
    161             opts.with_string_encryption = true;
    162         }
    163 
    164         opts
    165     }
    166 }
    167 
    168 /// Generate a random alphanumeric key of length 12-32
    169 fn generate_random_key() -> String {
    170     const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
    171                             abcdefghijklmnopqrstuvwxyz\
    172                             0123456789";
    173     let mut rng = rand::thread_rng();
    174 
    175     // Random length between 12 and 32
    176     let len = rng.gen_range(12..=32);
    177 
    178     (0..len)
    179         .map(|_| {
    180             let idx = rng.gen_range(0..CHARSET.len());
    181             CHARSET[idx] as char
    182         })
    183         .collect()
    184 }
    185 
    186 #[cfg(test)]
    187 mod tests {
    188     use super::*;
    189 
    190     #[test]
    191     fn test_basic() {
    192         assert_eq!(2 + 2, 4);
    193     }
    194 
    195     #[test]
    196     fn test_default_options() {
    197         let args = vec!["myph"];
    198         let opts = Options::parse_from(args);
    199 
    200         assert_eq!(opts.out, "out.exe");
    201         assert_eq!(opts.process, "cmd.exe");
    202         assert_eq!(opts.shellcode, "msf.raw");
    203         assert!(!opts.debug);
    204         assert!(!opts.with_api_hammering);
    205         assert!(!opts.with_amsi_patch);
    206         assert!(!opts.with_string_encryption);
    207         assert!(opts.key.is_none());
    208     }
    209 
    210     #[test]
    211     fn test_custom_options() {
    212         let args = vec![
    213             "myph",
    214             "-b",
    215             "dll",
    216             "-d",
    217             "-e",
    218             "xor",
    219             "-k",
    220             "test-key",
    221             "-f",
    222             "payload.dll",
    223             "-p",
    224             "explorer.exe",
    225             "-s",
    226             "shellcode.bin",
    227             "-t",
    228             "create-thread",
    229         ];
    230         let opts = Options::parse_from(args);
    231 
    232         assert_eq!(opts.out, "payload.dll");
    233         assert_eq!(opts.process, "explorer.exe");
    234         assert_eq!(opts.shellcode, "shellcode.bin");
    235         assert!(opts.debug);
    236         assert_eq!(opts.key.as_ref().unwrap(), "test-key");
    237     }
    238 
    239     #[test]
    240     fn test_encryption_options() {
    241         let args = vec!["myph", "-e", "chacha20"];
    242         let opts = Options::parse_from(args);
    243         assert!(matches!(opts.encryption, EncryptionKind::Chacha20));
    244 
    245         let args = vec!["myph", "-e", "blowfish"];
    246         let opts = Options::parse_from(args);
    247         assert!(matches!(opts.encryption, EncryptionKind::Blowfish));
    248 
    249         let args = vec!["myph", "-e", "aes"];
    250         let opts = Options::parse_from(args);
    251         assert!(matches!(opts.encryption, EncryptionKind::AES));
    252 
    253         let args = vec!["myph", "-e", "aes256"];
    254         let opts = Options::parse_from(args);
    255         assert!(matches!(opts.encryption, EncryptionKind::AES256));
    256 
    257         let args = vec!["myph", "-e", "xor"];
    258         let opts = Options::parse_from(args);
    259         assert!(matches!(opts.encryption, EncryptionKind::XOR));
    260 
    261         let args = vec!["myph", "-e", "twofish"];
    262         let opts = Options::parse_from(args);
    263         assert!(matches!(opts.encryption, EncryptionKind::Twofish));
    264 
    265         let args = vec!["myph", "-e", "camellia"];
    266         let opts = Options::parse_from(args);
    267         assert!(matches!(opts.encryption, EncryptionKind::Camellia));
    268     }
    269 
    270     #[test]
    271     fn test_technique_options() {
    272         let args = vec!["myph", "-t", "crt"];
    273         let opts = Options::parse_from(args);
    274         assert!(matches!(opts.technique, Technique::CRT));
    275 
    276         let args = vec!["myph", "-t", "crtx"];
    277         let opts = Options::parse_from(args);
    278         assert!(matches!(opts.technique, Technique::CRTx));
    279 
    280         let args = vec!["myph", "-t", "create-fiber"];
    281         let opts = Options::parse_from(args);
    282         assert!(matches!(opts.technique, Technique::CreateFiber));
    283 
    284         let args = vec!["myph", "-t", "process-hollowing"];
    285         let opts = Options::parse_from(args);
    286         assert!(matches!(opts.technique, Technique::ProcessHollowing));
    287 
    288         let args = vec!["myph", "-t", "create-thread"];
    289         let opts = Options::parse_from(args);
    290         assert!(matches!(opts.technique, Technique::CreateThread));
    291 
    292         let args = vec!["myph", "-t", "nt-create-thread-ex"];
    293         let opts = Options::parse_from(args);
    294         assert!(matches!(opts.technique, Technique::NtCreateThreadEx));
    295 
    296         let args = vec!["myph", "-t", "syscall"];
    297         let opts = Options::parse_from(args);
    298         assert!(matches!(opts.technique, Technique::Syscall));
    299 
    300         let args = vec!["myph", "-t", "etwp"];
    301         let opts = Options::parse_from(args);
    302         assert!(matches!(opts.technique, Technique::Etwp));
    303 
    304         let args = vec!["myph", "-t", "set-timer"];
    305         let opts = Options::parse_from(args);
    306         assert!(matches!(opts.technique, Technique::SetTimer));
    307 
    308         let args = vec!["myph", "-t", "enum-windows"];
    309         let opts = Options::parse_from(args);
    310         assert!(matches!(opts.technique, Technique::EnumWindows));
    311 
    312         let args = vec!["myph", "-t", "enum-system-locales"];
    313         let opts = Options::parse_from(args);
    314         assert!(matches!(opts.technique, Technique::EnumSystemLocales));
    315 
    316         let args = vec!["myph", "-t", "image-get-digest-stream"];
    317         let opts = Options::parse_from(args);
    318         assert!(matches!(opts.technique, Technique::ImageGetDigestStream));
    319 
    320         let args = vec!["myph", "-t", "queue-user-apc"];
    321         let opts = Options::parse_from(args);
    322         assert!(matches!(opts.technique, Technique::QueueUserAPC));
    323 
    324         let args = vec!["myph", "-t", "fairy-law"];
    325         let opts = Options::parse_from(args);
    326         assert!(matches!(opts.technique, Technique::FairyLaw));
    327         let args = vec!["myph", "-t", "module-stomping"];
    328         let opts = Options::parse_from(args);
    329         assert!(matches!(opts.technique, Technique::ModuleStomping));
    330 
    331         let args = vec!["myph", "-t", "thread-hijacking"];
    332         let opts = Options::parse_from(args);
    333         assert!(matches!(opts.technique, Technique::ThreadHijacking));
    334 
    335         let args = vec!["myph", "-t", "early-bird"];
    336         let opts = Options::parse_from(args);
    337         assert!(matches!(opts.technique, Technique::EarlyBird));
    338     }
    339 
    340     #[test]
    341     fn test_binary_format_options() {
    342         let args = vec!["myph", "-b", "exe"];
    343         let opts = Options::parse_from(args);
    344         assert!(matches!(opts.binary_format, BinaryFormat::Exe));
    345 
    346         let args = vec!["myph", "-b", "dll"];
    347         let opts = Options::parse_from(args);
    348         assert!(matches!(opts.binary_format, BinaryFormat::Dll));
    349 
    350         let args = vec!["myph", "-b", "lnk"];
    351         let opts = Options::parse_from(args);
    352         assert!(matches!(opts.binary_format, BinaryFormat::Lnk));
    353     }
    354 
    355     #[test]
    356     fn test_key_auto_generation() {
    357         let opts = Options::parse_and_generate();
    358 
    359         // Key should be auto-generated
    360         assert!(opts.key.is_some());
    361         let key = opts.key.unwrap();
    362 
    363         // Key length should be between 12 and 32
    364         assert!(key.len() >= 12 && key.len() <= 32);
    365 
    366         // Key should only contain alphanumeric characters
    367         assert!(key.chars().all(|c| c.is_alphanumeric()));
    368     }
    369 
    370     #[test]
    371     fn test_key_provided() {
    372         let args = vec!["myph", "-k", "custom-key"];
    373         let mut opts = Options::parse_from(args);
    374 
    375         // Manually simulate parse_and_generate behavior
    376         if opts.key.is_none() {
    377             opts.key = Some(generate_random_key());
    378         }
    379 
    380         // Key should be the provided one
    381         assert_eq!(opts.key.as_ref().unwrap(), "custom-key");
    382     }
    383 
    384     #[test]
    385     fn test_with_api_hammering_flag() {
    386         let args = vec!["myph", "--with-api-hammering"];
    387         let opts = Options::parse_from(args);
    388         assert!(opts.with_api_hammering);
    389     }
    390 
    391     #[test]
    392     fn test_without_api_hammering_flag() {
    393         let args = vec!["myph"];
    394         let opts = Options::parse_from(args);
    395         assert!(!opts.with_api_hammering);
    396     }
    397 
    398     #[test]
    399     fn test_with_amsi_patch_flag() {
    400         let args = vec!["myph", "--with-amsi-patch"];
    401         let opts = Options::parse_from(args);
    402         assert!(opts.with_amsi_patch);
    403     }
    404 
    405     #[test]
    406     fn test_without_amsi_patch_flag() {
    407         let args = vec!["myph"];
    408         let opts = Options::parse_from(args);
    409         assert!(!opts.with_amsi_patch);
    410     }
    411 
    412     #[test]
    413     fn test_with_string_encryption_flag() {
    414         let args = vec!["myph", "--with-string-encryption"];
    415         let opts = Options::parse_from(args);
    416         assert!(opts.with_string_encryption);
    417     }
    418 
    419     #[test]
    420     fn test_without_string_encryption_flag() {
    421         let args = vec!["myph"];
    422         let opts = Options::parse_from(args);
    423         assert!(!opts.with_string_encryption);
    424     }
    425 
    426     #[test]
    427     fn test_with_dynamic_function_resolver_flag() {
    428         let args = vec!["myph", "--with-dynamic-function-resolver"];
    429         let opts = Options::parse_from(args);
    430         assert!(opts.with_dynamic_function_resolver);
    431     }
    432 
    433     #[test]
    434     fn test_without_dynamic_function_resolver_flag() {
    435         let args = vec!["myph"];
    436         let opts = Options::parse_from(args);
    437         assert!(!opts.with_dynamic_function_resolver);
    438     }
    439 
    440     #[test]
    441     fn test_dynamic_function_resolver_enables_string_encryption() {
    442         let args = vec!["myph", "--with-dynamic-function-resolver"];
    443         let mut opts = Options::parse_from(args);
    444 
    445         // Simulate parse_and_generate behavior
    446         if opts.key.is_none() {
    447             opts.key = Some(generate_random_key());
    448         }
    449         if opts.with_dynamic_function_resolver {
    450             opts.with_string_encryption = true;
    451         }
    452 
    453         assert!(opts.with_dynamic_function_resolver);
    454         assert!(opts.with_string_encryption);
    455     }
    456 
    457     #[test]
    458     fn test_with_anti_vm_flag() {
    459         let args = vec!["myph", "--with-anti-vm"];
    460         let opts = Options::parse_from(args);
    461         assert!(opts.with_anti_vm);
    462     }
    463 
    464     #[test]
    465     fn test_without_anti_vm_flag() {
    466         let args = vec!["myph"];
    467         let opts = Options::parse_from(args);
    468         assert!(!opts.with_anti_vm);
    469     }
    470 
    471     #[test]
    472     fn test_with_anti_sandbox_flag() {
    473         let args = vec!["myph", "--with-anti-sandbox"];
    474         let opts = Options::parse_from(args);
    475         assert!(opts.with_anti_sandbox);
    476     }
    477 
    478     #[test]
    479     fn test_without_anti_sandbox_flag() {
    480         let args = vec!["myph"];
    481         let opts = Options::parse_from(args);
    482         assert!(!opts.with_anti_sandbox);
    483     }
    484 
    485     #[test]
    486     fn test_with_anti_debugger_flag() {
    487         let args = vec!["myph", "--with-anti-debugger"];
    488         let opts = Options::parse_from(args);
    489         assert!(opts.with_anti_debugger);
    490     }
    491 
    492     #[test]
    493     fn test_without_anti_debugger_flag() {
    494         let args = vec!["myph"];
    495         let opts = Options::parse_from(args);
    496         assert!(!opts.with_anti_debugger);
    497     }
    498 
    499     #[test]
    500     fn test_all_evasion_flags_combined() {
    501         let args = vec![
    502             "myph",
    503             "--with-anti-vm",
    504             "--with-anti-sandbox",
    505             "--with-anti-debugger",
    506         ];
    507         let opts = Options::parse_from(args);
    508         assert!(opts.with_anti_vm);
    509         assert!(opts.with_anti_sandbox);
    510         assert!(opts.with_anti_debugger);
    511     }
    512 
    513     #[test]
    514     fn test_with_callstack_spoofing_flag() {
    515         let args = vec!["myph", "--with-callstack-spoofing"];
    516         let opts = Options::parse_from(args);
    517         assert!(opts.with_callstack_spoofing);
    518     }
    519 
    520     #[test]
    521     fn test_without_callstack_spoofing_flag() {
    522         let args = vec!["myph"];
    523         let opts = Options::parse_from(args);
    524         assert!(!opts.with_callstack_spoofing);
    525     }
    526 
    527     #[test]
    528     fn test_with_low_resources_flag() {
    529         let args = vec!["myph", "--low-resources"];
    530         let opts = Options::parse_from(args);
    531         assert!(opts.low_resources);
    532     }
    533 
    534     #[test]
    535     fn test_without_low_resources_flag() {
    536         let args = vec!["myph"];
    537         let opts = Options::parse_from(args);
    538         assert!(!opts.low_resources);
    539     }
    540 }